/* LIBDGL -- a Directed Graph Library implementation * Copyright (C) 2002 Roberto Micarelli * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * best view with tabstop=4 */ /* * Build the depth-first spanning tree of 'pgraphIn' into 'pgraphOut' * - pgraphOut must have been previously initialized by the caller and is returned in TREE state * - I prefer using a iterative approach with a stack for 'waiting edges' instead of recursion: it's * cheaper to stack 8 bytes for each edge than the whole function stack * - The visited network is passed by the caller because this function can be used for two purposes: * 1. generate a single spanning tree (dglDepthSpanning) * 2. part of a loop for generating connected-components of the graph (dglDepthComponents) */ int DGL_SPAN_DEPTHFIRST_SPANNING_FUNC( dglGraph_s * pgraphIn , dglGraph_s * pgraphOut , dglInt32_t nVertex , void * pvVisited , dglSpanClip_fn fnClip , void * pvClipArg ) { struct _stackItem { dglInt32_t * pnHead; dglInt32_t * pnEdge; int iWay; }; struct _stackItem stackItem; struct _stackItem * pStackItem; dglInt32_t * pHead; dglInt32_t * pTail; dglInt32_t * pEdgeset; dglInt32_t * pEdge; long istack = 0; unsigned char * pstack = NULL; int nret; dglSpanClipInput_s clipInput; dglTreeNode_s findVisited; dglEdgesetTraverser_s laT; if ( (pHead = dglGetNode( pgraphIn, nVertex )) == NULL ) { pgraphIn->iErrno = DGL_ERR_HeadNodeNotFound; goto dfs_error; } /* * the simplest case is when vertex node is alone or has no outgoing edges, the result * of the spanning is a graph having only one node */ if ( DGL_NODE_STATUS(pHead) & DGL_NS_ALONE || (!(DGL_NODE_STATUS(pHead) & DGL_NS_HEAD) && DGL_NODE_STATUS(pHead) & DGL_NS_TAIL) ) { nret = DGL_ADD_NODE_FUNC( pgraphOut, DGL_NODE_ID(pHead), DGL_NODE_ATTR_PTR(pHead), 0 ); if ( nret < 0 ) { goto dfs_error; } return 0; } if ( (DGL_NODE_STATUS(pHead) & DGL_NS_HEAD) || pgraphIn->Version == 3) { pEdgeset = _DGL_OUTEDGESET(pgraphIn, pHead); if ( DGL_EDGESET_T_INITIALIZE_FUNC(pgraphIn,&laT,pEdgeset) < 0 ) { goto dfs_error; } for ( pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT) ; pEdge ; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT) ) { stackItem.pnHead = pHead; stackItem.pnEdge = pEdge; stackItem.iWay = 0; if ( (pstack = dgl_mempush( pstack , & istack , sizeof(stackItem) , &stackItem )) == NULL ) { pgraphIn->iErrno = DGL_ERR_MemoryExhausted; goto dfs_error; } } DGL_EDGESET_T_RELEASE_FUNC(&laT); if ( pgraphIn->Version == 3 ) { pEdgeset = _DGL_INEDGESET(pgraphIn, pHead); if ( DGL_EDGESET_T_INITIALIZE_FUNC(pgraphIn,&laT,pEdgeset) < 0 ) { goto dfs_error; } for ( pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT) ; pEdge ; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT) ) { if (DGL_EDGE_STATUS(pEdge) & DGL_ES_DIRECTED) continue; stackItem.pnHead = pHead; stackItem.pnEdge = pEdge; stackItem.iWay = 1; if ( (pstack = dgl_mempush( pstack , & istack , sizeof(stackItem) , &stackItem )) == NULL ) { pgraphIn->iErrno = DGL_ERR_MemoryExhausted; goto dfs_error; } } DGL_EDGESET_T_RELEASE_FUNC(&laT); } if ( dglTreeNodeAdd(pvVisited , DGL_NODE_ID(pHead)) == NULL ) { pgraphIn->iErrno = DGL_ERR_MemoryExhausted; goto dfs_error; } } while( (pStackItem = (struct _stackItem *)dgl_mempop( pstack , & istack , sizeof(stackItem) )) != NULL ) { pHead = pStackItem->pnHead; pEdge = pStackItem->pnEdge; if ( pStackItem->iWay == 0 ) pTail = _DGL_EDGE_TAILNODE(pgraphIn, pEdge); else pTail = _DGL_EDGE_HEADNODE(pgraphIn, pEdge); findVisited.nKey = DGL_NODE_ID(pTail); if ( avl_find( pvVisited , &findVisited) ) { /* already visited */ continue; } if ( fnClip ) { clipInput.pnNodeFrom = pHead; clipInput.pnEdge = pEdge; clipInput.pnNodeTo = pTail; if ( fnClip( pgraphIn, pgraphOut, & clipInput, NULL, pvClipArg ) ) continue; } if ( dglTreeNodeAdd(pvVisited , DGL_NODE_ID(pTail)) == NULL ) { pgraphIn->iErrno = DGL_ERR_MemoryExhausted; goto dfs_error; } /* add this edge */ nret = DGL_ADD_EDGE_FUNC( pgraphOut, DGL_NODE_ID(pHead), DGL_NODE_ID(pTail), DGL_EDGE_COST(pEdge), DGL_EDGE_ID(pEdge), DGL_NODE_ATTR_PTR(pHead), DGL_NODE_ATTR_PTR(pTail), DGL_EDGE_ATTR_PTR(pEdge), 0 ); if ( nret < 0 ) { goto dfs_error; } if ( (DGL_NODE_STATUS(pHead) & DGL_NS_HEAD) || pgraphIn->Version == 3 ) { pEdgeset = _DGL_OUTEDGESET(pgraphIn, pTail); if ( DGL_EDGESET_T_INITIALIZE_FUNC(pgraphIn,&laT,pEdgeset) < 0 ) { goto dfs_error; } for ( pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT) ; pEdge ; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT) ) { stackItem.pnHead = pTail; stackItem.pnEdge = pEdge; stackItem.iWay = 0; if ( (pstack = dgl_mempush( pstack , & istack , sizeof(stackItem) , &stackItem )) == NULL ) { pgraphIn->iErrno = DGL_ERR_MemoryExhausted; goto dfs_error; } } DGL_EDGESET_T_RELEASE_FUNC(&laT); if ( pgraphIn->Version == 3 ) { pEdgeset = _DGL_INEDGESET(pgraphIn, pTail); if ( DGL_EDGESET_T_INITIALIZE_FUNC(pgraphIn,&laT,pEdgeset) < 0 ) { goto dfs_error; } for ( pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT) ; pEdge ; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT) ) { if (DGL_EDGE_STATUS(pEdge) & DGL_ES_DIRECTED) continue; stackItem.pnHead = pTail; stackItem.pnEdge = pEdge; stackItem.iWay = 1; if ( (pstack = dgl_mempush( pstack , & istack , sizeof(stackItem) , &stackItem )) == NULL ) { pgraphIn->iErrno = DGL_ERR_MemoryExhausted; goto dfs_error; } } DGL_EDGESET_T_RELEASE_FUNC(&laT); } } } if ( pstack ) free( pstack ); return 0; dfs_error: if ( pstack ) free( pstack ); return -pgraphIn->iErrno; } /* * Use a edge prioritized, tree growing scheme (aka Prim algorithm) in order to * be appliable to both undirected graphs (minimum spanning tree - MST) and * digraphs (minimum arborescense tree - MAT) * The vertex argument is ignored in MST (when algorithm is applied to a * version 3 undirected graph). */ int DGL_SPAN_MINIMUM_SPANNING_FUNC( dglGraph_s * pgraphIn , dglGraph_s * pgraphOut , dglInt32_t nVertex , dglSpanClip_fn fnClip , void * pvClipArg ) { dglInt32_t * pHead, * pTail , * pEdgeset , * pEdge; dglHeap_s FrontEdgeHeap; dglHeapData_u HeapData; dglHeapNode_s HeapItem; dglTreeNode_s * pPredistItem, findItem; dglEdgesetTraverser_s laT; int nret; dglHeapInit( & FrontEdgeHeap ); if ( pgraphIn->Version == 3 ) { /* undirected: pick up the first node */ dglNodeTraverser_s pT; DGL_NODE_T_INITIALIZE_FUNC( pgraphIn, &pT ); pHead = DGL_NODE_T_FIRST_FUNC( &pT ); DGL_NODE_T_RELEASE_FUNC( &pT ); } else { /* directed: pick up the arborescense origin */ pHead = DGL_GET_NODE_FUNC( pgraphIn, nVertex ); } if ( pHead == NULL ) { pgraphIn->iErrno = DGL_ERR_HeadNodeNotFound; goto mst_error; } if ( DGL_NODE_STATUS(pHead) & DGL_NS_HEAD || DGL_NODE_STATUS(pHead) & DGL_NS_ALONE ) { if ( (DGL_NODE_STATUS(pHead) & DGL_NS_HEAD) || (DGL_NODE_STATUS(pHead) & DGL_NS_ALONE) || pgraphIn->Version == 3 ) { if ( DGL_ADD_NODE_FUNC(pgraphOut, DGL_NODE_ID(pHead), DGL_NODE_ATTR_PTR(pHead), 0) < 0 ) { goto mst_error; } if ( DGL_NODE_STATUS(pHead) & DGL_NS_ALONE ) { dglHeapFree( &FrontEdgeHeap , NULL ); return 0; } pEdgeset = _DGL_OUTEDGESET(pgraphIn, pHead); if ( DGL_EDGESET_T_INITIALIZE_FUNC(pgraphIn,&laT,pEdgeset) < 0 ) { goto mst_error; } for ( pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT) ; pEdge ; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT) ) { HeapData.pv = pEdge; if ( dglHeapInsertMin( &FrontEdgeHeap, DGL_EDGE_COST(pEdge), 0, HeapData ) < 0 ) { pgraphIn->iErrno = DGL_ERR_HeapError; goto mst_error; } } DGL_EDGESET_T_RELEASE_FUNC(&laT); if ( pgraphIn->Version == 3 ) { pEdgeset = _DGL_INEDGESET(pgraphIn, pHead); if ( DGL_EDGESET_T_INITIALIZE_FUNC(pgraphIn,&laT,pEdgeset) < 0 ) { goto mst_error; } for ( pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT) ; pEdge ; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT) ) { if (DGL_EDGE_STATUS(pEdge) & DGL_ES_DIRECTED) continue; HeapData.pv = pEdge; if ( dglHeapInsertMin( &FrontEdgeHeap, DGL_EDGE_COST(pEdge), 1, HeapData ) < 0 ) { pgraphIn->iErrno = DGL_ERR_HeapError; goto mst_error; } } DGL_EDGESET_T_RELEASE_FUNC(&laT); } } } else { pgraphIn->iErrno = DGL_ERR_BadEdge; goto mst_error; } while ( dglHeapExtractMin( &FrontEdgeHeap , &HeapItem ) == 1 ) { pEdge = HeapItem.value.pv; if ( HeapItem.flags == 0 ) { if ( (pHead = _DGL_EDGE_HEADNODE( pgraphIn, pEdge )) == NULL ) { pgraphIn->iErrno = DGL_ERR_UnexpectedNullPointer; goto mst_error; } if ( (pTail = _DGL_EDGE_TAILNODE( pgraphIn, pEdge )) == NULL ) { pgraphIn->iErrno = DGL_ERR_UnexpectedNullPointer; goto mst_error; } } else if (pgraphIn->Version == 3) { if ( (pTail = _DGL_EDGE_HEADNODE( pgraphIn, pEdge )) == NULL ) { pgraphIn->iErrno = DGL_ERR_UnexpectedNullPointer; goto mst_error; } if ( (pHead = _DGL_EDGE_TAILNODE( pgraphIn, pEdge )) == NULL ) { pgraphIn->iErrno = DGL_ERR_UnexpectedNullPointer; goto mst_error; } } else continue; findItem.nKey = DGL_NODE_ID( pTail ); if ( (pPredistItem = avl_find(pgraphOut->pNodeTree, &findItem)) != NULL ) { continue; } nret = DGL_ADD_EDGE_FUNC (pgraphOut, DGL_NODE_ID(pHead), DGL_NODE_ID(pTail), DGL_EDGE_COST(pEdge), DGL_EDGE_ID(pEdge), DGL_NODE_ATTR_PTR(pHead), DGL_NODE_ATTR_PTR(pTail), DGL_EDGE_ATTR_PTR(pEdge), 0 ); if ( nret < 0 ) { goto mst_error; } pHead = pTail; if ( (DGL_NODE_STATUS(pHead) & DGL_NS_HEAD) || pgraphIn->Version == 3 ) { pEdgeset = _DGL_OUTEDGESET(pgraphIn, pHead); if ( DGL_EDGESET_T_INITIALIZE_FUNC(pgraphIn,&laT,pEdgeset) < 0 ) { goto mst_error; } for ( pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT) ; pEdge ; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT) ) { HeapData.pv = pEdge; if ( dglHeapInsertMin( &FrontEdgeHeap, DGL_EDGE_COST(pEdge), 0, HeapData ) < 0 ) { pgraphIn->iErrno = DGL_ERR_HeapError; goto mst_error; } } if ( pgraphIn->Version == 3 ) { DGL_EDGESET_T_RELEASE_FUNC(&laT); pEdgeset = _DGL_OUTEDGESET(pgraphIn, pHead); if ( DGL_EDGESET_T_INITIALIZE_FUNC(pgraphIn,&laT,pEdgeset) < 0 ) { goto mst_error; } for ( pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT) ; pEdge ; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT) ) { if (DGL_EDGE_STATUS(pEdge) & DGL_ES_DIRECTED) continue; HeapData.pv = pEdge; if ( dglHeapInsertMin( &FrontEdgeHeap, DGL_EDGE_COST(pEdge), 1, HeapData ) < 0 ) { pgraphIn->iErrno = DGL_ERR_HeapError; goto mst_error; } } DGL_EDGESET_T_RELEASE_FUNC(&laT); } } } dglHeapFree( &FrontEdgeHeap , NULL ); return 0; mst_error: dglHeapFree( &FrontEdgeHeap , NULL ); return -pgraphIn->iErrno; }