123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638 |
- /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
- /*
- * best view with tabstop=4
- */
- /*
- * SHORTEST PATH CACHE
- *
- * components:
- * - start node id
- * - visited network: a node is marked as visited when its departing
- * edges have been added to the cache
- * - predist network: node distances from start node
- * - NodeHeap: holds unvisited nodes, the next node extracted is the
- * unvisited node closest to SP start
- *
- * not all nodes in the predist network have been visited, SP from start
- * is known only for visited nodes
- * unvisited nodes can be reached, but not necessarily on the shortest
- * possible path
- * important for DGL_SP_CACHE_DISTANCE_FUNC and DGL_SP_CACHE_REPORT_FUNC
- */
- #if !defined(DGL_DEFINE_TREE_PROCS) && !defined(DGL_DEFINE_FLAT_PROCS)
- int DGL_SP_CACHE_INITIALIZE_FUNC(dglGraph_s * pgraph, dglSPCache_s * pCache,
- dglInt32_t nStart)
- {
- pCache->nStartNode = nStart;
- pCache->pvVisited = NULL;
- pCache->pvPredist = NULL;
- dglHeapInit(&pCache->NodeHeap);
- if ((pCache->pvVisited =
- avl_create(dglTreeTouchI32Compare, NULL,
- dglTreeGetAllocator())) == NULL)
- return -1;
- if ((pCache->pvPredist =
- avl_create(dglTreePredistCompare, NULL,
- dglTreeGetAllocator())) == NULL)
- return -1;
- return 0;
- }
- void DGL_SP_CACHE_RELEASE_FUNC(dglGraph_s * pgraph, dglSPCache_s * pCache)
- {
- if (pCache->pvVisited)
- avl_destroy(pCache->pvVisited, dglTreeTouchI32Cancel);
- if (pCache->pvPredist)
- avl_destroy(pCache->pvPredist, dglTreePredistCancel);
- dglHeapFree(&pCache->NodeHeap, NULL);
- }
- static int DGL_SP_CACHE_DISTANCE_FUNC(dglGraph_s * pgraph,
- dglSPCache_s * pCache,
- dglInt32_t * pnDistance,
- dglInt32_t nStart,
- dglInt32_t nDestination)
- {
- dglTreeTouchI32_s VisitedItem;
- dglTreePredist_s *pPredistItem, PredistItem;
- if (pCache->nStartNode != nStart) {
- pgraph->iErrno = DGL_ERR_HeadNodeNotFound;
- return -pgraph->iErrno;
- }
- VisitedItem.nKey = nDestination;
- if (avl_find(pCache->pvVisited, &VisitedItem) == NULL) {
- pgraph->iErrno = DGL_ERR_TailNodeNotFound;
- return -pgraph->iErrno;
- }
- PredistItem.nKey = nDestination;
- if ((pPredistItem = avl_find(pCache->pvPredist, &PredistItem)) == NULL) {
- pgraph->iErrno = DGL_ERR_UnexpectedNullPointer;
- return -pgraph->iErrno;
- }
- if (pnDistance)
- *pnDistance = pPredistItem->nDistance;
- return 0;
- }
- static dglSPReport_s *DGL_SP_CACHE_REPORT_FUNC(dglGraph_s * pgraph,
- dglSPCache_s * pCache,
- dglInt32_t nStart,
- dglInt32_t nDestination)
- {
- dglTreeTouchI32_s VisitedItem;
- dglTreePredist_s *pPredistItem, PredistItem;
- dglInt32_t *pEdge;
- dglInt32_t *pDestination;
- dglSPArc_s arc;
- long i, istack = 0;
- unsigned char *pstack = NULL;
- unsigned char *ppop;
- dglSPReport_s *pReport = NULL;
- if (pCache->nStartNode != nStart) {
- pgraph->iErrno = DGL_ERR_HeadNodeNotFound;
- return NULL;
- }
- VisitedItem.nKey = nDestination;
- if (avl_find(pCache->pvVisited, &VisitedItem) == NULL) {
- pgraph->iErrno = DGL_ERR_TailNodeNotFound;
- return NULL;
- }
- PredistItem.nKey = nDestination;
- if (avl_find(pCache->pvPredist, &PredistItem) == NULL) {
- pgraph->iErrno = DGL_ERR_UnexpectedNullPointer;
- return NULL;
- }
- for (PredistItem.nKey = nDestination,
- pPredistItem = avl_find(pCache->pvPredist, &PredistItem);
- pPredistItem;
- PredistItem.nKey = pPredistItem->nFrom,
- pPredistItem = avl_find(pCache->pvPredist, &PredistItem)
- ) {
- if (pPredistItem->nFrom < 0) {
- pgraph->iErrno = DGL_ERR_BadEdge;
- goto spr_error;
- }
- pEdge = (dglInt32_t *) pPredistItem->pnEdge;
- if (pPredistItem->bFlags == 0) {
- if (pgraph->Flags & DGL_GS_FLAT) {
- pDestination =
- DGL_NODEBUFFER_SHIFT(pgraph,
- DGL_EDGE_TAILNODE_OFFSET(pEdge));
- }
- else {
- pDestination =
- DGL_GET_NODE_FUNC(pgraph,
- DGL_EDGE_TAILNODE_OFFSET(pEdge));
- }
- }
- else {
- if (pgraph->Flags & DGL_GS_FLAT) {
- pDestination =
- DGL_NODEBUFFER_SHIFT(pgraph,
- DGL_EDGE_HEADNODE_OFFSET(pEdge));
- }
- else {
- pDestination =
- DGL_GET_NODE_FUNC(pgraph,
- DGL_EDGE_HEADNODE_OFFSET(pEdge));
- }
- }
- if ((arc.pnEdge = DGL_EDGE_ALLOC(pgraph->EdgeAttrSize)) == NULL)
- goto spr_error;
- arc.nFrom = pPredistItem->nFrom;
- arc.nTo = DGL_NODE_ID(pDestination);
- arc.nDistance = pPredistItem->nDistance;
- memcpy(arc.pnEdge, pEdge, DGL_EDGE_SIZEOF(pgraph->EdgeAttrSize));
- DGL_EDGE_COST(arc.pnEdge) = pPredistItem->nCost;
- if ((pstack =
- dgl_mempush(pstack, &istack, sizeof(dglSPArc_s),
- &arc)) == NULL) {
- pgraph->iErrno = DGL_ERR_MemoryExhausted;
- goto spr_error;
- }
- if (arc.nFrom == nStart)
- break;
- }
- if (pPredistItem == NULL) {
- pgraph->iErrno = DGL_ERR_UnexpectedNullPointer;
- goto spr_error;
- }
- if ((pReport = malloc(sizeof(dglSPReport_s))) == NULL) {
- pgraph->iErrno = DGL_ERR_MemoryExhausted;
- goto spr_error;
- }
- memset(pReport, 0, sizeof(dglSPReport_s));
- pReport->cArc = istack;
- if ((pReport->pArc = malloc(sizeof(dglSPArc_s) * pReport->cArc)) == NULL) {
- pgraph->iErrno = DGL_ERR_MemoryExhausted;
- goto spr_error;
- }
- pReport->nDistance = 0;
- for (i = 0;
- (ppop = dgl_mempop(pstack, &istack, sizeof(dglSPArc_s))) != NULL;
- i++) {
- memcpy(&pReport->pArc[i], ppop, sizeof(dglSPArc_s));
- pReport->nDistance += DGL_EDGE_COST(pReport->pArc[i].pnEdge);
- }
- pReport->nStartNode = nStart;
- pReport->nDestinationNode = nDestination;
- if (pstack)
- free(pstack);
- return pReport;
- spr_error:
- if (pstack)
- free(pstack);
- if (pReport)
- dglFreeSPReport(pgraph, pReport);
- return NULL;
- }
- #endif
- #if defined(DGL_DEFINE_TREE_PROCS) || defined(DGL_DEFINE_FLAT_PROCS)
- #define __EDGELOOP_BODY_1(f) \
- if ( (f) == 0 ) { \
- pDestination = _DGL_EDGE_TAILNODE(pgraph, pEdge); \
- } \
- else { \
- pDestination = _DGL_EDGE_HEADNODE(pgraph, pEdge); \
- } \
- if ( !(DGL_NODE_STATUS(pDestination) & DGL_NS_TAIL) && pgraph->Version < 3) { \
- pgraph->iErrno = DGL_ERR_BadEdge; \
- goto sp_error; \
- } \
- clipOutput.nEdgeCost = DGL_EDGE_COST(pEdge); \
- if ( fnClip ) { \
- clipInput.pnPrevEdge = NULL; \
- clipInput.pnNodeFrom = pStart; \
- clipInput.pnEdge = pEdge; \
- clipInput.pnNodeTo = pDestination; \
- clipInput.nFromDistance = 0; \
- if ( fnClip( pgraph , & clipInput , & clipOutput , pvClipArg ) ) continue; \
- } \
- findPredist.nKey = DGL_NODE_ID(pDestination); \
- if ( (pPredistItem = avl_find( pCache->pvPredist, &findPredist)) == NULL ) { \
- if ( (pPredistItem = dglTreePredistAdd( pCache->pvPredist, DGL_NODE_ID(pDestination) )) == NULL ) { \
- pgraph->iErrno = DGL_ERR_MemoryExhausted; \
- goto sp_error; \
- } \
- } \
- else { \
- if ( pPredistItem->nDistance <= clipOutput.nEdgeCost ) { \
- continue; \
- } \
- } \
- pPredistItem->nFrom = nStart; \
- pPredistItem->pnEdge = pEdge; \
- pPredistItem->nCost = clipOutput.nEdgeCost; \
- pPredistItem->nDistance = clipOutput.nEdgeCost; \
- pPredistItem->bFlags = (f); \
- heapvalue.pv = pEdge; \
- if ( dglHeapInsertMin( & pCache->NodeHeap, pPredistItem->nDistance , f , heapvalue ) < 0 ) { \
- pgraph->iErrno = DGL_ERR_HeapError; \
- goto sp_error; \
- }
- #define __EDGELOOP_BODY_2(f) \
- if ( (f) == 0 ) { \
- pDestination = _DGL_EDGE_TAILNODE(pgraph, pEdge); \
- } \
- else if ( pgraph->Version == 3 ) { \
- pDestination = _DGL_EDGE_HEADNODE(pgraph, pEdge); \
- } \
- if ( !(DGL_NODE_STATUS(pDestination) & DGL_NS_TAIL) && pgraph->Version < 3) { \
- pgraph->iErrno = DGL_ERR_BadEdge; \
- goto sp_error; \
- } \
- clipOutput.nEdgeCost = DGL_EDGE_COST(pEdge); \
- if ( fnClip ) { \
- clipInput.pnPrevEdge = pEdge_prev; \
- clipInput.pnNodeFrom = pStart; \
- clipInput.pnEdge = pEdge; \
- clipInput.pnNodeTo = pDestination; \
- clipInput.nFromDistance = fromDist; \
- if ( fnClip( pgraph , & clipInput , & clipOutput , pvClipArg ) ) continue; \
- } \
- findPredist.nKey = DGL_NODE_ID(pDestination); \
- if ( (pPredistItem = avl_find( pCache->pvPredist, &findPredist)) == NULL ) { \
- if ( (pPredistItem = dglTreePredistAdd( pCache->pvPredist, DGL_NODE_ID(pDestination) )) == NULL ) { \
- pgraph->iErrno = DGL_ERR_MemoryExhausted; \
- goto sp_error; \
- } \
- } \
- else { \
- if ( pPredistItem->nDistance <= fromDist + clipOutput.nEdgeCost ) { \
- continue; \
- } \
- } \
- pPredistItem->nFrom = DGL_NODE_ID(pStart); \
- pPredistItem->pnEdge = pEdge; \
- pPredistItem->nCost = clipOutput.nEdgeCost; \
- pPredistItem->nDistance = fromDist + clipOutput.nEdgeCost; \
- pPredistItem->bFlags = (f); \
- heapvalue.pv = pEdge; \
- if ( dglHeapInsertMin( & pCache->NodeHeap, pPredistItem->nDistance , f , heapvalue ) < 0 ) { \
- pgraph->iErrno = DGL_ERR_HeapError; \
- goto sp_error; \
- }
- /*
- * Dijkstra Shortest Path
- */
- int DGL_SP_DIJKSTRA_FUNC(dglGraph_s * pgraph,
- dglSPReport_s ** ppReport,
- dglInt32_t * pDistance,
- dglInt32_t nStart,
- dglInt32_t nDestination,
- dglSPClip_fn fnClip,
- void *pvClipArg, dglSPCache_s * pCache)
- {
- dglInt32_t *pStart; /* pointer to the start node (pgraph->pNodeBuffer) */
- register dglInt32_t *pDestination; /* temporary destination pointer */
- register dglInt32_t *pEdgeset; /* pointer to the edge (pgraph->pEdgeBuffer) */
- register dglInt32_t *pEdge; /* pointer to the to-edges in edge */
- register dglInt32_t *pEdge_prev; /* pointer to the previous edge in path */
- int nRet;
- dglEdgesetTraverser_s laT;
- dglSPCache_s spCache;
- int new_cache = 0;
- /*
- * shortest path distance temporary min heap
- */
- dglHeapData_u heapvalue;
- dglHeapNode_s heapnode;
- /*
- * shortest path visited network
- */
- dglTreeTouchI32_s *pVisitedItem, findVisited;
- /*
- * shortest path predecessor and distance network
- */
- dglTreePredist_s *pPredistItem, findPredist;
- /*
- * args to clip()
- */
- dglSPClipInput_s clipInput;
- dglSPClipOutput_s clipOutput;
- /*
- * Initialize the cache: initialize the heap and create temporary networks -
- * The use of a predist network for predecessor and distance has two important results:
- * 1) allows us not having to reset the whole graph status at each call;
- * 2) use of a stack memory area for temporary (and otherwise possibly thread-conflicting) states.
- * If a cache pointer was supplied, do not initialize it but try to get SP immediately.
- */
- if (pCache == NULL) {
- pCache = &spCache;
- DGL_SP_CACHE_INITIALIZE_FUNC(pgraph, pCache, nStart);
- new_cache = 1;
- }
- else {
- if (ppReport) {
- if ((*ppReport =
- DGL_SP_CACHE_REPORT_FUNC(pgraph, pCache, nStart,
- nDestination)) != NULL) {
- return 1;
- }
- }
- else {
- if (DGL_SP_CACHE_DISTANCE_FUNC
- (pgraph, pCache, pDistance, nStart, nDestination) >= 0) {
- return 2;
- }
- }
- if (pgraph->iErrno == DGL_ERR_HeadNodeNotFound) {
- DGL_SP_CACHE_RELEASE_FUNC(pgraph, pCache);
- DGL_SP_CACHE_INITIALIZE_FUNC(pgraph, pCache, nStart);
- new_cache = 1;
- }
- else if (pgraph->iErrno != DGL_ERR_TailNodeNotFound) {
- goto sp_error;
- }
- }
- /*
- * reset error status after using the cache
- */
- pgraph->iErrno = 0;
- if ((pStart = DGL_GET_NODE_FUNC(pgraph, nStart)) == NULL) {
- pgraph->iErrno = DGL_ERR_HeadNodeNotFound;
- goto sp_error;
- }
- if ((pDestination = DGL_GET_NODE_FUNC(pgraph, nDestination)) == NULL) {
- pgraph->iErrno = DGL_ERR_TailNodeNotFound;
- goto sp_error;
- }
- if ((DGL_NODE_STATUS(pStart) & DGL_NS_ALONE) ||
- (DGL_NODE_STATUS(pDestination) & DGL_NS_ALONE)) {
- goto sp_error;
- }
- if (!(DGL_NODE_STATUS(pStart) & DGL_NS_HEAD) && pgraph->Version < 3) {
- goto sp_error;
- }
- if (!(DGL_NODE_STATUS(pDestination) & DGL_NS_TAIL) && pgraph->Version < 3) {
- goto sp_error;
- }
- /* if we do not need a new cache, we just continue with the unvisited
- * nodes in the cache */
- if (new_cache) {
- /*
- * now we inspect all edges departing from the start node
- * - at each loop 'pedge' points to the edge in the edge buffer
- * - we invoke the caller's clip() and eventually skip the edge (clip() != 0)
- * - we insert a item in the predist network to set actual predecessor and distance
- * (there is no precedecessor at this stage) and actual distance from the starting node
- * (at this stage it equals the edge's cost)
- * - we insert a item in the node min-heap (sorted on node distance), storing the offset of the
- * edge in the edge buffer.
- * In the case of undirected graph (version 3) we inspect input edges as well.
- */
- pEdgeset = _DGL_OUTEDGESET(pgraph, pStart);
- if (DGL_EDGESET_T_INITIALIZE_FUNC(pgraph, &laT, pEdgeset) < 0) {
- goto sp_error;
- }
- for (pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT);
- pEdge; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT)
- ) {
- __EDGELOOP_BODY_1(0);
- }
- DGL_EDGESET_T_RELEASE_FUNC(&laT);
- if (pgraph->Version == 3) {
- pEdgeset = _DGL_INEDGESET(pgraph, pStart);
- if (DGL_EDGESET_T_INITIALIZE_FUNC(pgraph, &laT, pEdgeset) < 0) {
- goto sp_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;
- __EDGELOOP_BODY_1(1);
- }
- DGL_EDGESET_T_RELEASE_FUNC(&laT);
- }
- }
- /*
- * Now we begin extracting nodes from the min-heap. Each node extracted is
- * the one that is actually closest to the SP start.
- */
- while (dglHeapExtractMin(&pCache->NodeHeap, &heapnode) == 1) {
- dglInt32_t fromDist;
- /*
- * recover the stored edge pointer
- */
- pEdge = heapnode.value.pv;
- /*
- * the new relative head is the tail of the edge
- * or the head of the edge if the traversal was reversed (undirected edge)
- */
- if (heapnode.flags == 0) {
- pStart = _DGL_EDGE_TAILNODE(pgraph, pEdge); /* continue from previous tail */
- }
- else {
- pStart = _DGL_EDGE_HEADNODE(pgraph, pEdge); /* reversed head/tail */
- }
- /*
- * We do not want to explore twice the same node as a relative starting point,
- * that's the meaning of 'visited'. We mark actual start node as 'visited' by
- * inserting it into the visited-network. If we find actual node in the network
- * we just give up and continue looping. Otherwise we add actual node to the network.
- */
- findVisited.nKey = DGL_NODE_ID(pStart);
- if ((pVisitedItem =
- avl_find(pCache->pvVisited, &findVisited)) == NULL) {
- if (dglTreeTouchI32Add(pCache->pvVisited, DGL_NODE_ID(pStart)) ==
- NULL) {
- pgraph->iErrno = DGL_ERR_MemoryExhausted;
- goto sp_error;
- }
- }
- /*
- * Give up with visited nodes now
- */
- if (pVisitedItem) {
- if (DGL_NODE_ID(pStart) == nDestination) {
- /* should not happen but does not harm
- * this case should have been handled above */
- goto destination_found;
- }
- else
- continue;
- }
- /*
- * If the node is not marked as having departing edges, then we are into a
- * blind alley. Just give up this direction and continue looping.
- * This only applies to v1 and v2 (digraphs)
- */
- if (!(DGL_NODE_STATUS(pStart) & DGL_NS_HEAD) && pgraph->Version < 3) {
- if (DGL_NODE_ID(pStart) == nDestination) {
- goto destination_found;
- }
- else
- continue;
- }
- /*
- * save actual edge for later clip()
- */
- pEdge_prev = pEdge;
- /*
- * Recover the head node distance from the predist network
- */
- findPredist.nKey = DGL_NODE_ID(pStart);
- if ((pPredistItem =
- avl_find(pCache->pvPredist, &findPredist)) == NULL) {
- pgraph->iErrno = DGL_ERR_UnexpectedNullPointer;
- goto sp_error;
- }
- fromDist = pPredistItem->nDistance;
- /*
- * Loop on departing edges:
- * Scan the edgeset and loads pedge at each iteration with next-edge.
- * iWay == DGL_EDGESET_T_WAY_OUT then pedge is a out arc (departing from node) else ot is a in arc.
- * V1 has no in-degree support so iWay is always OUT, V2/3 have in-degree support.
- *
- * This loop needs to be done also when destination is found, otherwise
- * the node is marked as visited but its departing edges are not added to the cache
- * --> loose end, we might need these edges later on
- */
- pEdgeset = _DGL_OUTEDGESET(pgraph, pStart);
- if (DGL_EDGESET_T_INITIALIZE_FUNC(pgraph, &laT, pEdgeset) < 0) {
- goto sp_error;
- }
- for (pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT);
- pEdge; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT)
- ) {
- __EDGELOOP_BODY_2(0);
- }
- DGL_EDGESET_T_RELEASE_FUNC(&laT);
- if (pgraph->Version == 3) {
- pEdgeset = _DGL_INEDGESET(pgraph, pStart);
- if (DGL_EDGESET_T_INITIALIZE_FUNC(pgraph, &laT, pEdgeset) < 0) {
- goto sp_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;
- __EDGELOOP_BODY_2(1);
- }
- DGL_EDGESET_T_RELEASE_FUNC(&laT);
- }
- /*
- * Dijkstra algorithm ends when the destination node is extracted from
- * the min distance heap, that means: no other path exist in the network giving
- * a shortest output.
- * If this happens we jump to the epilogue in order to build a path report and return.
- */
- if (DGL_NODE_ID(pStart) == nDestination) {
- goto destination_found;
- }
- }
- sp_error:
- if (pCache == &spCache) {
- DGL_SP_CACHE_RELEASE_FUNC(pgraph, pCache);
- }
- return -pgraph->iErrno; /* == 0 path not found */
- destination_found: /* path found - build a shortest path report or report the distance only */
- if (ppReport) {
- *ppReport =
- DGL_SP_CACHE_REPORT_FUNC(pgraph, pCache, nStart, nDestination);
- if (*ppReport == NULL) {
- nRet = -pgraph->iErrno;
- }
- else {
- nRet = 1;
- }
- }
- else {
- if (DGL_SP_CACHE_DISTANCE_FUNC
- (pgraph, pCache, pDistance, nStart, nDestination) < 0) {
- nRet = -pgraph->iErrno;
- }
- else {
- nRet = 2;
- }
- }
- if (pCache == &spCache) {
- DGL_SP_CACHE_RELEASE_FUNC(pgraph, pCache);
- }
- return nRet;
- }
- #endif
|