blob: 7c1ff9bd768ceb3a97abcacf2ab9a3bb53ebc797 [file] [log] [blame]
/*************************************************************************
*
* PathFinder: finding a series of labeled nodes within a
* two-layer directed, cyclic graph.
* Copyright (2013) Sandia Corporation
*
* Copyright (2013) Sandia Corporation. Under the terms of Contract
* DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
* retains certain rights in this software.
*
* This file is part of PathFinder.
*
* PathFinder is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* PathFinder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PathFinder. If not, see <http://www.gnu.org/licenses/>.
*
* Questions? Contact J. Brian Rigdon (jbrigdo@sandia.gov)
*
*/
/*
* searchDiagram.c
*
* Created on: Feb 1, 2013
* Author: Brian "Rig" Rigdon, Marcus Smith, Samuel Mulder
*/
#include "searchDiagram.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
/* Scream through the diagram to get the element corresponding to this key. This
* simply iterates through the entire list, there is no optimization in the search
* algorithm.
*/
SearchDiagram *SearchDiagram_findNode ( SearchDiagram *diagram, Node *node )
{
int idx;
/* Do some basic error checking */
if ( !diagram )
return( NULL );
for ( idx = 0; diagram[idx].node != NULL; ++idx )
{
if ( diagram[idx].node == node ) /* We found it! */
return( &diagram[idx] );
}
/* If we made it here, we couldn't find it. */
return( NULL );
}
/* The meat of this process - the function that creates the search diagram.
* It allocates the memory for the array of search diagram elements and
* populates them with node data and edgeID arrays. Outer nodes are inserted
* into the node array from the lowest addresses and inner nodes are inserted
* into the search diagram from the highest. The authors did this to (hopefully)
* give a little more efficiency to the searching of the diagram.
*/
SearchDiagram *SearchDiagram_build( NodeList *nodeTree, int nodeCount )
{
SearchDiagram *diagram = NULL;
NodeList *outerNodes = NULL;
Node *node = NULL;
NodeList *innerNodes = NULL;
/* Originally, we speculated that searches would stream through the outer
* nodes more than through the interior nodes. (We were wrong.) So we had
* this mechanism in place to put the outer nodes at the beginning of the
* diagram and the interior nodes at the end. Because we were wrong, this
* increases the likelihood of cache misses rather than decreasing it. Ergo
* we have replaced the outerIdx and the innerIdx calls.
*
* So... it turns out that putting the inner nodes near their containing
* node actually slows things down - but it's minor (2% - 3 minutes in a
* 120 minute run.)
int index = 0;
*/
int outerIdx = 0; /* index from the bottom up for outer nodes */
int innerIdx = nodeCount-1; /* and from the top down for interior nodes */
int edgeCount;
EdgeList *edges;
SearchDiagram *element; /* for searching in our diagram */
/* Do some basic error checking */
if ( !nodeTree || nodeCount == 0 )
return NULL;
/* Allocate an array of SearchDiagram Elements (+1 for a terminus element) */
diagram = malloc ( (nodeCount+1) * sizeof(SearchDiagram) );
if ( !diagram ) /* malloc failed */
return( NULL );
diagram[nodeCount].node = NULL;
diagram[nodeCount].edgeReferenceArray = NULL;
/* Scream through the Nodes and fill in the Search Array allocate space for edges */
for ( outerNodes = nodeTree; outerNodes != NULL; outerNodes = outerNodes->nextNode )
{
node = outerNodes->node;
/* This diagram entry needs a key */
diagram[outerIdx].node = node;
/* How many edges do we need? */
edgeCount = node->edgeCount + node->entranceCount;
if ( edgeCount > 0 ) /* allocate space for edgeIdArray (+1 for a terminus element) */
{
diagram[outerIdx].edgeReferenceArray = malloc ( (edgeCount+1) * sizeof(EdgeReferences) );
diagram[outerIdx].edgeReferenceArray[edgeCount].edgeTarget = NULL;
diagram[outerIdx].edgeReferenceArray[edgeCount].targetNodeEdges = NULL;
}
else
diagram[outerIdx].edgeReferenceArray = NULL;
/* What do we do if that malloc fails? */
/* increment the outer index: */
++outerIdx;
/* Now do the same for this node's interior graph (if present) */
for ( innerNodes = node->interiorNodes; innerNodes != NULL; innerNodes = innerNodes->nextNode )
{
node = innerNodes->node;
diagram[innerIdx].node = node;
edgeCount = node->edgeCount + node->entranceCount;
if ( node->edgeCount > 0 ) /* allocate space for edgeIdArray (+1 for a terminus element) */
{
diagram[innerIdx].edgeReferenceArray = malloc ( (edgeCount+1) * sizeof(EdgeReferences) );
diagram[innerIdx].edgeReferenceArray[edgeCount].edgeTarget = NULL;
diagram[innerIdx].edgeReferenceArray[edgeCount].targetNodeEdges = NULL;
}
else
diagram[innerIdx].edgeReferenceArray = NULL;
/* Again, what do we do if that malloc fails? */
/* decrement the interior index: */
--innerIdx;
}
}
/* Check to make sure our counters line up. If they don't, we have a corrupt
* graph. That is, there is a mismatch between node definitions and edge lists.
* The innerIdx should reference the previous outerIdx location.
*/
if ( outerIdx != innerIdx +1 )
{
/* Houston, we have a problem */
printf ( "Bummer. outerIdx: %d, innerIdx: %d, node count: %d\n", outerIdx, innerIdx, nodeCount );
}
/* For each node, populate its edge list - edges and entrance nodes (if there) */
outerIdx = 0;
innerIdx = nodeCount-1;
for ( outerNodes = nodeTree; outerNodes != NULL; outerNodes = outerNodes->nextNode )
{
/* iterate through this node's edges to populate the EdgeIdArray */
node = outerNodes->node;
for ( edges = node->edges, edgeCount = 0;
edges != NULL;
edges = edges->nextEdge, ++edgeCount )
{
element = SearchDiagram_findNode( diagram, edges->targetNode );
diagram[outerIdx].edgeReferenceArray[edgeCount].edgeTarget = edges->targetNode;
diagram[outerIdx].edgeReferenceArray[edgeCount].targetNodeEdges = element->edgeReferenceArray;
}
++outerIdx;
/* And then scream through the interior nodes to take care of their edges */
for ( innerNodes = node->interiorNodes; innerNodes != NULL; innerNodes = innerNodes->nextNode )
{
node = innerNodes->node;
for ( edges = node->edges, edgeCount = 0;
edges != NULL;
edges = edges->nextEdge, ++edgeCount )
{
element = SearchDiagram_findNode( diagram, edges->targetNode );
diagram[innerIdx].edgeReferenceArray[edgeCount].edgeTarget = edges->targetNode;
diagram[innerIdx].edgeReferenceArray[edgeCount].targetNodeEdges = element->edgeReferenceArray;
}
--innerIdx;
}
}
return diagram;
}
bool SearchDiagram_findSignatureAlongEdges ( Node *node, EdgeReferences *edges,
char *labels[], NodePtrVec *result, Bitfield *visited )
{
bool success = false;
NodePtrVec *nextLegResult = NULL;
Bitfield *nextLegVisited = NULL;
int i;
/* A little basic error checking */
if ( !node || !edges || !labels || !result|| !visited )
{
return( false );
}
/* If this node has already been visited, we have found a loop. return false. */
/*
if ( NodePtrVec_findReverse( visited, node ) )
return( false );
else
NodePtrVec_push( visited, node );
*/
if ( Bitfield_nodeVisited( visited, node ) )
return( false );
NodePtrVec_push( result, node );
/* Go through the edges once to see if we've found a label match */
for ( i = 0; edges[i].edgeTarget != NULL; ++i )
{
if ( edges[i].edgeTarget->label )
{
//strcmp based:
if ( strcmp( edges[i].edgeTarget->label, labels[0] ) == 0 ) /* match! */
// index based: if ( edges[i].edgeTarget->labelIdx == labelIdxs[0] )
{
if ( labels[1] == NULL ) /* end of the signature, we're DONE */
{
NodePtrVec_push( result, edges[i].edgeTarget );
return( true );
}
else /* RECURSE TO SEE IF THE REST OF THE SIGNATURE CAN BE FOUND!!! */
{
nextLegResult = NodePtrVec_new( 50 ); /* arbitrary size, malloc success checked in recursion */
nextLegVisited = Bitfield_new(visited->bitsNeeded);
success = SearchDiagram_findSignatureAlongEdges ( edges[i].edgeTarget,
edges[i].targetNodeEdges,
&labels[1], nextLegResult, nextLegVisited );
Bitfield_delete( nextLegVisited );
if ( success )
{
NodePtrVec_appendVectors( result, nextLegResult, true );
NodePtrVec_delete( nextLegResult );
return( true );
}
}
}
}
}
/* IF we made it here, we need to continue through the tree, seeing if any of our
* edge nodes have a connection to a labeled node.
*/
for ( i = 0; edges[i].edgeTarget != NULL; ++i )
{
success = SearchDiagram_findSignatureAlongEdges( edges[i].edgeTarget,
edges[i].targetNodeEdges,
labels, result, visited );
if ( success )
return( true ); /* this edge has a path to the ultimate signature path */
}
/* and, if we make it here, we have failed. */
NodePtrVec_pop( result ); /* take current node off the result stack */
return false;
}