Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
test_dijkstra.cc
Go to the documentation of this file.
1
13#include <gtest/gtest.h>
14
15#include <Dijkstra.H>
16#include <tpl_graph.H>
17
18using namespace Aleph;
19
20// Helper iterator for multigraphs that visits ALL arcs including parallel ones
21// and provides the get_current_arc_ne() method required by Dijkstra
22template <class GT, class SA = Dft_Show_Arc<GT>>
23class Out_Iterator_AllArcs : public Out_Iterator<GT, SA>
24{
25public:
27 using Base::Base;
28
30 {
31 return const_cast<Out_Iterator_AllArcs *>(this)->get_current_arc();
32 }
33};
34
35// Graph type for tests - uses double weights for distances
37using Node = GT::Node;
38using Arc = GT::Arc;
39
40// Digraph type for directed graph tests
43using DArc = DGT::Arc;
44
45struct BinHeapTag
46{
47 template <class G, class D, class A>
49};
50
51struct FibHeapTag
52{
53 template <class G, class D, class A>
55};
56
57template <typename HeapTag, typename Graph>
60 Dft_Show_Arc<Graph>, HeapTag::template Heap>;
61
62// Dijkstra with Out_Iterator_AllArcs for multigraphs (visits ALL arcs, including parallel)
63template <typename HeapTag, typename Graph>
66 Dft_Show_Arc<Graph>, HeapTag::template Heap>;
67
68template <typename HeapTag>
70
71template <typename HeapTag>
73
74template <typename HeapTag>
76
77template <typename HeapTag>
78class DijkstraHeapTest : public ::testing::Test {};
79
80template <typename HeapTag>
81class DijkstraDigraphHeapTest : public ::testing::Test {};
82
83using HeapTypes = ::testing::Types<BinHeapTag, FibHeapTag>;
86
87// ========== TEST 1: Basic Shortest Path ==========
89 GT g;
90 Node* n0 = g.insert_node(0);
91 Node* n1 = g.insert_node(1);
92 Node* n2 = g.insert_node(2);
93 Node* n3 = g.insert_node(3);
94 Node* n4 = g.insert_node(4);
95
96 g.insert_arc(n0, n1, 10.0);
97 g.insert_arc(n0, n2, 5.0);
98 g.insert_arc(n1, n2, 2.0);
99 g.insert_arc(n1, n3, 1.0);
100 g.insert_arc(n2, n1, 3.0);
101 g.insert_arc(n2, n3, 9.0);
102 g.insert_arc(n2, n4, 2.0);
103 g.insert_arc(n3, n4, 4.0);
104 g.insert_arc(n4, n0, 7.0);
105 g.insert_arc(n4, n3, 6.0);
106
108 Path<GT> path(g);
109 double d = dij(g, n0, n3, path);
110
111 // Path 0->2->1->3 costs 5+2+1=8 (undirected graph)
112 ASSERT_EQ(d, 8.0);
113
114 Path<GT>::Iterator it(path);
115 ASSERT_TRUE(it.has_curr());
116 EXPECT_EQ(it.get_current_node()->get_info(), 0);
117 it.next();
118 ASSERT_TRUE(it.has_curr());
119 EXPECT_EQ(it.get_current_node()->get_info(), 2);
120 it.next();
121 ASSERT_TRUE(it.has_curr());
122 EXPECT_EQ(it.get_current_node()->get_info(), 1);
123 it.next();
124 ASSERT_TRUE(it.has_curr());
125 EXPECT_EQ(it.get_current_node()->get_info(), 3);
126 it.next();
128}
129
130// ========== TEST 2: Path to Self ==========
132 GT g;
133 Node* n0 = g.insert_node(0);
134
136 Path<GT> path(g);
137 double d = dij(g, n0, n0, path);
138
139 // When start == end and no self-loop exists, Dijkstra returns max distance
140 // and empty path (there's no actual "path" from a node to itself)
141 EXPECT_EQ(d, std::numeric_limits<double>::max());
142 EXPECT_TRUE(path.is_empty());
143}
144
145// ========== TEST 3: No Path Exists ==========
147 GT g;
148 Node* n0 = g.insert_node(0);
149 Node* n1 = g.insert_node(1);
150
151 // No arcs connecting the nodes
152
154 Path<GT> path(g);
155 double d = dij(g, n0, n1, path);
156
157 EXPECT_EQ(d, std::numeric_limits<double>::max());
158 EXPECT_TRUE(path.is_empty());
159}
160
161// ========== TEST 4: Compute Spanning Tree ==========
163 GT g;
164 Node* n0 = g.insert_node(0);
165 Node* n1 = g.insert_node(1);
166 Node* n2 = g.insert_node(2);
167 Node* n3 = g.insert_node(3);
168 Node* n4 = g.insert_node(4);
169
170 g.insert_arc(n0, n1, 10.0);
171 g.insert_arc(n0, n2, 5.0);
172 g.insert_arc(n1, n2, 2.0);
173 g.insert_arc(n1, n3, 1.0);
174 g.insert_arc(n2, n1, 3.0);
175 g.insert_arc(n2, n3, 9.0);
176 g.insert_arc(n2, n4, 2.0);
177 g.insert_arc(n3, n4, 4.0);
178 g.insert_arc(n4, n0, 7.0);
179 g.insert_arc(n4, n3, 6.0);
180
182 GT tree;
183 dij(g, n0, tree);
184
185 EXPECT_EQ(tree.get_num_nodes(), 5);
186 EXPECT_EQ(tree.get_num_arcs(), 4); // n-1 arcs in spanning tree
187}
188
189// ========== TEST 5: Update Path in Heap (Relaxation) ==========
191 GT g;
192 Node* n0 = g.insert_node(0);
193 Node* n1 = g.insert_node(1);
194 Node* n2 = g.insert_node(2);
195
196 // Direct path 0->1 costs 10, but 0->2->1 costs 2
197 g.insert_arc(n0, n1, 10.0);
198 g.insert_arc(n0, n2, 1.0);
199 g.insert_arc(n2, n1, 1.0);
200
202 Path<GT> path(g);
203 double d = dij(g, n0, n1, path);
204
205 ASSERT_EQ(d, 2.0); // Should find the cheaper path through n2
206
207 Path<GT>::Iterator it(path);
208 ASSERT_TRUE(it.has_curr());
209 EXPECT_EQ(it.get_current_node()->get_info(), 0);
210 it.next();
211 ASSERT_TRUE(it.has_curr());
212 EXPECT_EQ(it.get_current_node()->get_info(), 2);
213 it.next();
214 ASSERT_TRUE(it.has_curr());
215 EXPECT_EQ(it.get_current_node()->get_info(), 1);
216 it.next();
218}
219
220// ========== TEST 6: Paint Spanning Tree ==========
222 GT g;
223 Node* n0 = g.insert_node(0);
224 Node* n1 = g.insert_node(1);
225 Node* n2 = g.insert_node(2);
226 Node* n3 = g.insert_node(3);
227
228 g.insert_arc(n0, n1, 1.0);
229 g.insert_arc(n0, n2, 4.0);
230 g.insert_arc(n1, n2, 2.0);
231 g.insert_arc(n1, n3, 5.0);
232 g.insert_arc(n2, n3, 1.0);
233
235 dij.paint_min_paths_tree(g, n0);
236
237 // After painting, we should be able to get min path to any node
238 Path<GT> path(g);
239 dij.get_min_path(n3, path);
240
241 // Verify path: 0 -> 1 -> 2 -> 3 (cost 4)
242 ASSERT_FALSE(path.is_empty());
243}
244
245// ========== TEST 7: Copy Painted Min Paths Tree ==========
247 GT g;
248 Node* n0 = g.insert_node(0);
249 Node* n1 = g.insert_node(1);
250 Node* n2 = g.insert_node(2);
251 Node* n3 = g.insert_node(3);
252
253 g.insert_arc(n0, n1, 1.0);
254 g.insert_arc(n0, n2, 4.0);
255 g.insert_arc(n1, n2, 2.0);
256 g.insert_arc(n1, n3, 5.0);
257 g.insert_arc(n2, n3, 1.0);
258
260 dij.paint_min_paths_tree(g, n0);
261
262 GT tree;
263 dij.copy_painted_min_paths_tree(g, tree);
264
265 EXPECT_EQ(tree.get_num_nodes(), 4);
266 EXPECT_EQ(tree.get_num_arcs(), 3); // n-1 arcs in tree
267}
268
269// ========== TEST 8: Single Node Graph ==========
271 GT g;
272 Node* n0 = g.insert_node(0);
273
275 GT tree;
276 dij(g, n0, tree);
277
278 EXPECT_EQ(tree.get_num_nodes(), 1);
279 EXPECT_EQ(tree.get_num_arcs(), 0);
280}
281
282// ========== TEST 9: Linear Chain Graph ==========
284 GT g;
285 Node* n0 = g.insert_node(0);
286 Node* n1 = g.insert_node(1);
287 Node* n2 = g.insert_node(2);
288 Node* n3 = g.insert_node(3);
289
290 g.insert_arc(n0, n1, 1.0);
291 g.insert_arc(n1, n2, 1.0);
292 g.insert_arc(n2, n3, 1.0);
293
295 Path<GT> path(g);
296 double d = dij(g, n0, n3, path);
297
298 EXPECT_EQ(d, 3.0);
299}
300
301// ========== TEST 10: Complete Graph K4 ==========
303 GT g;
304 Node* n0 = g.insert_node(0);
305 Node* n1 = g.insert_node(1);
306 Node* n2 = g.insert_node(2);
307 Node* n3 = g.insert_node(3);
308
309 // All edges with weight 1
310 g.insert_arc(n0, n1, 1.0);
311 g.insert_arc(n0, n2, 1.0);
312 g.insert_arc(n0, n3, 1.0);
313 g.insert_arc(n1, n2, 1.0);
314 g.insert_arc(n1, n3, 1.0);
315 g.insert_arc(n2, n3, 1.0);
316
318 Path<GT> path(g);
319 double d = dij(g, n0, n3, path);
320
321 EXPECT_EQ(d, 1.0); // Direct path
322}
323
324// ========== TEST 11: Graph with Self Loop ==========
326 GT g;
327 Node* n0 = g.insert_node(0);
328 Node* n1 = g.insert_node(1);
329
330 g.insert_arc(n0, n0, 5.0); // Self loop
331 g.insert_arc(n0, n1, 2.0);
332
334 Path<GT> path(g);
335 double d = dij(g, n0, n1, path);
336
337 EXPECT_EQ(d, 2.0); // Self loop should be ignored
338}
339
340// ========== TEST 12: Digraph Basic Path ==========
342 DGT g;
343 DNode* n0 = g.insert_node(0);
344 DNode* n1 = g.insert_node(1);
345 DNode* n2 = g.insert_node(2);
346
347 g.insert_arc(n0, n1, 1.0);
348 g.insert_arc(n1, n2, 1.0);
349 // Note: no arc n2->n0, so no path back
350
352 Path<DGT> path(g);
353 double d = dij(g, n0, n2, path);
354
355 EXPECT_EQ(d, 2.0);
356}
357
358// ========== TEST 13: Digraph No Return Path ==========
360 DGT g;
361 DNode* n0 = g.insert_node(0);
362 DNode* n1 = g.insert_node(1);
363
364 g.insert_arc(n0, n1, 1.0); // Only forward direction
365
367
369 double d1 = dij(g, n0, n1, path_forward);
370 EXPECT_EQ(d1, 1.0);
371
373 double d2 = dij(g, n1, n0, path_backward);
374 EXPECT_EQ(d2, std::numeric_limits<double>::max());
375}
376
377// ========== TEST 14: Empty Graph ==========
379 GT g;
380
382 GT tree;
383
384 // Should handle empty graph gracefully - tree stays empty
385 // Note: calling dij on empty graph would need a null node, so we just check tree
386 EXPECT_EQ(tree.get_num_nodes(), 0);
387 EXPECT_EQ(tree.get_num_arcs(), 0);
388}
389
390// ========== TEST 15: Multiple Paths Same Cost ==========
392 GT g;
393 Node* n0 = g.insert_node(0);
394 Node* n1 = g.insert_node(1);
395 Node* n2 = g.insert_node(2);
396 Node* n3 = g.insert_node(3);
397
398 // Two paths from n0 to n3 with same cost 2
399 // Path 1: n0 -> n1 -> n3 (cost 2)
400 // Path 2: n0 -> n2 -> n3 (cost 2)
401 g.insert_arc(n0, n1, 1.0);
402 g.insert_arc(n0, n2, 1.0);
403 g.insert_arc(n1, n3, 1.0);
404 g.insert_arc(n2, n3, 1.0);
405
407 Path<GT> path(g);
408 double d = dij(g, n0, n3, path);
409
410 EXPECT_EQ(d, 2.0);
411 ASSERT_FALSE(path.is_empty());
412}
413
414// ========== TEST 16: Large Weights ==========
416 GT g;
417 Node* n0 = g.insert_node(0);
418 Node* n1 = g.insert_node(1);
419 Node* n2 = g.insert_node(2);
420
421 g.insert_arc(n0, n1, 1e10);
422 g.insert_arc(n1, n2, 1e10);
423
425 Path<GT> path(g);
426 double d = dij(g, n0, n2, path);
427
428 EXPECT_EQ(d, 2e10);
429}
430
431// ========== TEST 17: Zero Weight Edges ==========
433 GT g;
434 Node* n0 = g.insert_node(0);
435 Node* n1 = g.insert_node(1);
436 Node* n2 = g.insert_node(2);
437
438 g.insert_arc(n0, n1, 0.0);
439 g.insert_arc(n1, n2, 0.0);
440
442 Path<GT> path(g);
443 double d = dij(g, n0, n2, path);
444
445 EXPECT_EQ(d, 0.0);
446}
447
448// ========== TEST 18: Fractional Weights ==========
450 GT g;
451 Node* n0 = g.insert_node(0);
452 Node* n1 = g.insert_node(1);
453 Node* n2 = g.insert_node(2);
454
455 g.insert_arc(n0, n1, 0.5);
456 g.insert_arc(n1, n2, 0.3);
457
459 Path<GT> path(g);
460 double d = dij(g, n0, n2, path);
461
462 EXPECT_NEAR(d, 0.8, 1e-9);
463}
464
465// ========== TEST 19: Star Graph ==========
467 GT g;
468 Node* center = g.insert_node(0);
469 Node* n1 = g.insert_node(1);
470 Node* n2 = g.insert_node(2);
471 Node* n3 = g.insert_node(3);
472 Node* n4 = g.insert_node(4);
473
474 g.insert_arc(center, n1, 1.0);
475 g.insert_arc(center, n2, 2.0);
476 g.insert_arc(center, n3, 3.0);
477 g.insert_arc(center, n4, 4.0);
478
480 GT tree;
481 dij(g, center, tree);
482
483 EXPECT_EQ(tree.get_num_nodes(), 5);
484 EXPECT_EQ(tree.get_num_arcs(), 4);
485}
486
487// ========== TEST 20: Verify Path Correctness ==========
489 GT g;
490 Node* n0 = g.insert_node(0);
491 Node* n1 = g.insert_node(1);
492 Node* n2 = g.insert_node(2);
493 Node* n3 = g.insert_node(3);
494
495 g.insert_arc(n0, n1, 1.0);
496 g.insert_arc(n1, n2, 2.0);
497 g.insert_arc(n2, n3, 3.0);
498 g.insert_arc(n0, n3, 100.0); // Much longer direct path
499
501 Path<GT> path(g);
502 double d = dij(g, n0, n3, path);
503
504 EXPECT_EQ(d, 6.0);
505
506 // Collect all nodes in path
508 for (Path<GT>::Iterator it(path); it.has_curr(); it.next())
509 path_nodes.append(it.get_current_node()->get_info());
510
512
513 auto pit = path_nodes.get_it();
514 EXPECT_EQ(pit.get_curr(), 0); pit.next();
515 EXPECT_EQ(pit.get_curr(), 1); pit.next();
516 EXPECT_EQ(pit.get_curr(), 2); pit.next();
517 EXPECT_EQ(pit.get_curr(), 3);
518}
519
520// ========== TEST 21: State Getters ==========
522 GT g;
523 Node* n0 = g.insert_node(0);
524 Node* n1 = g.insert_node(1);
525 g.insert_arc(n0, n1, 1.0);
526
528
529 // Before any computation
530 EXPECT_FALSE(dij.has_computation());
531 EXPECT_FALSE(dij.is_painted());
532 EXPECT_EQ(dij.get_start_node(), nullptr);
533 EXPECT_EQ(dij.get_graph(), nullptr);
534
535 // After painting
536 dij.paint_min_paths_tree(g, n0);
537
538 EXPECT_TRUE(dij.has_computation());
539 EXPECT_TRUE(dij.is_painted());
540 EXPECT_EQ(dij.get_start_node(), n0);
541 EXPECT_EQ(dij.get_graph(), &g);
542}
543
544// ========== TEST 22: Compute Partial Min Paths Tree ==========
546 GT g;
547 Node* n0 = g.insert_node(0);
548 Node* n1 = g.insert_node(1);
549 Node* n2 = g.insert_node(2);
550 Node* n3 = g.insert_node(3);
551 Node* n4 = g.insert_node(4);
552
553 g.insert_arc(n0, n1, 1.0);
554 g.insert_arc(n1, n2, 1.0);
555 g.insert_arc(n2, n3, 1.0);
556 g.insert_arc(n3, n4, 1.0); // n4 should NOT be in partial tree
557
559 GT tree;
560 dij.compute_partial_min_paths_tree(g, n0, n3, tree);
561
562 // Tree should contain n0, n1, n2, n3 but NOT n4
563 EXPECT_EQ(tree.get_num_nodes(), 4);
564 EXPECT_EQ(tree.get_num_arcs(), 3);
565}
566
567// ========== TEST 23: Get Min Path from Tree ==========
569 GT g;
570 Node* n0 = g.insert_node(0);
571 Node* n1 = g.insert_node(1);
572 Node* n2 = g.insert_node(2);
573 Node* n3 = g.insert_node(3);
574
575 g.insert_arc(n0, n1, 1.0);
576 g.insert_arc(n1, n2, 2.0);
577 g.insert_arc(n2, n3, 3.0);
578
580 GT tree;
581 auto tree_start = dij.compute_min_paths_tree(g, n0, tree);
582
583 // Verify tree was built correctly
584 EXPECT_EQ(tree.get_num_nodes(), 4);
585 EXPECT_EQ(tree.get_num_arcs(), 3);
586 EXPECT_NE(tree_start, nullptr);
587
588 // Use find_min_path instead which is more straightforward
589 Path<GT> path(g);
590 double d = dij(g, n0, n3, path);
591
592 EXPECT_EQ(d, 6.0);
593 ASSERT_FALSE(path.is_empty());
594}
595
596// ========== TEST 24: Null Node Validation ==========
598 GT g;
599 Node* n0 = g.insert_node(0);
600
602 GT tree;
603
604 EXPECT_THROW(dij.compute_min_paths_tree(g, nullptr, tree), std::domain_error);
605 EXPECT_THROW(dij.compute_partial_min_paths_tree(g, nullptr, n0, tree), std::domain_error);
606 EXPECT_THROW(dij.compute_partial_min_paths_tree(g, n0, nullptr, tree), std::domain_error);
607 EXPECT_THROW(dij.paint_min_paths_tree(g, nullptr), std::domain_error);
608 EXPECT_THROW(dij.paint_partial_min_paths_tree(g, nullptr, n0), std::domain_error);
609 EXPECT_THROW(dij.paint_partial_min_paths_tree(g, n0, nullptr), std::domain_error);
610}
611
612// ========== TEST 25: Disconnected Graph ==========
614 GT g;
615 Node* n0 = g.insert_node(0);
616 Node* n1 = g.insert_node(1);
617 Node* n2 = g.insert_node(2);
618 Node* n3 = g.insert_node(3);
619
620 // Two disconnected components: {n0, n1} and {n2, n3}
621 g.insert_arc(n0, n1, 1.0);
622 g.insert_arc(n2, n3, 1.0);
623
625 Path<GT> path(g);
626 double d = dij(g, n0, n3, path);
627
628 // n3 is not reachable from n0
629 EXPECT_EQ(d, std::numeric_limits<double>::max());
630 EXPECT_TRUE(path.is_empty());
631}
632
633// ========== TEST 26: Integer Weights ==========
636
637 IGT g;
638 auto* n0 = g.insert_node(0);
639 auto* n1 = g.insert_node(1);
640 auto* n2 = g.insert_node(2);
641
642 g.insert_arc(n0, n1, 5);
643 g.insert_arc(n1, n2, 3);
644
646 Path<IGT> path(g);
647 int d = dij(g, n0, n2, path);
648
649 EXPECT_EQ(d, 8);
650}
651
652// ========== TEST 27: Get Distance After Painting ==========
654 GT g;
655 Node* n0 = g.insert_node(0);
656 Node* n1 = g.insert_node(1);
657 Node* n2 = g.insert_node(2);
658 Node* n3 = g.insert_node(3);
659
660 g.insert_arc(n0, n1, 1.0);
661 g.insert_arc(n1, n2, 2.0);
662 g.insert_arc(n2, n3, 3.0);
663
665 dij.paint_min_paths_tree(g, n0);
666
667 EXPECT_EQ(dij.get_distance(n0), 0.0);
668 EXPECT_EQ(dij.get_distance(n1), 1.0);
669 EXPECT_EQ(dij.get_distance(n2), 3.0);
670 EXPECT_EQ(dij.get_distance(n3), 6.0);
671}
672
673// ========== TEST 28: Get Distance Before Painting ==========
675 GT g;
676 Node* n0 = g.insert_node(0);
677
679
680 // Should throw because not painted yet
681 EXPECT_THROW(dij.get_distance(n0), std::domain_error);
682}
683
684// ========== TEST 29: Get Distance Unreachable Node ==========
686 GT g;
687 Node* n0 = g.insert_node(0);
688 Node* n1 = g.insert_node(1);
689 Node* n2 = g.insert_node(2); // Not connected
690
691 g.insert_arc(n0, n1, 1.0);
692
694 dij.paint_min_paths_tree(g, n0);
695
696 // n2 is not reachable
697 EXPECT_THROW(dij.get_distance(n2), std::domain_error);
698}
699
700// ========== TEST 30: Multiple Successive Computations ==========
702 GT g;
703 Node* n0 = g.insert_node(0);
704 Node* n1 = g.insert_node(1);
705 Node* n2 = g.insert_node(2);
706
707 g.insert_arc(n0, n1, 1.0);
708 g.insert_arc(n1, n2, 2.0);
709 g.insert_arc(n0, n2, 10.0);
710
712
713 // First computation from n0
714 Path<GT> path1(g);
715 double d1 = dij(g, n0, n2, path1);
716 EXPECT_EQ(d1, 3.0);
717
718 // Second computation from n2 (different start)
719 Path<GT> path2(g);
720 double d2 = dij(g, n2, n0, path2);
721 EXPECT_EQ(d2, 3.0); // Same in undirected graph
722}
723
724// ========== TEST 31: Paint Partial Returns False When Not Found ==========
726 GT g;
727 Node* n0 = g.insert_node(0);
728 Node* n1 = g.insert_node(1);
729 Node* n2 = g.insert_node(2); // Disconnected
730
731 g.insert_arc(n0, n1, 1.0);
732
734 bool found = dij.paint_partial_min_paths_tree(g, n0, n2);
735
737}
738
739// ========== TEST 32: Paint Partial Returns True When Found ==========
741 GT g;
742 Node* n0 = g.insert_node(0);
743 Node* n1 = g.insert_node(1);
744 Node* n2 = g.insert_node(2);
745
746 g.insert_arc(n0, n1, 1.0);
747 g.insert_arc(n1, n2, 2.0);
748
750 bool found = dij.paint_partial_min_paths_tree(g, n0, n2);
751
753
754 // Should be able to get path now
755 Path<GT> path(g);
756 double d = dij.get_min_path(n2, path);
757 EXPECT_EQ(d, 3.0);
758}
759
760// ========== TEST 33: Very Long Path ==========
762 GT g;
763 const int N = 100;
764
765 std::vector<Node*> nodes(N);
766 for (int i = 0; i < N; ++i)
767 nodes[i] = g.insert_node(i);
768
769 for (int i = 0; i < N - 1; ++i)
770 g.insert_arc(nodes[i], nodes[i+1], 1.0);
771
773 Path<GT> path(g);
774 double d = dij(g, nodes[0], nodes[N-1], path);
775
776 EXPECT_EQ(d, static_cast<double>(N - 1));
777}
778
779// ========== TEST 34: Dense Graph Performance ==========
781 GT g;
782 const int N = 50;
783
784 std::vector<Node*> nodes(N);
785 for (int i = 0; i < N; ++i)
786 nodes[i] = g.insert_node(i);
787
788 // Create complete graph
789 for (int i = 0; i < N; ++i)
790 for (int j = i + 1; j < N; ++j)
791 g.insert_arc(nodes[i], nodes[j], static_cast<double>(i + j));
792
794 GT tree;
795 dij(g, nodes[0], tree);
796
797 EXPECT_EQ(tree.get_num_nodes(), N);
798 EXPECT_EQ(tree.get_num_arcs(), N - 1);
799}
800
801// ========== TEST 35: Compute Spanning Tree on Disconnected Graph ==========
803 GT g;
804 Node* n0 = g.insert_node(0);
805 Node* n1 = g.insert_node(1);
806 Node* n2 = g.insert_node(2);
807 Node* n3 = g.insert_node(3);
808
809 g.insert_arc(n0, n1, 1.0);
810 g.insert_arc(n2, n3, 1.0);
811
813 GT tree;
814 dij.compute_min_paths_tree(g, n0, tree);
815
816 EXPECT_EQ(tree.get_num_nodes(), 2);
817 EXPECT_EQ(tree.get_num_arcs(), 1);
818}
819
820// ========== TEST 36: Parallel Arcs (Multigraph) ==========
821// Test that Dijkstra correctly handles graphs with multiple arcs between same nodes
822// NOTE: Uses DijkstraAllArcs which uses Out_Iterator_Ne to visit ALL arcs including parallel ones
824 GT g;
825 Node* n0 = g.insert_node(0);
826 Node* n1 = g.insert_node(1);
827 Node* n2 = g.insert_node(2);
828
829 // Multiple arcs between n0 and n1 with different weights
830 g.insert_arc(n0, n1, 10.0); // expensive
831 g.insert_arc(n0, n1, 3.0); // cheap - should be chosen
832 g.insert_arc(n0, n1, 7.0); // medium
833
834 // Single arc to destination
835 g.insert_arc(n1, n2, 2.0);
836
837 // Check if graph supports parallel arcs
838 if (g.get_num_arcs() < 4) {
839 GTEST_SKIP() << "Graph type does not support parallel arcs (multigraph)";
840 }
841
843 Path<GT> path(g);
844 double d = dij(g, n0, n2, path);
845
846 // Shortest path should use the 3.0 arc: 3.0 + 2.0 = 5.0
847 EXPECT_EQ(d, 5.0);
848}
849
850// ========== TEST 37: Parallel Arcs in Both Directions ==========
852 GT g;
853 Node* n0 = g.insert_node(0);
854 Node* n1 = g.insert_node(1);
855
856 // Arcs in both directions with different weights
857 g.insert_arc(n0, n1, 5.0);
858 g.insert_arc(n1, n0, 3.0);
859 g.insert_arc(n0, n1, 2.0); // Best from n0 to n1
860
861 // Check if graph supports parallel arcs
862 if (g.get_num_arcs() < 3) {
863 GTEST_SKIP() << "Graph type does not support parallel arcs (multigraph)";
864 }
865
867 Path<GT> path(g);
868 double d = dij(g, n0, n1, path);
869
870 // In undirected graph, shortest should be 2.0
871 EXPECT_EQ(d, 2.0);
872}
873
874// ========== TEST 38: Complex Multigraph ==========
876 GT g;
877 Node* n0 = g.insert_node(0);
878 Node* n1 = g.insert_node(1);
879 Node* n2 = g.insert_node(2);
880 Node* n3 = g.insert_node(3);
881
882 // Multiple paths with parallel arcs
883 g.insert_arc(n0, n1, 4.0);
884 g.insert_arc(n0, n1, 2.0); // Best n0->n1
885 g.insert_arc(n1, n2, 3.0);
886 g.insert_arc(n1, n2, 1.0); // Best n1->n2
887 g.insert_arc(n2, n3, 5.0);
888 g.insert_arc(n2, n3, 2.0); // Best n2->n3
889
890 // Alternative path
891 g.insert_arc(n0, n3, 10.0);
892
893 // Check if graph supports parallel arcs
894 if (g.get_num_arcs() < 7) {
895 GTEST_SKIP() << "Graph type does not support parallel arcs (multigraph)";
896 }
897
899 Path<GT> path(g);
900 double d = dij(g, n0, n3, path);
901
902 // Best path: 2.0 + 1.0 + 2.0 = 5.0
903 EXPECT_EQ(d, 5.0);
904}
905
906// ========== TEST 39: Get Min Path From Tree ==========
908 GT g;
909 Node* n0 = g.insert_node(0);
910 Node* n1 = g.insert_node(1);
911 Node* n2 = g.insert_node(2);
912 Node* n3 = g.insert_node(3);
913 Node* n4 = g.insert_node(4);
914
915 g.insert_arc(n0, n1, 1.0);
916 g.insert_arc(n1, n2, 2.0);
917 g.insert_arc(n2, n3, 3.0);
918 g.insert_arc(n3, n4, 4.0);
919 g.insert_arc(n0, n4, 100.0); // Long direct path
920
922 GT tree;
923
924 // First compute the spanning tree
925 auto tree_start = dij.compute_min_paths_tree(g, n0, tree);
926 ASSERT_NE(tree_start, nullptr);
927
928 // Now extract paths from the tree to different destinations
930 double d2 = dij.get_min_path(tree, n2, path_to_n2);
931 EXPECT_EQ(d2, 3.0); // 1 + 2
933
935 double d4 = dij.get_min_path(tree, n4, path_to_n4);
936 EXPECT_EQ(d4, 10.0); // 1 + 2 + 3 + 4
938
939 // Verify path nodes for n4
941 for (Path<GT>::Iterator it(path_to_n4); it.has_curr(); it.next())
942 path_nodes.append(it.get_current_node()->get_info());
943
945 auto pit = path_nodes.get_it();
946 EXPECT_EQ(pit.get_curr(), 0); pit.next();
947 EXPECT_EQ(pit.get_curr(), 1); pit.next();
948 EXPECT_EQ(pit.get_curr(), 2); pit.next();
949 EXPECT_EQ(pit.get_curr(), 3); pit.next();
950 EXPECT_EQ(pit.get_curr(), 4);
951}
952
953// ========== TEST 40: Get Min Path From Tree Multiple Queries ==========
955 GT g;
956 Node* n0 = g.insert_node(0);
957 Node* n1 = g.insert_node(1);
958 Node* n2 = g.insert_node(2);
959 Node* n3 = g.insert_node(3);
960
961 // Star graph from n0
962 g.insert_arc(n0, n1, 1.0);
963 g.insert_arc(n0, n2, 2.0);
964 g.insert_arc(n0, n3, 3.0);
965
967 GT tree;
968 dij.compute_min_paths_tree(g, n0, tree);
969
970 // Query multiple destinations from same tree (efficient!)
971 Path<GT> p1(g), p2(g), p3(g);
972
973 double d1 = dij.get_min_path(tree, n1, p1);
974 double d2 = dij.get_min_path(tree, n2, p2);
975 double d3 = dij.get_min_path(tree, n3, p3);
976
977 EXPECT_EQ(d1, 1.0);
978 EXPECT_EQ(d2, 2.0);
979 EXPECT_EQ(d3, 3.0);
980
981 // Each path should have 2 nodes (start + destination)
982 EXPECT_EQ(p1.size(), 2);
983 EXPECT_EQ(p2.size(), 2);
984 EXPECT_EQ(p3.size(), 2);
985}
986
987// ========== GoogleTest main ==========
988int main(int argc, char **argv)
989{
990 ::testing::InitGoogleTest(&argc, argv);
991 return RUN_ALL_TESTS();
992}
Dijkstra's shortest path algorithm.
int main()
::testing::Types< BinHeapTag, FibHeapTag > HeapTypes
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
auto get_current_arc() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition tpl_graph.H:1666
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
typename BaseGraph::Arc Arc
Definition graph-dry.H:3852
typename BaseGraph::Node Node
Definition graph-dry.H:3851
Spanning tree calculation of all shortest paths from a given node according to Dijkstra's algorithm.
Definition Dijkstra.H:101
void next()
Move the iterator one item forward.
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
T & append(const T &item)
Append a new item by copy.
Definition htlist.H:1562
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Graph_Arc< double > Arc
The node class type.
Definition tpl_graph.H:433
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Filtered iterator for outcoming arcs of a node.
Definition tpl_graph.H:1750
Iterator on nodes and arcs of a path.
Definition tpl_graph.H:3201
Node * get_current_node() const
Return the current node of a path.
Definition tpl_graph.H:3222
Path on a graph.
Definition tpl_graph.H:2669
size_t size() const noexcept
Return the path length in nodes.
Definition tpl_graph.H:2807
bool is_empty() const noexcept
Return true if the path is empty.
Definition tpl_graph.H:2813
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
constexpr size_t get_num_arcs() const noexcept
Definition graph-dry.H:778
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:190
GT::Arc * get_current_arc_ne() const noexcept
#define N
Definition fib.C:294
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1119
TYPED_TEST(DijkstraHeapTest, BasicShortestPath)
DGT::Arc DArc
DGT::Node DNode
TYPED_TEST_SUITE(DijkstraHeapTest, HeapTypes)
Generic graph and digraph implementations.