sp-template.c 18 KB

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