sp-template.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. /* LIBDGL -- a Directed Graph Library implementation
  2. * Copyright (C) 2002 Roberto Micarelli
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. */
  18. /*
  19. * best view with tabstop=4
  20. */
  21. /*
  22. * SHORTEST PATH CACHE
  23. */
  24. #if !defined(DGL_DEFINE_TREE_PROCS) && !defined(DGL_DEFINE_FLAT_PROCS)
  25. int DGL_SP_CACHE_INITIALIZE_FUNC(dglGraph_s * pgraph, dglSPCache_s * pCache,
  26. dglInt32_t nStart)
  27. {
  28. pCache->nStartNode = nStart;
  29. pCache->pvVisited = NULL;
  30. pCache->pvPredist = NULL;
  31. dglHeapInit(&pCache->NodeHeap);
  32. if ((pCache->pvVisited =
  33. avl_create(dglTreeTouchI32Compare, NULL,
  34. dglTreeGetAllocator())) == NULL)
  35. return -1;
  36. if ((pCache->pvPredist =
  37. avl_create(dglTreePredistCompare, NULL,
  38. dglTreeGetAllocator())) == NULL)
  39. return -1;
  40. return 0;
  41. }
  42. void DGL_SP_CACHE_RELEASE_FUNC(dglGraph_s * pgraph, dglSPCache_s * pCache)
  43. {
  44. if (pCache->pvVisited)
  45. avl_destroy(pCache->pvVisited, dglTreeTouchI32Cancel);
  46. if (pCache->pvPredist)
  47. avl_destroy(pCache->pvPredist, dglTreePredistCancel);
  48. dglHeapFree(&pCache->NodeHeap, NULL);
  49. }
  50. static int DGL_SP_CACHE_DISTANCE_FUNC(dglGraph_s * pgraph,
  51. dglSPCache_s * pCache,
  52. dglInt32_t * pnDistance,
  53. dglInt32_t nStart,
  54. dglInt32_t nDestination)
  55. {
  56. dglTreeTouchI32_s *pVisitedItem, VisitedItem;
  57. dglTreePredist_s *pPredistItem, PredistItem;
  58. if (pCache->nStartNode != nStart) {
  59. pgraph->iErrno = DGL_ERR_HeadNodeNotFound;
  60. return -pgraph->iErrno;
  61. }
  62. VisitedItem.nKey = nDestination;
  63. if ((pVisitedItem = avl_find(pCache->pvPredist, &VisitedItem)) == NULL) {
  64. pgraph->iErrno = DGL_ERR_TailNodeNotFound;
  65. return -pgraph->iErrno;
  66. }
  67. PredistItem.nKey = nDestination;
  68. if ((pPredistItem = avl_find(pCache->pvPredist, &PredistItem)) == NULL) {
  69. pgraph->iErrno = DGL_ERR_UnexpectedNullPointer;
  70. return -pgraph->iErrno;
  71. }
  72. if (pnDistance)
  73. *pnDistance = pPredistItem->nDistance;
  74. return 0;
  75. }
  76. static dglSPReport_s *DGL_SP_CACHE_REPORT_FUNC(dglGraph_s * pgraph,
  77. dglSPCache_s * pCache,
  78. dglInt32_t nStart,
  79. dglInt32_t nDestination)
  80. {
  81. dglTreeTouchI32_s VisitedItem;
  82. dglTreePredist_s *pPredistItem, PredistItem;
  83. dglInt32_t *pEdge;
  84. dglInt32_t *pDestination;
  85. dglSPArc_s arc;
  86. long i, istack = 0;
  87. unsigned char *pstack = NULL;
  88. unsigned char *ppop;
  89. dglSPReport_s *pReport = NULL;
  90. if (pCache->nStartNode != nStart) {
  91. pgraph->iErrno = DGL_ERR_HeadNodeNotFound;
  92. return NULL;
  93. }
  94. VisitedItem.nKey = nDestination;
  95. if (avl_find(pCache->pvVisited, &VisitedItem) == NULL) {
  96. pgraph->iErrno = DGL_ERR_TailNodeNotFound;
  97. return NULL;
  98. }
  99. for (PredistItem.nKey = nDestination,
  100. pPredistItem = avl_find(pCache->pvPredist, &PredistItem);
  101. pPredistItem;
  102. PredistItem.nKey = pPredistItem->nFrom,
  103. pPredistItem = avl_find(pCache->pvPredist, &PredistItem)
  104. ) {
  105. if (pPredistItem->nFrom < 0) {
  106. pgraph->iErrno = DGL_ERR_BadEdge;
  107. goto spr_error;
  108. }
  109. pEdge = (dglInt32_t *) pPredistItem->pnEdge;
  110. if (pPredistItem->bFlags == 0) {
  111. if (pgraph->Flags & DGL_GS_FLAT) {
  112. pDestination =
  113. DGL_NODEBUFFER_SHIFT(pgraph,
  114. DGL_EDGE_TAILNODE_OFFSET(pEdge));
  115. }
  116. else {
  117. pDestination =
  118. DGL_GET_NODE_FUNC(pgraph,
  119. DGL_EDGE_TAILNODE_OFFSET(pEdge));
  120. }
  121. }
  122. else {
  123. if (pgraph->Flags & DGL_GS_FLAT) {
  124. pDestination =
  125. DGL_NODEBUFFER_SHIFT(pgraph,
  126. DGL_EDGE_HEADNODE_OFFSET(pEdge));
  127. }
  128. else {
  129. pDestination =
  130. DGL_GET_NODE_FUNC(pgraph,
  131. DGL_EDGE_HEADNODE_OFFSET(pEdge));
  132. }
  133. }
  134. if ((arc.pnEdge = DGL_EDGE_ALLOC(pgraph->EdgeAttrSize)) == NULL)
  135. goto spr_error;
  136. arc.nFrom = pPredistItem->nFrom;
  137. arc.nTo = DGL_NODE_ID(pDestination);
  138. arc.nDistance = pPredistItem->nDistance;
  139. memcpy(arc.pnEdge, pEdge, DGL_EDGE_SIZEOF(pgraph->EdgeAttrSize));
  140. DGL_EDGE_COST(arc.pnEdge) = pPredistItem->nCost;
  141. if ((pstack =
  142. dgl_mempush(pstack, &istack, sizeof(dglSPArc_s),
  143. &arc)) == NULL) {
  144. pgraph->iErrno = DGL_ERR_MemoryExhausted;
  145. goto spr_error;
  146. }
  147. if (arc.nFrom == nStart)
  148. break;
  149. }
  150. if (pPredistItem == NULL) {
  151. pgraph->iErrno = DGL_ERR_UnexpectedNullPointer;
  152. goto spr_error;
  153. }
  154. if ((pReport = malloc(sizeof(dglSPReport_s))) == NULL) {
  155. pgraph->iErrno = DGL_ERR_MemoryExhausted;
  156. goto spr_error;
  157. }
  158. memset(pReport, 0, sizeof(dglSPReport_s));
  159. pReport->cArc = istack;
  160. if ((pReport->pArc = malloc(sizeof(dglSPArc_s) * pReport->cArc)) == NULL) {
  161. pgraph->iErrno = DGL_ERR_MemoryExhausted;
  162. goto spr_error;
  163. }
  164. pReport->nDistance = 0;
  165. for (i = 0;
  166. (ppop = dgl_mempop(pstack, &istack, sizeof(dglSPArc_s))) != NULL;
  167. i++) {
  168. memcpy(&pReport->pArc[i], ppop, sizeof(dglSPArc_s));
  169. pReport->nDistance += DGL_EDGE_COST(pReport->pArc[i].pnEdge);
  170. }
  171. pReport->nStartNode = nStart;
  172. pReport->nDestinationNode = nDestination;
  173. if (pstack)
  174. free(pstack);
  175. return pReport;
  176. spr_error:
  177. if (pstack)
  178. free(pstack);
  179. if (pReport)
  180. dglFreeSPReport(pgraph, pReport);
  181. return NULL;
  182. }
  183. #endif
  184. #if defined(DGL_DEFINE_TREE_PROCS) || defined(DGL_DEFINE_FLAT_PROCS)
  185. #define __EDGELOOP_BODY_1(f) \
  186. if ( (f) == 0 ) { \
  187. pDestination = _DGL_EDGE_TAILNODE(pgraph, pEdge); \
  188. } \
  189. else { \
  190. pDestination = _DGL_EDGE_HEADNODE(pgraph, pEdge); \
  191. } \
  192. if ( !(DGL_NODE_STATUS(pDestination) & DGL_NS_TAIL) && pgraph->Version < 3) { \
  193. pgraph->iErrno = DGL_ERR_BadEdge; \
  194. goto sp_error; \
  195. } \
  196. clipOutput.nEdgeCost = DGL_EDGE_COST(pEdge); \
  197. if ( fnClip ) { \
  198. clipInput.pnPrevEdge = NULL; \
  199. clipInput.pnNodeFrom = pStart; \
  200. clipInput.pnEdge = pEdge; \
  201. clipInput.pnNodeTo = pDestination; \
  202. clipInput.nFromDistance = 0; \
  203. if ( fnClip( pgraph , & clipInput , & clipOutput , pvClipArg ) ) continue; \
  204. } \
  205. pPredistItem = dglTreePredistAdd( pCache->pvPredist, DGL_NODE_ID(pDestination) ); \
  206. if ( pPredistItem == NULL ) goto sp_error; \
  207. pPredistItem->nFrom = nStart; \
  208. pPredistItem->pnEdge = pEdge; \
  209. pPredistItem->nCost = clipOutput.nEdgeCost; \
  210. pPredistItem->nDistance = clipOutput.nEdgeCost; \
  211. pPredistItem->bFlags = (f); \
  212. heapvalue.pv = pEdge; \
  213. if ( dglHeapInsertMin( & pCache->NodeHeap, pPredistItem->nDistance , f , heapvalue ) < 0 ) { \
  214. pgraph->iErrno = DGL_ERR_HeapError; \
  215. goto sp_error; \
  216. }
  217. #define __EDGELOOP_BODY_2(f) \
  218. if ( (f) == 0 ) { \
  219. pDestination = _DGL_EDGE_TAILNODE(pgraph, pEdge); \
  220. } \
  221. else if ( pgraph->Version == 3 ) { \
  222. pDestination = _DGL_EDGE_HEADNODE(pgraph, pEdge); \
  223. } \
  224. if ( !(DGL_NODE_STATUS(pDestination) & DGL_NS_TAIL) && pgraph->Version < 3) { \
  225. pgraph->iErrno = DGL_ERR_BadEdge; \
  226. goto sp_error; \
  227. } \
  228. clipOutput.nEdgeCost = DGL_EDGE_COST(pEdge); \
  229. if ( fnClip ) { \
  230. clipInput.pnPrevEdge = pEdge_prev; \
  231. clipInput.pnNodeFrom = pStart; \
  232. clipInput.pnEdge = pEdge; \
  233. clipInput.pnNodeTo = pDestination; \
  234. clipInput.nFromDistance = fromDist; \
  235. if ( fnClip( pgraph , & clipInput , & clipOutput , pvClipArg ) ) continue; \
  236. } \
  237. findPredist.nKey = DGL_NODE_ID(pDestination); \
  238. if ( (pPredistItem = avl_find( pCache->pvPredist, &findPredist)) == NULL ) { \
  239. if ( (pPredistItem = dglTreePredistAdd( pCache->pvPredist, DGL_NODE_ID(pDestination) )) == NULL ) { \
  240. pgraph->iErrno = DGL_ERR_MemoryExhausted; \
  241. goto sp_error; \
  242. } \
  243. } \
  244. else { \
  245. if ( pPredistItem->nDistance <= fromDist + clipOutput.nEdgeCost ) { \
  246. continue; \
  247. } \
  248. } \
  249. pPredistItem->nFrom = DGL_NODE_ID(pStart); \
  250. pPredistItem->pnEdge = pEdge; \
  251. pPredistItem->nCost = clipOutput.nEdgeCost; \
  252. pPredistItem->nDistance = fromDist + clipOutput.nEdgeCost; \
  253. pPredistItem->bFlags = (f); \
  254. heapvalue.pv = pEdge; \
  255. if ( dglHeapInsertMin( & pCache->NodeHeap, pPredistItem->nDistance , f , heapvalue ) < 0 ) { \
  256. pgraph->iErrno = DGL_ERR_HeapError; \
  257. goto sp_error; \
  258. }
  259. /*
  260. * Dijkstra Shortest Path
  261. */
  262. int DGL_SP_DIJKSTRA_FUNC(dglGraph_s * pgraph,
  263. dglSPReport_s ** ppReport,
  264. dglInt32_t * pDistance,
  265. dglInt32_t nStart,
  266. dglInt32_t nDestination,
  267. dglSPClip_fn fnClip,
  268. void *pvClipArg, dglSPCache_s * pCache)
  269. {
  270. dglInt32_t *pStart; /* pointer to the start node (pgraph->pNodeBuffer) */
  271. register dglInt32_t *pDestination; /* temporary destination pointer */
  272. register dglInt32_t *pEdgeset; /* pointer to the edge (pgraph->pEdgeBuffer) */
  273. register dglInt32_t *pEdge; /* pointer to the to-edges in edge */
  274. register dglInt32_t *pEdge_prev; /* pointer to the previous edge in path */
  275. int nRet;
  276. dglEdgesetTraverser_s laT;
  277. dglSPCache_s spCache;
  278. /*
  279. * shortest path distance temporary min heap
  280. */
  281. dglHeapData_u heapvalue;
  282. dglHeapNode_s heapnode;
  283. /*
  284. * shortest path visited network
  285. */
  286. dglTreeTouchI32_s *pVisitedItem, findVisited;
  287. /*
  288. * shortest path predecessor and distance network
  289. */
  290. dglTreePredist_s *pPredistItem, findPredist;
  291. /*
  292. * args to clip()
  293. */
  294. dglSPClipInput_s clipInput;
  295. dglSPClipOutput_s clipOutput;
  296. /*
  297. * Initialize the cache: initialize the heap and create temporary networks -
  298. * The use of a predist network for predecessor and distance has two important results:
  299. * 1) allows us not having to reset the whole graph status at each call;
  300. * 2) use of a stack memory area for temporary (and otherwise possibly thread-conflicting) states.
  301. * If a cache pointer was supplied, do not initialize it but try to get SP immediately.
  302. */
  303. if (pCache == NULL) {
  304. pCache = &spCache;
  305. DGL_SP_CACHE_INITIALIZE_FUNC(pgraph, pCache, nStart);
  306. }
  307. else {
  308. if (ppReport) {
  309. if ((*ppReport =
  310. DGL_SP_CACHE_REPORT_FUNC(pgraph, pCache, nStart,
  311. nDestination)) != NULL) {
  312. return 1;
  313. }
  314. }
  315. else {
  316. if (DGL_SP_CACHE_DISTANCE_FUNC
  317. (pgraph, pCache, pDistance, nStart, nDestination) >= 0) {
  318. return 2;
  319. }
  320. }
  321. if (pgraph->iErrno == DGL_ERR_HeadNodeNotFound) {
  322. DGL_SP_CACHE_RELEASE_FUNC(pgraph, pCache);
  323. DGL_SP_CACHE_INITIALIZE_FUNC(pgraph, pCache, nStart);
  324. }
  325. else if (pgraph->iErrno != DGL_ERR_TailNodeNotFound) {
  326. goto sp_error;
  327. }
  328. /*
  329. * reset visited status for existing cache: fix for BUG1
  330. */
  331. if (pCache->pvVisited) {
  332. avl_destroy(pCache->pvVisited, dglTreeTouchI32Cancel);
  333. if ((pCache->pvVisited =
  334. avl_create(dglTreeTouchI32Compare, NULL,
  335. dglTreeGetAllocator())) == NULL)
  336. return -1;
  337. }
  338. }
  339. /*
  340. * reset error status after using the cache
  341. */
  342. pgraph->iErrno = 0;
  343. if ((pStart = DGL_GET_NODE_FUNC(pgraph, nStart)) == NULL) {
  344. pgraph->iErrno = DGL_ERR_HeadNodeNotFound;
  345. goto sp_error;
  346. }
  347. if ((pDestination = DGL_GET_NODE_FUNC(pgraph, nDestination)) == NULL) {
  348. pgraph->iErrno = DGL_ERR_TailNodeNotFound;
  349. goto sp_error;
  350. }
  351. if ((DGL_NODE_STATUS(pStart) & DGL_NS_ALONE) ||
  352. (DGL_NODE_STATUS(pDestination) & DGL_NS_ALONE)) {
  353. goto sp_error;
  354. }
  355. if (!(DGL_NODE_STATUS(pStart) & DGL_NS_HEAD) && pgraph->Version < 3) {
  356. goto sp_error;
  357. }
  358. if (!(DGL_NODE_STATUS(pDestination) & DGL_NS_TAIL) && pgraph->Version < 3) {
  359. goto sp_error;
  360. }
  361. /*
  362. * now we inspect all edges departing from the start node
  363. * - at each loop 'pedge' points to the edge in the edge buffer
  364. * - we invoke the caller's clip() and eventually skip the edge (clip() != 0)
  365. * - we insert a item in the predist network to set actual predecessor and distance
  366. * (there is no precedecessor at this stage) and actual distance from the starting node
  367. * (at this stage it equals the edge's cost)
  368. * - we insert a item in the node min-heap (sorted on node distance), storing the offset of the
  369. * edge in the edge buffer.
  370. * In the case of undirected graph (version 3) we inspect input edges as well.
  371. */
  372. pEdgeset = _DGL_OUTEDGESET(pgraph, pStart);
  373. if (DGL_EDGESET_T_INITIALIZE_FUNC(pgraph, &laT, pEdgeset) < 0) {
  374. goto sp_error;
  375. }
  376. for (pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT);
  377. pEdge; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT)
  378. ) {
  379. __EDGELOOP_BODY_1(0);
  380. }
  381. DGL_EDGESET_T_RELEASE_FUNC(&laT);
  382. if (pgraph->Version == 3) {
  383. pEdgeset = _DGL_INEDGESET(pgraph, pStart);
  384. if (DGL_EDGESET_T_INITIALIZE_FUNC(pgraph, &laT, pEdgeset) < 0) {
  385. goto sp_error;
  386. }
  387. for (pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT);
  388. pEdge; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT)
  389. ) {
  390. if (DGL_EDGE_STATUS(pEdge) & DGL_ES_DIRECTED)
  391. continue;
  392. __EDGELOOP_BODY_1(1);
  393. }
  394. DGL_EDGESET_T_RELEASE_FUNC(&laT);
  395. }
  396. /*
  397. * Now we begin extracting nodes from the min-heap. Each node extracted is
  398. * the one that is actually closest to the SP start.
  399. */
  400. while (dglHeapExtractMin(&pCache->NodeHeap, &heapnode) == 1) {
  401. dglInt32_t fromDist;
  402. /*
  403. * recover the stored edge pointer
  404. */
  405. pEdge = heapnode.value.pv;
  406. /*
  407. * the new relative head is the tail of the edge
  408. * or the head of the edge if the traversal was reversed (undirected edge)
  409. */
  410. if (heapnode.flags == 0) {
  411. pStart = _DGL_EDGE_TAILNODE(pgraph, pEdge); /* continue from previous tail */
  412. }
  413. else {
  414. pStart = _DGL_EDGE_HEADNODE(pgraph, pEdge); /* reversed head/tail */
  415. }
  416. /*
  417. * We do not want to explore twice the same node as a relative starting point,
  418. * that's the meaning of 'visited'. We mark actual start node as 'visited' by
  419. * inserting it into the visited-network. If we find actual node in the network
  420. * we just give up and continue looping. Otherwise we add actual node to the network.
  421. */
  422. findVisited.nKey = DGL_NODE_ID(pStart);
  423. if ((pVisitedItem =
  424. avl_find(pCache->pvVisited, &findVisited)) == NULL) {
  425. if (dglTreeTouchI32Add(pCache->pvVisited, DGL_NODE_ID(pStart)) ==
  426. NULL) {
  427. pgraph->iErrno = DGL_ERR_MemoryExhausted;
  428. goto sp_error;
  429. }
  430. }
  431. /*
  432. * Dijkstra algorithm ends when the destination node is extracted from
  433. * the min distance heap, that means: no other path exist in the network giving
  434. * a shortest output.
  435. * If this happens we jump to the epilogue in order to build a path report and return.
  436. */
  437. if (DGL_NODE_ID(pStart) == nDestination) {
  438. goto destination_found;
  439. }
  440. /*
  441. * Give up with visited nodes now
  442. */
  443. if (pVisitedItem) {
  444. continue;
  445. }
  446. /*
  447. * If the node is not marked as having departing edges, then we are into a
  448. * blind alley. Just give up this direction and continue looping.
  449. * This only applies to v1 and v2 (digraphs)
  450. */
  451. if (!(DGL_NODE_STATUS(pStart) & DGL_NS_HEAD) && pgraph->Version < 3)
  452. continue;
  453. /*
  454. * save actual edge for later clip()
  455. */
  456. pEdge_prev = pEdge;
  457. /*
  458. * Recover the head node distance from the predist network
  459. */
  460. findPredist.nKey = DGL_NODE_ID(pStart);
  461. if ((pPredistItem =
  462. avl_find(pCache->pvPredist, &findPredist)) == NULL) {
  463. pgraph->iErrno = DGL_ERR_UnexpectedNullPointer;
  464. goto sp_error;
  465. }
  466. fromDist = pPredistItem->nDistance;
  467. /*
  468. * Loop on departing edges:
  469. * Scan the edgeset and loads pedge at each iteration with next-edge.
  470. * iWay == DGL_EDGESET_T_WAY_OUT then pedge is a out arc (departing from node) else ot is a in arc.
  471. * V1 has no in-degree support so iWay is always OUT, V2/3 have in-degree support.
  472. */
  473. pEdgeset = _DGL_OUTEDGESET(pgraph, pStart);
  474. if (DGL_EDGESET_T_INITIALIZE_FUNC(pgraph, &laT, pEdgeset) < 0) {
  475. goto sp_error;
  476. }
  477. for (pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT);
  478. pEdge; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT)
  479. ) {
  480. __EDGELOOP_BODY_2(0);
  481. }
  482. DGL_EDGESET_T_RELEASE_FUNC(&laT);
  483. if (pgraph->Version == 3) {
  484. pEdgeset = _DGL_INEDGESET(pgraph, pStart);
  485. if (DGL_EDGESET_T_INITIALIZE_FUNC(pgraph, &laT, pEdgeset) < 0) {
  486. goto sp_error;
  487. }
  488. for (pEdge = DGL_EDGESET_T_FIRST_FUNC(&laT);
  489. pEdge; pEdge = DGL_EDGESET_T_NEXT_FUNC(&laT)
  490. ) {
  491. if (DGL_EDGE_STATUS(pEdge) & DGL_ES_DIRECTED)
  492. continue;
  493. __EDGELOOP_BODY_2(1);
  494. }
  495. DGL_EDGESET_T_RELEASE_FUNC(&laT);
  496. }
  497. }
  498. sp_error:
  499. if (pCache == &spCache) {
  500. DGL_SP_CACHE_RELEASE_FUNC(pgraph, pCache);
  501. }
  502. return -pgraph->iErrno; /* ==0 path not found */
  503. destination_found: /* path found - build a shortest path report or report the distance only */
  504. if (ppReport) {
  505. *ppReport =
  506. DGL_SP_CACHE_REPORT_FUNC(pgraph, pCache, nStart, nDestination);
  507. if (*ppReport == NULL) {
  508. nRet = -pgraph->iErrno;
  509. }
  510. else {
  511. nRet = 1;
  512. }
  513. }
  514. else {
  515. if (DGL_SP_CACHE_DISTANCE_FUNC
  516. (pgraph, pCache, pDistance, nStart, nDestination) < 0) {
  517. nRet = -pgraph->iErrno;
  518. }
  519. else {
  520. nRet = 2;
  521. }
  522. }
  523. if (pCache == &spCache) {
  524. DGL_SP_CACHE_RELEASE_FUNC(pgraph, pCache);
  525. }
  526. return nRet;
  527. }
  528. #endif