GRASS 8 Programmer's Manual 8.6.0dev(2026)-1878fdfec5
Loading...
Searching...
No Matches
sp-template.c
Go to the documentation of this file.
1/* LIBDGL -- a Directed Graph Library implementation
2 * SPDX-FileCopyrightText: 2002 Roberto Micarelli
3 * SPDX-FileCopyrightText: GRASS Development Team
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7/*
8 * best view with tabstop=4
9 */
10
11/*
12 * SHORTEST PATH CACHE
13 *
14 * components:
15 * - start node id
16 * - visited network: a node is marked as visited when its departing
17 * edges have been added to the cache
18 * - predist network: node distances from start node
19 * - NodeHeap: holds unvisited nodes, the next node extracted is the
20 * unvisited node closest to SP start
21 *
22 * not all nodes in the predist network have been visited, SP from start
23 * is known only for visited nodes
24 * unvisited nodes can be reached, but not necessarily on the shortest
25 * possible path
26 * important for DGL_SP_CACHE_DISTANCE_FUNC and DGL_SP_CACHE_REPORT_FUNC
27 */
28
29#if !defined(DGL_DEFINE_TREE_PROCS) && !defined(DGL_DEFINE_FLAT_PROCS)
30
31#include <grass/gis.h>
32
35{
36 pCache->nStartNode = nStart;
37 pCache->pvVisited = NULL;
38 pCache->pvPredist = NULL;
39 dglHeapInit(&pCache->NodeHeap);
42 return -1;
45 return -1;
46 return 0;
47}
48
51{
52 if (pCache->pvVisited)
54 if (pCache->pvPredist)
56 dglHeapFree(&pCache->NodeHeap, NULL);
57}
58
62{
65
66 if (pCache->nStartNode != nStart) {
68 return -pgraph->iErrno;
69 }
70
72 if (avl_find(pCache->pvVisited, &VisitedItem) == NULL) {
74 return -pgraph->iErrno;
75 }
76
78 if ((pPredistItem = avl_find(pCache->pvPredist, &PredistItem)) == NULL) {
80 return -pgraph->iErrno;
81 }
82
83 if (pnDistance)
84 *pnDistance = pPredistItem->nDistance;
85 return 0;
86}
87
92{
97 dglSPArc_s arc;
98 long i, istack = 0;
99 unsigned char *pstack = NULL;
100 unsigned char *ppop;
102
103 if (pCache->nStartNode != nStart) {
105 return NULL;
106 }
107
109 if (avl_find(pCache->pvVisited, &VisitedItem) == NULL) {
111 return NULL;
112 }
113
115 if (avl_find(pCache->pvPredist, &PredistItem) == NULL) {
117 return NULL;
118 }
119
120 for (PredistItem.nKey = nDestination,
121 pPredistItem = avl_find(pCache->pvPredist, &PredistItem);
123 pPredistItem = avl_find(pCache->pvPredist, &PredistItem)) {
124 if (pPredistItem->nFrom < 0) {
125 pgraph->iErrno = DGL_ERR_BadEdge;
126 goto spr_error;
127 }
128
129 pEdge = (dglInt32_t *)pPredistItem->pnEdge;
130
131 if (pPredistItem->bFlags == 0) {
132 if (pgraph->Flags & DGL_GS_FLAT) {
135 }
136 else {
139 }
140 }
141 else {
142 if (pgraph->Flags & DGL_GS_FLAT) {
145 }
146 else {
149 }
150 }
151
152 if ((arc.pnEdge = DGL_EDGE_ALLOC(pgraph->EdgeAttrSize)) == NULL)
153 goto spr_error;
154 arc.nFrom = pPredistItem->nFrom;
156 arc.nDistance = pPredistItem->nDistance;
157 memcpy(arc.pnEdge, pEdge, DGL_EDGE_SIZEOF(pgraph->EdgeAttrSize));
158 DGL_EDGE_COST(arc.pnEdge) = pPredistItem->nCost;
159
160 if ((pstack = dgl_mempush(pstack, &istack, sizeof(dglSPArc_s), &arc)) ==
161 NULL) {
163 goto spr_error;
164 }
165
166 if (arc.nFrom == nStart)
167 break;
168 }
169
170 if (pPredistItem == NULL) {
172 goto spr_error;
173 }
174
175 if ((pReport = malloc(sizeof(dglSPReport_s))) == NULL) {
177 goto spr_error;
178 }
179 memset(pReport, 0, sizeof(dglSPReport_s));
180
181 pReport->cArc = istack;
182
183 if ((pReport->pArc = malloc(sizeof(dglSPArc_s) * pReport->cArc)) == NULL) {
185 goto spr_error;
186 }
187
188 pReport->nDistance = 0;
189
190 for (i = 0;
191 (ppop = dgl_mempop(pstack, &istack, sizeof(dglSPArc_s))) != NULL;
192 i++) {
193 memcpy(&pReport->pArc[i], ppop, sizeof(dglSPArc_s));
194 pReport->nDistance += DGL_EDGE_COST(pReport->pArc[i].pnEdge);
195 }
196
197 pReport->nStartNode = nStart;
198 pReport->nDestinationNode = nDestination;
199
200 if (pstack)
201 free(pstack);
202
203 return pReport;
204
206 if (pstack)
207 free(pstack);
208 if (pReport)
210
211 return NULL;
212}
213#endif
214
215#if defined(DGL_DEFINE_TREE_PROCS) || defined(DGL_DEFINE_FLAT_PROCS)
216
217#define __EDGELOOP_BODY_1(f) \
218 if ((f) == 0) { \
219 pDestination = _DGL_EDGE_TAILNODE(pgraph, pEdge); \
220 } \
221 else { \
222 pDestination = _DGL_EDGE_HEADNODE(pgraph, pEdge); \
223 } \
224 if (!(DGL_NODE_STATUS(pDestination) & DGL_NS_TAIL) && \
225 pgraph->Version < 3) { \
226 pgraph->iErrno = DGL_ERR_BadEdge; \
227 goto sp_error; \
228 } \
229 clipOutput.nEdgeCost = DGL_EDGE_COST(pEdge); \
230 if (fnClip) { \
231 clipInput.pnPrevEdge = NULL; \
232 clipInput.pnNodeFrom = pStart; \
233 clipInput.pnEdge = pEdge; \
234 clipInput.pnNodeTo = pDestination; \
235 clipInput.nFromDistance = 0; \
236 if (fnClip(pgraph, &clipInput, &clipOutput, pvClipArg)) \
237 continue; \
238 } \
239 findPredist.nKey = DGL_NODE_ID(pDestination); \
240 if ((pPredistItem = avl_find(pCache->pvPredist, &findPredist)) == NULL) { \
241 if ((pPredistItem = dglTreePredistAdd( \
242 pCache->pvPredist, DGL_NODE_ID(pDestination))) == NULL) { \
243 pgraph->iErrno = DGL_ERR_MemoryExhausted; \
244 goto sp_error; \
245 } \
246 } \
247 else { \
248 if (pPredistItem->nDistance <= clipOutput.nEdgeCost) { \
249 continue; \
250 } \
251 } \
252 pPredistItem->nFrom = nStart; \
253 pPredistItem->pnEdge = pEdge; \
254 pPredistItem->nCost = clipOutput.nEdgeCost; \
255 pPredistItem->nDistance = clipOutput.nEdgeCost; \
256 pPredistItem->bFlags = (f); \
257 heapvalue.pv = pEdge; \
258 if (dglHeapInsertMin(&pCache->NodeHeap, pPredistItem->nDistance, f, \
259 heapvalue) < 0) { \
260 pgraph->iErrno = DGL_ERR_HeapError; \
261 goto sp_error; \
262 }
263
264#define __EDGELOOP_BODY_2(f) \
265 if ((f) == 0) { \
266 pDestination = _DGL_EDGE_TAILNODE(pgraph, pEdge); \
267 } \
268 else if (pgraph->Version == 3) { \
269 pDestination = _DGL_EDGE_HEADNODE(pgraph, pEdge); \
270 } \
271 if (!(DGL_NODE_STATUS(pDestination) & DGL_NS_TAIL) && \
272 pgraph->Version < 3) { \
273 pgraph->iErrno = DGL_ERR_BadEdge; \
274 goto sp_error; \
275 } \
276 clipOutput.nEdgeCost = DGL_EDGE_COST(pEdge); \
277 if (fnClip) { \
278 clipInput.pnPrevEdge = pEdge_prev; \
279 clipInput.pnNodeFrom = pStart; \
280 clipInput.pnEdge = pEdge; \
281 clipInput.pnNodeTo = pDestination; \
282 clipInput.nFromDistance = fromDist; \
283 if (fnClip(pgraph, &clipInput, &clipOutput, pvClipArg)) \
284 continue; \
285 } \
286 findPredist.nKey = DGL_NODE_ID(pDestination); \
287 if ((pPredistItem = avl_find(pCache->pvPredist, &findPredist)) == NULL) { \
288 if ((pPredistItem = dglTreePredistAdd( \
289 pCache->pvPredist, DGL_NODE_ID(pDestination))) == NULL) { \
290 pgraph->iErrno = DGL_ERR_MemoryExhausted; \
291 goto sp_error; \
292 } \
293 } \
294 else { \
295 if (pPredistItem->nDistance <= fromDist + clipOutput.nEdgeCost) { \
296 continue; \
297 } \
298 } \
299 pPredistItem->nFrom = DGL_NODE_ID(pStart); \
300 pPredistItem->pnEdge = pEdge; \
301 pPredistItem->nCost = clipOutput.nEdgeCost; \
302 pPredistItem->nDistance = fromDist + clipOutput.nEdgeCost; \
303 pPredistItem->bFlags = (f); \
304 heapvalue.pv = pEdge; \
305 if (dglHeapInsertMin(&pCache->NodeHeap, pPredistItem->nDistance, f, \
306 heapvalue) < 0) { \
307 pgraph->iErrno = DGL_ERR_HeapError; \
308 goto sp_error; \
309 }
310
311/*
312 * Dijkstra Shortest Path
313 */
318{
319 dglInt32_t *pStart; /* pointer to the start node (pgraph->pNodeBuffer) */
320 register dglInt32_t *pDestination; /* temporary destination pointer */
321 register dglInt32_t
322 *pEdgeset; /* pointer to the edge (pgraph->pEdgeBuffer) */
323 register dglInt32_t *pEdge; /* pointer to the to-edges in edge */
324 register dglInt32_t *pEdge_prev; /* pointer to the previous edge in path */
325 int nRet;
327
328 dglSPCache_s spCache;
329 int new_cache = 0;
330
331 /*
332 * shortest path distance temporary min heap
333 */
336
337 /*
338 * shortest path visited network
339 */
341
342 /*
343 * shortest path predecessor and distance network
344 */
346
347 /*
348 * args to clip()
349 */
352
353 /*
354 * Initialize the cache: initialize the heap and create temporary networks -
355 * The use of a predist network for predecessor and distance has two
356 * important results: 1) allows us not having to reset the whole graph
357 * status at each call; 2) use of a stack memory area for temporary (and
358 * otherwise possibly thread-conflicting) states. If a cache pointer was
359 * supplied, do not initialize it but try to get SP immediately.
360 */
361 if (pCache == NULL) {
362 pCache = &spCache;
364 new_cache = 1;
365 }
366 else {
367 if (ppReport) {
369 nDestination)) != NULL) {
370 return 1;
371 }
372 }
373 else {
375 nDestination) >= 0) {
376 return 2;
377 }
378 }
379 if (pgraph->iErrno == DGL_ERR_HeadNodeNotFound) {
382 new_cache = 1;
383 }
384 else if (pgraph->iErrno != DGL_ERR_TailNodeNotFound) {
385 goto sp_error;
386 }
387 }
388
389 /*
390 * reset error status after using the cache
391 */
392 pgraph->iErrno = 0;
393
396 goto sp_error;
397 }
398
401 goto sp_error;
402 }
403
406 goto sp_error;
407 }
408
409 if (!(DGL_NODE_STATUS(pStart) & DGL_NS_HEAD) && pgraph->Version < 3) {
410 goto sp_error;
411 }
412
413 if (!(DGL_NODE_STATUS(pDestination) & DGL_NS_TAIL) && pgraph->Version < 3) {
414 goto sp_error;
415 }
416
417 /* if we do not need a new cache, we just continue with the unvisited
418 * nodes in the cache */
419 if (new_cache) {
420 /*
421 * now we inspect all edges departing from the start node
422 * - at each loop 'pedge' points to the edge in the edge buffer
423 * - we invoke the caller's clip() and eventually skip the edge (clip()
424 * != 0)
425 * - we insert a item in the predist network to set actual predecessor
426 * and distance (there is no precedecessor at this stage) and actual
427 * distance from the starting node (at this stage it equals the edge's
428 * cost)
429 * - we insert a item in the node min-heap (sorted on node distance),
430 * storing the offset of the edge in the edge buffer. In the case of
431 * undirected graph (version 3) we inspect input edges as well.
432 */
435 goto sp_error;
436 }
440 }
442
443 if (pgraph->Version == 3) {
446 goto sp_error;
447 }
451 continue;
453 }
455 }
456 }
457
458 /*
459 * Now we begin extracting nodes from the min-heap. Each node extracted is
460 * the one that is actually closest to the SP start.
461 */
462 while (dglHeapExtractMin(&pCache->NodeHeap, &heapnode) == 1) {
464
465 /*
466 * recover the stored edge pointer
467 */
468 pEdge = heapnode.value.pv;
469
470 /*
471 * the new relative head is the tail of the edge
472 * or the head of the edge if the traversal was reversed (undirected
473 * edge)
474 */
475 if (heapnode.flags == 0) {
477 pgraph, pEdge); /* continue from previous tail */
478 }
479 else {
480 pStart = _DGL_EDGE_HEADNODE(pgraph, pEdge); /* reversed head/tail */
481 }
482
483 /*
484 * We do not want to explore twice the same node as a relative starting
485 * point, that's the meaning of 'visited'. We mark actual start node as
486 * 'visited' by inserting it into the visited-network. If we find actual
487 * node in the network we just give up and continue looping. Otherwise
488 * we add actual node to the network.
489 */
491 if ((pVisitedItem = avl_find(pCache->pvVisited, &findVisited)) ==
492 NULL) {
493 if (dglTreeTouchI32Add(pCache->pvVisited, DGL_NODE_ID(pStart)) ==
494 NULL) {
496 goto sp_error;
497 }
498 }
499
500 /*
501 * Give up with visited nodes now
502 */
503 if (pVisitedItem) {
505 /* should not happen but does not harm
506 * this case should have been handled above */
508 }
509 else
510 continue;
511 }
512
513 /*
514 * If the node is not marked as having departing edges, then we are into
515 * a blind alley. Just give up this direction and continue looping. This
516 * only applies to v1 and v2 (digraphs)
517 */
518 if (!(DGL_NODE_STATUS(pStart) & DGL_NS_HEAD) && pgraph->Version < 3) {
521 }
522 else
523 continue;
524 }
525
526 /*
527 * save actual edge for later clip()
528 */
530
531 /*
532 * Recover the head node distance from the predist network
533 */
535 if ((pPredistItem = avl_find(pCache->pvPredist, &findPredist)) ==
536 NULL) {
538 goto sp_error;
539 }
540
541 fromDist = pPredistItem->nDistance;
542
543 /*
544 * Loop on departing edges:
545 * Scan the edgeset and loads pedge at each iteration with next-edge.
546 * iWay == DGL_EDGESET_T_WAY_OUT then pedge is an out arc (departing
547 * from node) else it is an in arc. V1 has no in-degree support so iWay
548 * is always OUT, V2/3 have in-degree support.
549 *
550 * This loop needs to be done also when destination is found, otherwise
551 * the node is marked as visited but its departing edges are not added
552 * to the cache
553 * --> loose end, we might need these edges later on
554 */
557 goto sp_error;
558 }
562 }
564
565 if (pgraph->Version == 3) {
568 goto sp_error;
569 }
573 continue;
575 }
577 }
578
579 /*
580 * Dijkstra algorithm ends when the destination node is extracted from
581 * the min distance heap, that means: no other path exist in the network
582 * giving a shortest output. If this happens we jump to the epilogue in
583 * order to build a path report and return.
584 */
587 }
588 }
589
591 if (pCache == &spCache) {
593 }
594 return -pgraph->iErrno; /* == 0 path not found */
595
596destination_found: /* path found - build a shortest path report or report the
597 distance only */
598
599 if (ppReport) {
600 *ppReport =
602 if (*ppReport == NULL) {
603 nRet = -pgraph->iErrno;
604 }
605 else {
606 nRet = 1;
607 }
608 }
609 else {
611 nDestination) < 0) {
612 nRet = -pgraph->iErrno;
613 }
614 else {
615 nRet = 2;
616 }
617 }
618 if (pCache == &spCache) {
620 }
621 return nRet;
622}
623
624#endif
#define NULL
Definition ccmath.h:32
#define G_UNUSED
A macro for an attribute, if attached to a variable, indicating that the variable is not used.
Definition gis.h:43
int(* dglSPClip_fn)(dglGraph_s *, dglSPClipInput_s *, dglSPClipOutput_s *, void *)
Definition graph.h:167
#define DGL_NS_TAIL
Definition graph.h:48
#define DGL_NS_ALONE
Definition graph.h:49
#define DGL_ERR_MemoryExhausted
Definition graph.h:242
#define DGL_NS_HEAD
Definition graph.h:47
#define DGL_GS_FLAT
Definition graph.h:24
#define DGL_ERR_HeadNodeNotFound
Definition graph.h:249
#define DGL_ERR_UnexpectedNullPointer
Definition graph.h:256
#define DGL_ERR_BadEdge
Definition graph.h:251
#define DGL_ERR_TailNodeNotFound
Definition graph.h:250
#define DGL_ES_DIRECTED
Definition graph.h:54
void dglHeapInit(dglHeap_s *pheap)
Definition heap.c:15
void dglHeapFree(dglHeap_s *pheap, dglHeapCancelItem_fn pfnCancelItem)
Definition heap.c:23
int dglHeapExtractMin(dglHeap_s *pheap, dglHeapNode_s *pnoderet)
Definition heap.c:64
unsigned char * dgl_mempop(unsigned char *pstack, long *istack, long size)
Definition helpers.c:35
unsigned char * dgl_mempush(unsigned char *pstack, long *istack, long size, void *pv)
Definition helpers.c:22
void * malloc(unsigned)
void free(void *)
dglInt32_t nDistance
Definition graph.h:184
dglInt32_t nTo
Definition graph.h:182
dglInt32_t * pnEdge
Definition graph.h:183
dglInt32_t nFrom
Definition graph.h:181
void dglTreePredistCancel(void *pvPredist, void *pvParam)
Definition tree.c:240
int dglTreeTouchI32Compare(const void *pvTouchI32A, const void *pvTouchI32B, void *pvParam)
Definition tree.c:195
int dglTreePredistCompare(const void *pvPredistA, const void *pvPredistB, void *pvParam)
Definition tree.c:245
void * dglTreeGetAllocator(void)
Definition tree.c:394
dglTreeTouchI32_s * dglTreeTouchI32Add(void *pavl, dglInt32_t nKey)
Definition tree.c:208
void dglTreeTouchI32Cancel(void *pvTouchI32, void *pvParam)
Definition tree.c:190
#define avl_find
Definition tree.h:26
#define avl_create
Definition tree.h:19
#define avl_destroy
Definition tree.h:21
long dglInt32_t
Definition type.h:24
#define DGL_SP_CACHE_REPORT_FUNC
Definition v1-defs.h:74
#define DGL_EDGESET_T_FIRST_FUNC
Definition v1-defs.h:100
#define DGL_SP_CACHE_INITIALIZE_FUNC
Definition v1-defs.h:72
#define DGL_NODE_STATUS
Definition v1-defs.h:114
#define DGL_SP_CACHE_RELEASE_FUNC
Definition v1-defs.h:73
#define DGL_EDGE_SIZEOF
Definition v1-defs.h:122
#define DGL_NODE_ID
Definition v1-defs.h:115
#define DGL_EDGE_ALLOC
Definition v1-defs.h:121
#define DGL_EDGE_TAILNODE_OFFSET
Definition v1-defs.h:129
#define DGL_GET_NODE_FUNC
Definition v1-defs.h:80
#define DGL_EDGE_STATUS(p)
Definition v1-defs.h:124
#define DGL_EDGESET_T_RELEASE_FUNC
Definition v1-defs.h:99
#define DGL_EDGESET_T_INITIALIZE_FUNC
Definition v1-defs.h:98
#define DGL_SP_CACHE_DISTANCE_FUNC
Definition v1-defs.h:75
#define DGL_EDGE_HEADNODE_OFFSET
Definition v1-defs.h:128
#define DGL_EDGESET_T_NEXT_FUNC
Definition v1-defs.h:101
#define DGL_EDGE_COST
Definition v1-defs.h:125
#define DGL_NODEBUFFER_SHIFT
Definition v1-defs.h:145
void dglFreeSPReport(dglGraph_s *pgraph, dglSPReport_s *pSPReport)