Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
test_dial.cc
Go to the documentation of this file.
1
9# include <gtest/gtest.h>
10# include <cstdint>
11# include <Dial.H>
12# include <Dijkstra.H>
13# include <tpl_graph.H>
14
15using namespace Aleph;
16
17// ============================================================================
18// Type aliases
19// ============================================================================
20
22using Node = GT::Node;
23using Arc = GT::Arc;
24
27using DArc = DGT::Arc;
28
30{
32
33 [[nodiscard]] Distance_Type operator()(Arc * arc) const noexcept
34 {
35 return static_cast<Distance_Type>(arc->get_info());
36 }
37};
38
39// ============================================================================
40// TEST 1: Basic shortest path
41// ============================================================================
43{
44 GT g;
45 auto n0 = g.insert_node(0);
46 auto n1 = g.insert_node(1);
47 auto n2 = g.insert_node(2);
48 auto n3 = g.insert_node(3);
49
50 g.insert_arc(n0, n1, 3);
51 g.insert_arc(n0, n2, 1);
52 g.insert_arc(n1, n3, 2);
53 g.insert_arc(n2, n3, 5);
54 g.insert_arc(n2, n1, 1);
55
57 Path<GT> path(g);
58 int d = dial(g, n0, n3, path);
59
60 // Best: n0->n2(1)->n1(1)->n3(2) = 4
61 EXPECT_EQ(d, 4);
62}
63
64// ============================================================================
65// TEST 2: Path to self
66// ============================================================================
68{
69 GT g;
70 auto n0 = g.insert_node(0);
71
73 Path<GT> path(g);
74 int d = dial(g, n0, n0, path);
75
76 EXPECT_EQ(d, 0);
77}
78
79// ============================================================================
80// TEST 3: No path exists
81// ============================================================================
83{
84 GT g;
85 auto n0 = g.insert_node(0);
86 auto n1 = g.insert_node(1);
87
89 Path<GT> path(g);
90 int d = dial(g, n0, n1, path);
91
92 EXPECT_EQ(d, std::numeric_limits<int>::max());
93 EXPECT_TRUE(path.is_empty());
94}
95
96// ============================================================================
97// TEST 4: Single node graph
98// ============================================================================
100{
101 GT g;
102 auto n0 = g.insert_node(0);
103
105 Path<GT> path(g);
106 int d = dial(g, n0, n0, path);
107
108 EXPECT_EQ(d, 0);
109}
110
111// ============================================================================
112// TEST 5: Linear chain
113// ============================================================================
115{
116 GT g;
117 auto n0 = g.insert_node(0);
118 auto n1 = g.insert_node(1);
119 auto n2 = g.insert_node(2);
120 auto n3 = g.insert_node(3);
121
122 g.insert_arc(n0, n1, 2);
123 g.insert_arc(n1, n2, 3);
124 g.insert_arc(n2, n3, 4);
125
127 Path<GT> path(g);
128 int d = dial(g, n0, n3, path);
129
130 EXPECT_EQ(d, 9);
131}
132
133// ============================================================================
134// TEST 6: All zero weights
135// ============================================================================
137{
138 GT g;
139 auto n0 = g.insert_node(0);
140 auto n1 = g.insert_node(1);
141 auto n2 = g.insert_node(2);
142
143 g.insert_arc(n0, n1, 0);
144 g.insert_arc(n1, n2, 0);
145
147 Path<GT> path(g);
148 int d = dial(g, n0, n2, path);
149
150 EXPECT_EQ(d, 0);
151}
152
153// ============================================================================
154// TEST 7: Weight 1 edges (like BFS)
155// ============================================================================
157{
158 GT g;
159 auto n0 = g.insert_node(0);
160 auto n1 = g.insert_node(1);
161 auto n2 = g.insert_node(2);
162 auto n3 = g.insert_node(3);
163
164 g.insert_arc(n0, n1, 1);
165 g.insert_arc(n0, n2, 1);
166 g.insert_arc(n1, n3, 1);
167 g.insert_arc(n2, n3, 1);
168
170 Path<GT> path(g);
171 int d = dial(g, n0, n3, path);
172
173 EXPECT_EQ(d, 2);
174}
175
176// ============================================================================
177// TEST 8: Null node validation
178// ============================================================================
180{
181 GT g;
182 auto n0 = g.insert_node(0);
183
185 Path<GT> path(g);
186
187 EXPECT_THROW(dial(g, nullptr, n0, path), std::domain_error);
188 EXPECT_THROW(dial(g, n0, nullptr, path), std::domain_error);
189}
190
191// ============================================================================
192// TEST 9: Negative weight validation and rollback
193// ============================================================================
195{
196 GT g;
197 auto n0 = g.insert_node(0);
198 auto n1 = g.insert_node(1);
199 auto a01 = g.insert_arc(n0, n1, -3);
200
202 Path<GT> path(g);
203
204 EXPECT_THROW(dial(g, n0, n1, path), std::domain_error);
205 EXPECT_EQ(NODE_COOKIE(n0), nullptr);
206 EXPECT_EQ(NODE_COOKIE(n1), nullptr);
210 EXPECT_FALSE(dial.is_painted());
211 EXPECT_EQ(dial.get_graph(), nullptr);
212 EXPECT_EQ(dial.get_start_node(), nullptr);
213}
214
215// ============================================================================
216// TEST 9: Paint and get_min_path
217// ============================================================================
219{
220 GT g;
221 auto n0 = g.insert_node(0);
222 auto n1 = g.insert_node(1);
223 auto n2 = g.insert_node(2);
224 auto n3 = g.insert_node(3);
225
226 g.insert_arc(n0, n1, 1);
227 g.insert_arc(n1, n2, 2);
228 g.insert_arc(n2, n3, 3);
229
232
233 EXPECT_TRUE(dial.is_painted());
234
235 Path<GT> path(g);
236 int d = dial.get_min_path(n3, path);
237 EXPECT_EQ(d, 6);
238}
239
240// ============================================================================
241// TEST 10: Get distance after painting
242// ============================================================================
244{
245 GT g;
246 auto n0 = g.insert_node(0);
247 auto n1 = g.insert_node(1);
248 auto n2 = g.insert_node(2);
249 auto n3 = g.insert_node(3);
250
251 g.insert_arc(n0, n1, 1);
252 g.insert_arc(n1, n2, 2);
253 g.insert_arc(n2, n3, 3);
254
257
258 EXPECT_EQ(dial.get_distance(n0), 0);
259 EXPECT_EQ(dial.get_distance(n1), 1);
260 EXPECT_EQ(dial.get_distance(n2), 3);
261 EXPECT_EQ(dial.get_distance(n3), 6);
262}
263
264// ============================================================================
265// TEST 11: Get distance before painting
266// ============================================================================
268{
269 GT g;
270 auto n0 = g.insert_node(0);
271
273 EXPECT_THROW(dial.get_distance(n0), std::domain_error);
274}
275
276// ============================================================================
277// TEST 12: Cross-validation with Dijkstra
278// ============================================================================
280{
281 GT g;
282 auto n0 = g.insert_node(0);
283 auto n1 = g.insert_node(1);
284 auto n2 = g.insert_node(2);
285 auto n3 = g.insert_node(3);
286 auto n4 = g.insert_node(4);
287 auto n5 = g.insert_node(5);
288
289 g.insert_arc(n0, n1, 4);
290 g.insert_arc(n0, n2, 2);
291 g.insert_arc(n1, n3, 5);
292 g.insert_arc(n2, n1, 1);
293 g.insert_arc(n2, n3, 8);
294 g.insert_arc(n3, n4, 2);
295 g.insert_arc(n4, n5, 1);
296 g.insert_arc(n2, n5, 10);
297
298 // Dial
301 int dial_d = dial(g, n0, n5, dial_path);
302
303 // Dijkstra
306 int dij_d = dij(g, n0, n5, dij_path);
307
309}
310
311// ============================================================================
312// TEST 13: Directed graph
313// ============================================================================
315{
316 DGT g;
317 auto n0 = g.insert_node(0);
318 auto n1 = g.insert_node(1);
319 auto n2 = g.insert_node(2);
320
321 g.insert_arc(n0, n1, 3);
322 g.insert_arc(n1, n2, 2);
323
325 Path<DGT> path(g);
326 int d = dial(g, n0, n2, path);
327
328 EXPECT_EQ(d, 5);
329}
330
331// ============================================================================
332// TEST 14: Directed graph no return
333// ============================================================================
335{
336 DGT g;
337 auto n0 = g.insert_node(0);
338 auto n1 = g.insert_node(1);
339
340 g.insert_arc(n0, n1, 3);
341
343
344 Path<DGT> p1(g);
345 EXPECT_EQ(dial(g, n0, n1, p1), 3);
346
347 Path<DGT> p2(g);
348 EXPECT_EQ(dial(g, n1, n0, p2), std::numeric_limits<int>::max());
349}
350
351// ============================================================================
352// TEST 15: Multiple computations
353// ============================================================================
355{
356 GT g;
357 auto n0 = g.insert_node(0);
358 auto n1 = g.insert_node(1);
359 auto n2 = g.insert_node(2);
360
361 g.insert_arc(n0, n1, 2);
362 g.insert_arc(n1, n2, 3);
363
365
366 Path<GT> p1(g);
367 EXPECT_EQ(dial(g, n0, n2, p1), 5);
368
369 Path<GT> p2(g);
370 EXPECT_EQ(dial(g, n2, n0, p2), 5); // undirected
371}
372
373// ============================================================================
374// TEST 16: Large linear graph
375// ============================================================================
377{
378 GT g;
379 const int N = 100;
380
382 for (int i = 0; i < N; ++i)
383 nodes[i] = g.insert_node(i);
384
385 for (int i = 0; i < N - 1; ++i)
386 g.insert_arc(nodes[i], nodes[i + 1], 1);
387
389 Path<GT> path(g);
390 int d = dial(g, nodes[0], nodes[N - 1], path);
391
392 EXPECT_EQ(d, N - 1);
393}
394
395// ============================================================================
396// TEST 17: Large weights
397// ============================================================================
399{
400 GT g;
401 auto n0 = g.insert_node(0);
402 auto n1 = g.insert_node(1);
403 auto n2 = g.insert_node(2);
404
405 g.insert_arc(n0, n1, 100);
406 g.insert_arc(n1, n2, 200);
407
409 Path<GT> path(g);
410 int d = dial(g, n0, n2, path);
411
412 EXPECT_EQ(d, 300);
413}
414
415// ============================================================================
416// TEST 18: Path node-by-node verification
417// ============================================================================
419{
420 GT g;
421 auto n0 = g.insert_node(0);
422 auto n1 = g.insert_node(1);
423 auto n2 = g.insert_node(2);
424 auto n3 = g.insert_node(3);
425
426 g.insert_arc(n0, n1, 1);
427 g.insert_arc(n1, n2, 2);
428 g.insert_arc(n2, n3, 3);
429 g.insert_arc(n0, n3, 100);
430
432 Path<GT> path(g);
433 int d = dial(g, n0, n3, path);
434
435 EXPECT_EQ(d, 6);
436
438 for (Path<GT>::Iterator it(path); it.has_curr(); it.next())
439 nodes.append(it.get_current_node()->get_info());
440
441 ASSERT_EQ(nodes.size(), 4u);
442 auto pit = nodes.get_it();
443 EXPECT_EQ(pit.get_curr(), 0); pit.next();
444 EXPECT_EQ(pit.get_curr(), 1); pit.next();
445 EXPECT_EQ(pit.get_curr(), 2); pit.next();
446 EXPECT_EQ(pit.get_curr(), 3);
447}
448
449// ============================================================================
450// TEST 19: Complete graph K4
451// ============================================================================
453{
454 GT g;
455 auto n0 = g.insert_node(0);
456 auto n1 = g.insert_node(1);
457 auto n2 = g.insert_node(2);
458 auto n3 = g.insert_node(3);
459
460 g.insert_arc(n0, n1, 1);
461 g.insert_arc(n0, n2, 1);
462 g.insert_arc(n0, n3, 1);
463 g.insert_arc(n1, n2, 1);
464 g.insert_arc(n1, n3, 1);
465 g.insert_arc(n2, n3, 1);
466
468 Path<GT> path(g);
469 int d = dial(g, n0, n3, path);
470
471 EXPECT_EQ(d, 1);
472}
473
474// ============================================================================
475// TEST 20: Graph with cycle
476// ============================================================================
478{
479 GT g;
480 auto n0 = g.insert_node(0);
481 auto n1 = g.insert_node(1);
482 auto n2 = g.insert_node(2);
483
484 g.insert_arc(n0, n1, 1);
485 g.insert_arc(n1, n2, 1);
486 g.insert_arc(n2, n0, 1);
487
489 Path<GT> path(g);
490 int d = dial(g, n0, n2, path);
491
492 // Undirected: n0->n2 via edge n2-n0 costs 1
493 EXPECT_EQ(d, 1);
494}
495
496// ============================================================================
497// TEST 21: State getters
498// ============================================================================
500{
501 GT g;
502 auto n0 = g.insert_node(0);
503 auto n1 = g.insert_node(1);
504 g.insert_arc(n0, n1, 1);
505
507
508 EXPECT_FALSE(dial.is_painted());
509 EXPECT_EQ(dial.get_start_node(), nullptr);
510 EXPECT_EQ(dial.get_graph(), nullptr);
511
512 dial.paint_min_paths_tree(g, n0);
513
514 EXPECT_TRUE(dial.is_painted());
515 EXPECT_EQ(dial.get_start_node(), n0);
516 EXPECT_EQ(dial.get_graph(), &g);
517}
518
519// ============================================================================
520// TEST 22: Disconnected graph painting
521// ============================================================================
523{
524 GT g;
525 auto n0 = g.insert_node(0);
526 auto n1 = g.insert_node(1);
527 auto n2 = g.insert_node(2);
528 auto n3 = g.insert_node(3);
529
530 g.insert_arc(n0, n1, 1);
531 g.insert_arc(n2, n3, 2);
532
535
536 EXPECT_EQ(dial.get_distance(n1), 1);
537 EXPECT_THROW(dial.get_distance(n2), std::domain_error);
538}
539
540// ============================================================================
541// TEST 23: Relaxation correctness (cheaper path found later)
542// ============================================================================
544{
545 GT g;
546 auto n0 = g.insert_node(0);
547 auto n1 = g.insert_node(1);
548 auto n2 = g.insert_node(2);
549
550 g.insert_arc(n0, n1, 10);
551 g.insert_arc(n0, n2, 1);
552 g.insert_arc(n2, n1, 1);
553
555 Path<GT> path(g);
556 int d = dial(g, n0, n1, path);
557
558 EXPECT_EQ(d, 2); // n0->n2(1)->n1(1) = 2, not direct n0->n1(10)
559}
560
561// ============================================================================
562// TEST 24: Star graph
563// ============================================================================
565{
566 GT g;
567 auto center = g.insert_node(0);
568 auto n1 = g.insert_node(1);
569 auto n2 = g.insert_node(2);
570 auto n3 = g.insert_node(3);
571 auto n4 = g.insert_node(4);
572
573 g.insert_arc(center, n1, 1);
574 g.insert_arc(center, n2, 2);
575 g.insert_arc(center, n3, 3);
576 g.insert_arc(center, n4, 4);
577
579 dial.paint_min_paths_tree(g, center);
580
581 EXPECT_EQ(dial.get_distance(n1), 1);
582 EXPECT_EQ(dial.get_distance(n2), 2);
583 EXPECT_EQ(dial.get_distance(n3), 3);
584 EXPECT_EQ(dial.get_distance(n4), 4);
585}
586
587// ============================================================================
588// === EXAMPLE: Road network with integer kilometer distances ===
589// A small network of cities connected by roads with integer distances.
590// Find the shortest route from city A to city E.
591//
592// A ---3--- B ---2--- E
593// | |
594// 1 4
595// | |
596// C ---1--- D
597//
598// Shortest A->E: A->C(1)->D(1)->B(4)->E(2) = 8? No, A->B(3)->E(2)=5
599// ============================================================================
601{
602 GT g;
603 auto a = g.insert_node(0); // A
604 auto b = g.insert_node(1); // B
605 auto c = g.insert_node(2); // C
606 auto d = g.insert_node(3); // D
607 auto e = g.insert_node(4); // E
608
609 g.insert_arc(a, b, 3);
610 g.insert_arc(a, c, 1);
611 g.insert_arc(b, d, 4);
612 g.insert_arc(b, e, 2);
613 g.insert_arc(c, d, 1);
614
616 Path<GT> path(g);
617 int dist = dial(g, a, e, path);
618
619 EXPECT_EQ(dist, 5); // A->B(3)->E(2)
620 EXPECT_FALSE(path.is_empty());
621}
622
623// ============================================================================
624// TEST 26: Mixed weights cross-validation with Dijkstra on larger graph
625// ============================================================================
627{
628 GT g;
629 const int N = 20;
630
632 for (int i = 0; i < N; ++i)
633 nodes[i] = g.insert_node(i);
634
635 // Create edges with various weights
636 for (int i = 0; i < N - 1; ++i)
637 g.insert_arc(nodes[i], nodes[i + 1], (i % 5) + 1);
638
639 // Some cross edges
640 for (int i = 0; i < N - 3; i += 3)
641 g.insert_arc(nodes[i], nodes[i + 3], 10);
642
645
646 for (int end = 1; end < N; end += 3)
647 {
648 Path<GT> dp(g), jp(g);
649 int dd = dial(g, nodes[0], nodes[end], dp);
650 int jd = dij(g, nodes[0], nodes[end], jp);
651 EXPECT_EQ(dd, jd) << "Mismatch for end=" << end;
652 }
653}
654
655// ============================================================================
656// TEST 27: Distance type overflow validation
657// ============================================================================
659{
660 GT g;
661 auto n0 = g.insert_node(0);
662 auto n1 = g.insert_node(1);
663 auto n2 = g.insert_node(2);
664
665 // max_dist = (3-1) * 120 = 240, which does not fit in int8_t.
666 g.insert_arc(n0, n1, 120);
667 g.insert_arc(n1, n2, 120);
668
670 Path<GT> path(g);
671 EXPECT_THROW(dial(g, n0, n2, path), std::overflow_error);
672}
673
674// ============================================================================
675// TEST 28: Bucket upper bound validation
676// ============================================================================
678{
679 GT g;
680 auto n0 = g.insert_node(0);
681 auto n1 = g.insert_node(1);
682 constexpr int bucket_limit = 16 * 1024 * 1024;
683
684 // With |V|=2, max_dist = max_w. This forces num_buckets beyond safety limit.
685 g.insert_arc(n0, n1, bucket_limit);
686
688 Path<GT> path(g);
689 EXPECT_THROW(dial(g, n0, n1, path), std::runtime_error);
690}
691
692// ============================================================================
693// TEST 29: Parallel edges must keep a single parent arc marked
694// ============================================================================
696{
697 GT g;
698 auto start = g.insert_node(0);
699 auto mid = g.insert_node(1);
700 auto end = g.insert_node(2);
701
702 auto direct_high = g.insert_arc(start, end, 10);
703 auto start_mid = g.insert_arc(start, mid, 1);
704 auto mid_end_high = g.insert_arc(mid, end, 5);
705 auto mid_end_low = g.insert_arc(mid, end, 1); // Parallel arc with better cost
706
708 dial.paint_min_paths_tree(g, start);
709
710 EXPECT_EQ(dial.get_distance(end), 2);
711
712 Path<GT> path(g);
713 EXPECT_EQ(dial.get_min_path(end, path), 2);
714
719
720 size_t painted_arcs = 0;
721 for (GT::Arc_Iterator it(g); it.has_curr(); it.next())
722 if (IS_ARC_VISITED(it.get_curr(), Aleph::Spanning_Tree))
723 ++painted_arcs;
724
726}
Dial's algorithm for shortest paths with small integer weights.
Dijkstra's shortest path algorithm.
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:194
Dial's algorithm for shortest paths with integer weights.
Definition Dial.H:126
void paint_min_paths_tree(const GT &g, Node *start)
Paints the shortest paths tree on the graph from start.
Definition Dial.H:444
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3854
typename BaseGraph::Arc Arc
Definition graph-dry.H:3858
typename BaseGraph::Node Node
Definition graph-dry.H:3857
Spanning tree calculation of all shortest paths from a given node according to Dijkstra's algorithm.
Definition Dijkstra.H:101
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Graph_Arc< int > 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
Iterator on nodes and arcs of a path.
Definition tpl_graph.H:3208
Path on a graph.
Definition tpl_graph.H:2669
bool is_empty() const noexcept
Return true if the path is empty.
Definition tpl_graph.H:2813
#define TEST(name)
#define N
Definition fib.C:294
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
#define IS_NODE_VISITED(p, bit)
Determine whether the control bit is set or not to one.
#define NODE_COOKIE(p)
Return the node cookie
#define IS_ARC_VISITED(p, bit)
Determine whether the bit field is or not set to one.
@ Spanning_Tree
Definition aleph-graph.H:79
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
int8_t Distance_Type
Definition test_dial.cc:31
Distance_Type operator()(Arc *arc) const noexcept
Definition test_dial.cc:33
DGT::Arc DArc
DGT::Node DNode
Generic graph and digraph implementations.