Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tarjan_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
33
38#include <gtest/gtest.h>
39#include <tpl_graph.H>
40#include <Tarjan.H>
41
42using namespace Aleph;
43
45
46// Helper functions
48{
49 return g.insert_node(value);
50}
51
53 int value = 0)
54{
55 return g.insert_arc(src, tgt, value);
56}
57
58// ============================================================================
59// Test: Empty graph
60// ============================================================================
62{
65
66 // Test connected_components returning node lists
67 auto sccs = tarjan.connected_components(g);
69
70 // Test connected_components with sizes
72 tarjan.connected_components(g, sizes);
74
75 // Test has_cycle
76 EXPECT_FALSE(tarjan.has_cycle(g));
77
78 // Test is_dag
79 EXPECT_TRUE(tarjan.is_dag(g));
80
81 // Test compute_cycle
82 Path<TestDigraph> path(g);
83 EXPECT_FALSE(tarjan.compute_cycle(g, path));
84 EXPECT_TRUE(path.is_empty());
85
86 // Test test_connectivity - empty graph is trivially connected
87 EXPECT_TRUE(tarjan.test_connectivity(g));
88}
89
90// ============================================================================
91// Test: Single node without self-loop
92// ============================================================================
94{
96 auto n1 = add_node(g, 1);
97
99
100 // Test connected_components
101 auto sccs = tarjan.connected_components(g);
102 EXPECT_EQ(sccs.size(), 1u);
103 EXPECT_EQ(sccs.get_first().size(), 1u);
104 EXPECT_EQ(sccs.get_first().get_first(), n1);
105
106 // Test sizes
108 tarjan.connected_components(g, sizes);
109 EXPECT_EQ(sizes.size(), 1u);
111
112 // Test has_cycle - single node without self-loop has no cycle
113 EXPECT_FALSE(tarjan.has_cycle(g));
114
115 // Test is_dag
116 EXPECT_TRUE(tarjan.is_dag(g));
117
118 // Test compute_cycle
119 Path<TestDigraph> path(g);
120 EXPECT_FALSE(tarjan.compute_cycle(g, path));
121
122 // Test connectivity
123 EXPECT_TRUE(tarjan.test_connectivity(g));
124}
125
126// ============================================================================
127// Test: Single node with self-loop
128// ============================================================================
130{
131 TestDigraph g;
132 auto n1 = add_node(g, 1);
133 add_arc(g, n1, n1);
134
136
137 // Test connected_components
138 auto sccs = tarjan.connected_components(g);
139 EXPECT_EQ(sccs.size(), 1u);
140 EXPECT_EQ(sccs.get_first().size(), 1u);
141
142 // Test has_cycle - self-loop IS a cycle
143 EXPECT_TRUE(tarjan.has_cycle(g));
144
145 // Test is_dag
146 EXPECT_FALSE(tarjan.is_dag(g));
147
148 // Test compute_cycle - should find the self-loop cycle
149 Path<TestDigraph> path(g);
150 bool found = tarjan.compute_cycle(g, path);
152 EXPECT_FALSE(path.is_empty());
153 // For a self-loop, the path should start and end at the same node
154 EXPECT_EQ(path.get_first_node(), path.get_last_node());
155
156 // Test connectivity
157 EXPECT_TRUE(tarjan.test_connectivity(g));
158}
159
160// ============================================================================
161// Test: Two nodes, one arc (A -> B), no cycle
162// ============================================================================
164{
165 TestDigraph g;
166 auto a = add_node(g, 1);
167 auto b = add_node(g, 2);
168 add_arc(g, a, b);
169
171
172 // Should have 2 SCCs (each node is its own SCC)
173 auto sccs = tarjan.connected_components(g);
174 EXPECT_EQ(sccs.size(), 2u);
175
176 // Test sizes
178 tarjan.connected_components(g, sizes);
179 EXPECT_EQ(sizes.size(), 2u);
180 for (auto sz : sizes)
181 EXPECT_EQ(sz, 1u);
182
183 // No cycle
184 EXPECT_FALSE(tarjan.has_cycle(g));
185 EXPECT_TRUE(tarjan.is_dag(g));
186
187 Path<TestDigraph> path(g);
188 EXPECT_FALSE(tarjan.compute_cycle(g, path));
189
190 // Not strongly connected (can't go from B to A)
191 EXPECT_FALSE(tarjan.test_connectivity(g));
192}
193
194// ============================================================================
195// Test: Two nodes with cycle (A <-> B)
196// ============================================================================
198{
199 TestDigraph g;
200 auto a = add_node(g, 1);
201 auto b = add_node(g, 2);
202 add_arc(g, a, b);
203 add_arc(g, b, a);
204
206
207 // Should have 1 SCC with both nodes
208 auto sccs = tarjan.connected_components(g);
209 EXPECT_EQ(sccs.size(), 1u);
210 EXPECT_EQ(sccs.get_first().size(), 2u);
211
212 // Has cycle
213 EXPECT_TRUE(tarjan.has_cycle(g));
214 EXPECT_FALSE(tarjan.is_dag(g));
215
216 // compute_cycle should find the cycle
217 Path<TestDigraph> path(g);
218 EXPECT_TRUE(tarjan.compute_cycle(g, path));
219 EXPECT_FALSE(path.is_empty());
220 EXPECT_EQ(path.get_first_node(), path.get_last_node());
221
222 // Strongly connected
223 EXPECT_TRUE(tarjan.test_connectivity(g));
224}
225
226// ============================================================================
227// Test: Linear chain (A -> B -> C -> D), DAG
228// ============================================================================
230{
231 TestDigraph g;
232 auto a = add_node(g, 1);
233 auto b = add_node(g, 2);
234 auto c = add_node(g, 3);
235 auto d = add_node(g, 4);
236 add_arc(g, a, b);
237 add_arc(g, b, c);
238 add_arc(g, c, d);
239
241
242 // 4 SCCs
243 auto sccs = tarjan.connected_components(g);
244 EXPECT_EQ(sccs.size(), 4u);
245
246 EXPECT_FALSE(tarjan.has_cycle(g));
247 EXPECT_TRUE(tarjan.is_dag(g));
248 EXPECT_FALSE(tarjan.test_connectivity(g));
249}
250
251// ============================================================================
252// Test: Simple cycle (A -> B -> C -> A)
253// ============================================================================
255{
256 TestDigraph g;
257 auto a = add_node(g, 1);
258 auto b = add_node(g, 2);
259 auto c = add_node(g, 3);
260 add_arc(g, a, b);
261 add_arc(g, b, c);
262 add_arc(g, c, a);
263
265
266 // 1 SCC with 3 nodes
267 auto sccs = tarjan.connected_components(g);
268 EXPECT_EQ(sccs.size(), 1u);
269 EXPECT_EQ(sccs.get_first().size(), 3u);
270
271 EXPECT_TRUE(tarjan.has_cycle(g));
272 EXPECT_FALSE(tarjan.is_dag(g));
273
274 Path<TestDigraph> path(g);
275 EXPECT_TRUE(tarjan.compute_cycle(g, path));
276 EXPECT_EQ(path.get_first_node(), path.get_last_node());
277
278 EXPECT_TRUE(tarjan.test_connectivity(g));
279}
280
281// ============================================================================
282// Test: Multiple SCCs
283// ============================================================================
285{
286 TestDigraph g;
287 auto a = add_node(g, 1);
288 auto b = add_node(g, 2);
289 auto c = add_node(g, 3);
290 auto d = add_node(g, 4);
291 auto e = add_node(g, 5);
292
293 // SCC1: A -> B -> C -> A
294 add_arc(g, a, b);
295 add_arc(g, b, c);
296 add_arc(g, c, a);
297
298 // SCC2: D -> E -> D
299 add_arc(g, d, e);
300 add_arc(g, e, d);
301
302 // Inter-SCC arc
303 add_arc(g, b, d);
304
306
307 // 2 SCCs
308 auto sccs = tarjan.connected_components(g);
309 EXPECT_EQ(sccs.size(), 2u);
310
311 // Check sizes (3 and 2)
313 tarjan.connected_components(g, sizes);
314 EXPECT_EQ(sizes.size(), 2u);
315 size_t total = 0;
316 for (auto sz : sizes)
317 total += sz;
318 EXPECT_EQ(total, 5u);
319
320 // Has cycle
321 EXPECT_TRUE(tarjan.has_cycle(g));
322
323 // Not strongly connected (different SCCs)
324 EXPECT_FALSE(tarjan.test_connectivity(g));
325}
326
327// ============================================================================
328// Test: Connected components with block list and arc list
329// ============================================================================
331{
332 TestDigraph g;
333 auto a = add_node(g, 1);
334 auto b = add_node(g, 2);
335 auto c = add_node(g, 3);
336 auto d = add_node(g, 4);
337
338 // SCC1: A -> B -> A
339 add_arc(g, a, b);
340 add_arc(g, b, a);
341
342 // SCC2: C -> D -> C
343 add_arc(g, c, d);
344 add_arc(g, d, c);
345
346 // Inter-SCC arcs
347 auto cross1 = add_arc(g, b, c);
348 auto cross2 = add_arc(g, a, d);
349
351
354 tarjan.connected_components(g, blk_list, arc_list);
355
356 // 2 blocks
357 EXPECT_EQ(blk_list.size(), 2u);
358
359 // 2 inter-block arcs
360 EXPECT_EQ(arc_list.size(), 2u);
361
362 // Verify the cross arcs are in the list
363 bool found_cross1 = false, found_cross2 = false;
364 for (auto arc : arc_list)
365 {
366 if (arc == cross1)
367 found_cross1 = true;
368 if (arc == cross2)
369 found_cross2 = true;
370 }
373}
374
375// ============================================================================
376// Test: compute_cycle from specific source node
377// ============================================================================
379{
380 TestDigraph g;
381 auto a = add_node(g, 1);
382 auto b = add_node(g, 2);
383 auto c = add_node(g, 3);
384 add_arc(g, a, b);
385 add_arc(g, b, c);
386 add_arc(g, c, b); // cycle between B and C only
387
389
390 // Starting from A, we should find the cycle in B-C
392 bool found1 = tarjan.compute_cycle(g, a, path1);
394
395 // Starting from B, we should find the cycle
397 bool found2 = tarjan.compute_cycle(g, b, path2);
399}
400
401// ============================================================================
402// Test: Compute_Cycle_In_Digraph wrapper class
403// ============================================================================
405{
406 TestDigraph g;
407 auto a = add_node(g, 1);
408 auto b = add_node(g, 2);
409 auto c = add_node(g, 3);
410 add_arc(g, a, b);
411 add_arc(g, b, c);
412 add_arc(g, c, a);
413
414 // Test with operator()(g, path)
416 Path<TestDigraph> path(g);
417 bool found = finder(g, path);
419 EXPECT_FALSE(path.is_empty());
420
421 // Test with operator()(g) returning path
422 auto path2 = finder(g);
424
425 // Test with operator()(g, src) returning path
426 auto path3 = finder(g, a);
428}
429
430// ============================================================================
431// Test: DAG with diamond shape
432// ============================================================================
434{
435 TestDigraph g;
436 auto a = add_node(g, 1);
437 auto b = add_node(g, 2);
438 auto c = add_node(g, 3);
439 auto d = add_node(g, 4);
440 add_arc(g, a, b);
441 add_arc(g, a, c);
442 add_arc(g, b, d);
443 add_arc(g, c, d);
444
446
447 // 4 SCCs (each node is its own SCC)
448 auto sccs = tarjan.connected_components(g);
449 EXPECT_EQ(sccs.size(), 4u);
450
451 EXPECT_FALSE(tarjan.has_cycle(g));
452 EXPECT_TRUE(tarjan.is_dag(g));
453 EXPECT_FALSE(tarjan.test_connectivity(g));
454}
455
456// ============================================================================
457// Test: Complex graph with nested cycles
458// ============================================================================
460{
461 TestDigraph g;
462 auto n1 = add_node(g, 1);
463 auto n2 = add_node(g, 2);
464 auto n3 = add_node(g, 3);
465 auto n4 = add_node(g, 4);
466 auto n5 = add_node(g, 5);
467 auto n6 = add_node(g, 6);
468
469 // Create a strongly connected component with all 6 nodes
470 add_arc(g, n1, n2);
471 add_arc(g, n2, n3);
472 add_arc(g, n3, n1); // small cycle
473 add_arc(g, n3, n4);
474 add_arc(g, n4, n5);
475 add_arc(g, n5, n6);
476 add_arc(g, n6, n1); // large cycle back to n1
477
479
480 // All nodes should be in one SCC
481 auto sccs = tarjan.connected_components(g);
482 EXPECT_EQ(sccs.size(), 1u);
483 EXPECT_EQ(sccs.get_first().size(), 6u);
484
485 EXPECT_TRUE(tarjan.has_cycle(g));
486 EXPECT_TRUE(tarjan.test_connectivity(g));
487
488 Path<TestDigraph> path(g);
489 EXPECT_TRUE(tarjan.compute_cycle(g, path));
490 EXPECT_EQ(path.get_first_node(), path.get_last_node());
491}
492
493// ============================================================================
494// Test: Operator() overloads for DynDlist
495// ============================================================================
497{
498 TestDigraph g;
499 auto a = add_node(g, 1);
500 auto b = add_node(g, 2);
501 add_arc(g, a, b);
502 add_arc(g, b, a);
503
505
506 // Test DynDlist<GT> overload
509 tarjan(g, blk_list, arc_list);
510 EXPECT_EQ(blk_list.size(), 1u);
511 EXPECT_TRUE(arc_list.is_empty()); // no inter-block arcs
512
513 // Test DynDlist<DynDlist<Node*>> overload
515 tarjan(g, blks);
516 EXPECT_EQ(blks.size(), 1u);
517 EXPECT_EQ(blks.get_first().size(), 2u);
518}
519
520// ============================================================================
521// Test: Disconnected graph components (no edges between them)
522// ============================================================================
524{
525 TestDigraph g;
526 // Component 1: A -> B -> C -> A
527 auto a = add_node(g, 1);
528 auto b = add_node(g, 2);
529 auto c = add_node(g, 3);
530 add_arc(g, a, b);
531 add_arc(g, b, c);
532 add_arc(g, c, a);
533
534 // Component 2: D (isolated)
535 auto d = add_node(g, 4);
536 (void)d;
537
538 // Component 3: E -> F
539 auto e = add_node(g, 5);
540 auto f = add_node(g, 6);
541 add_arc(g, e, f);
542
544
545 // Should have 4 SCCs: {A,B,C}, {D}, {E}, {F}
546 auto sccs = tarjan.connected_components(g);
547 EXPECT_EQ(sccs.size(), 4u);
548
549 // Check sizes
551 tarjan.connected_components(g, sizes);
552 EXPECT_EQ(sizes.size(), 4u);
553
554 // One SCC has size 3, others have size 1
555 size_t count_3 = 0, count_1 = 0;
556 for (auto sz : sizes)
557 {
558 if (sz == 3)
559 count_3++;
560 else if (sz == 1)
561 count_1++;
562 }
563 EXPECT_EQ(count_3, 1u);
564 EXPECT_EQ(count_1, 3u);
565
566 // Has cycle (in A-B-C)
567 EXPECT_TRUE(tarjan.has_cycle(g));
568
569 // Not strongly connected
570 EXPECT_FALSE(tarjan.test_connectivity(g));
571}
572
573// ============================================================================
574// Test: Large cycle
575// ============================================================================
577{
578 TestDigraph g;
579 const size_t N = 100;
580
582 for (size_t i = 0; i < N; ++i)
583 nodes.append(add_node(g, static_cast<int>(i)));
584
585 // Create a cycle: 0 -> 1 -> 2 -> ... -> 99 -> 0
586 auto it = nodes.get_it();
587 auto prev = it.get_curr();
588 auto first = prev;
589 for (it.next(); it.has_curr(); it.next())
590 {
591 auto curr = it.get_curr();
592 add_arc(g, prev, curr);
593 prev = curr;
594 }
595 add_arc(g, prev, first); // close the cycle
596
598
599 // One SCC with all N nodes
600 auto sccs = tarjan.connected_components(g);
601 EXPECT_EQ(sccs.size(), 1u);
602 EXPECT_EQ(sccs.get_first().size(), N);
603
604 EXPECT_TRUE(tarjan.has_cycle(g));
605 EXPECT_TRUE(tarjan.test_connectivity(g));
606
607 Path<TestDigraph> path(g);
608 EXPECT_TRUE(tarjan.compute_cycle(g, path));
609}
610
611// ============================================================================
612// Test: Graph with only inter-SCC arcs (tree-like structure of SCCs)
613// ============================================================================
615{
616 TestDigraph g;
617 // SCC A: a1 -> a2 -> a1
618 auto a1 = add_node(g, 1);
619 auto a2 = add_node(g, 2);
620 add_arc(g, a1, a2);
621 add_arc(g, a2, a1);
622
623 // SCC B: b1 -> b2 -> b1
624 auto b1 = add_node(g, 3);
625 auto b2 = add_node(g, 4);
626 add_arc(g, b1, b2);
627 add_arc(g, b2, b1);
628
629 // SCC C: c1 -> c2 -> c1
630 auto c1 = add_node(g, 5);
631 auto c2 = add_node(g, 6);
632 add_arc(g, c1, c2);
633 add_arc(g, c2, c1);
634
635 // Tree structure: A -> B, A -> C
636 add_arc(g, a1, b1);
637 add_arc(g, a2, c1);
638
640
641 // 3 SCCs
642 auto sccs = tarjan.connected_components(g);
643 EXPECT_EQ(sccs.size(), 3u);
644
645 // Each SCC has 2 nodes
646 for (auto &scc : sccs)
647 EXPECT_EQ(scc.size(), 2u);
648
649 // Has cycle (within each SCC)
650 EXPECT_TRUE(tarjan.has_cycle(g));
651
652 // Not strongly connected
653 EXPECT_FALSE(tarjan.test_connectivity(g));
654}
655
656// ============================================================================
657// Test: num_connected_components convenience method
658// ============================================================================
660{
661 TestDigraph g;
662
663 // Empty graph
665 EXPECT_EQ(tarjan1.num_connected_components(g), 0u);
666
667 // Single node
668 auto a = add_node(g, 1);
670 EXPECT_EQ(tarjan2.num_connected_components(g), 1u);
671
672 // Two nodes, no connection = 2 SCCs
673 auto b = add_node(g, 2);
675 EXPECT_EQ(tarjan3.num_connected_components(g), 2u);
676
677 // Add bidirectional connection = 1 SCC
678 add_arc(g, a, b);
679 add_arc(g, b, a);
681 EXPECT_EQ(tarjan4.num_connected_components(g), 1u);
682}
683
684// ============================================================================
685// Test: Null source node validation
686// ============================================================================
688{
689 TestDigraph g;
690 add_node(g, 1);
691
693 Path<TestDigraph> path(g);
694
695 EXPECT_THROW((void) tarjan.compute_cycle(g, nullptr, path), std::domain_error);
696}
697
698// ============================================================================
699// Test: Filter accessor
700// ============================================================================
702{
704
705 // Just verify the methods compile and return references
706 [[maybe_unused]] auto &filter = tarjan.get_filter();
707
709 [[maybe_unused]] const auto &const_filter = const_tarjan.get_filter();
710}
711
712// ============================================================================
713// Test: State getters
714// ============================================================================
716{
717 TestDigraph g;
718 auto a = add_node(g, 1);
719 auto b = add_node(g, 2);
720 add_arc(g, a, b);
721 add_arc(g, b, a);
722
724
725 // Before computation
726 EXPECT_FALSE(tarjan.has_computation());
727 EXPECT_EQ(tarjan.get_graph(), nullptr);
728
729 // After computation
730 auto sccs = tarjan.connected_components(g);
731 EXPECT_TRUE(tarjan.has_computation());
732 EXPECT_EQ(tarjan.get_graph(), &g);
733}
734
735// ============================================================================
736// Test: Move semantics
737// ============================================================================
739{
740 TestDigraph g;
741 auto a = add_node(g, 1);
742 auto b = add_node(g, 2);
743 add_arc(g, a, b);
744 add_arc(g, b, a);
745
747 auto sccs1 = tarjan1.connected_components(g);
748 EXPECT_EQ(sccs1.size(), 1u);
749
750 // Move constructor
752 auto sccs2 = tarjan2.connected_components(g);
753 EXPECT_EQ(sccs2.size(), 1u);
754
755 // Move assignment
757 tarjan3 = std::move(tarjan2);
758 auto sccs3 = tarjan3.connected_components(g);
759 EXPECT_EQ(sccs3.size(), 1u);
760}
761
762// ============================================================================
763// Test: Custom arc filter
764// ============================================================================
766{
767 bool operator()(TestDigraph::Arc *a) const noexcept
768 {
769 return a->get_info() != 0;
770 }
771};
772
774{
775 TestDigraph g;
776 auto a = add_node(g, 1);
777 auto b = add_node(g, 2);
778 auto c = add_node(g, 3);
779
780 // Create cycle a -> b -> c -> a, but with weight 0 on c -> a
781 add_arc(g, a, b, 1);
782 add_arc(g, b, c, 1);
783 add_arc(g, c, a, 0); // This arc will be filtered out
784
785 // Without filter: 1 SCC
787 EXPECT_EQ(tarjan1.num_connected_components(g), 1u);
788
789 // With filter: 3 SCCs (c -> a is filtered, breaking the cycle)
791 EXPECT_EQ(tarjan2.num_connected_components(g), 3u);
792}
793
794// ============================================================================
795// Test: Stress test with large graph
796// ============================================================================
798{
799 TestDigraph g;
800 const size_t N = 1000;
801
802 // Create N nodes
803 std::vector<TestDigraph::Node*> nodes(N);
804 for (size_t i = 0; i < N; ++i)
805 nodes[i] = add_node(g, static_cast<int>(i));
806
807 // Create a single large cycle
808 for (size_t i = 0; i < N; ++i)
809 add_arc(g, nodes[i], nodes[(i + 1) % N]);
810
812
813 // Should be 1 SCC
814 EXPECT_EQ(tarjan.num_connected_components(g), 1u);
815
816 // Should be strongly connected
817 EXPECT_TRUE(tarjan.test_connectivity(g));
818
819 // Should have a cycle
820 EXPECT_TRUE(tarjan.has_cycle(g));
821}
822
823// ============================================================================
824// Test: Multiple disjoint cycles
825// ============================================================================
827{
828 TestDigraph g;
829 const size_t NUM_CYCLES = 10;
830 const size_t CYCLE_SIZE = 5;
831
832 std::vector<TestDigraph::Node*> first_nodes;
833
834 for (size_t c = 0; c < NUM_CYCLES; ++c)
835 {
836 std::vector<TestDigraph::Node*> cycle_nodes(CYCLE_SIZE);
837 for (size_t i = 0; i < CYCLE_SIZE; ++i)
838 cycle_nodes[i] = add_node(g, static_cast<int>(c * CYCLE_SIZE + i));
839
840 first_nodes.push_back(cycle_nodes[0]);
841
842 for (size_t i = 0; i < CYCLE_SIZE; ++i)
843 add_arc(g, cycle_nodes[i], cycle_nodes[(i + 1) % CYCLE_SIZE]);
844 }
845
847
848 // Should have NUM_CYCLES SCCs
849 EXPECT_EQ(tarjan.num_connected_components(g), NUM_CYCLES);
850
851 // Should have cycles
852 EXPECT_TRUE(tarjan.has_cycle(g));
853
854 // Verify each cycle is an SCC
856 tarjan.connected_components(g, sizes);
857 for (auto sz : sizes)
859}
860
861// ============================================================================
862// Test: Path cycle verification
863// ============================================================================
865{
866 TestDigraph g;
867 auto a = add_node(g, 1);
868 auto b = add_node(g, 2);
869 auto c = add_node(g, 3);
870 add_arc(g, a, b);
871 add_arc(g, b, c);
872 add_arc(g, c, a);
873
875 Path<TestDigraph> path(g);
876 bool found = tarjan.compute_cycle(g, path);
877
879 ASSERT_FALSE(path.is_empty());
880
881 // Verify cycle property: first node == last node
882 EXPECT_EQ(path.get_first_node(), path.get_last_node());
883
884 // Verify all nodes in path are connected
885 size_t path_len = 0;
886 for (Path<TestDigraph>::Iterator it(path); it.has_curr(); it.next())
887 ++path_len;
888
889 // Path should have at least 2 nodes for a valid cycle (start + end being same)
890 EXPECT_GE(path_len, 2u);
891}
892
893// ============================================================================
894// Test: Complete digraph (all nodes connected to all others)
895// ============================================================================
897{
898 TestDigraph g;
899 const size_t N = 10;
900
901 std::vector<TestDigraph::Node*> nodes(N);
902 for (size_t i = 0; i < N; ++i)
903 nodes[i] = add_node(g, static_cast<int>(i));
904
905 // Create complete digraph
906 for (size_t i = 0; i < N; ++i)
907 for (size_t j = 0; j < N; ++j)
908 if (i != j)
909 add_arc(g, nodes[i], nodes[j]);
910
912
913 // All nodes in one SCC
914 EXPECT_EQ(tarjan.num_connected_components(g), 1u);
915 EXPECT_TRUE(tarjan.test_connectivity(g));
916 EXPECT_TRUE(tarjan.has_cycle(g));
917}
918
919// ============================================================================
920// Test: Reusing Tarjan instance for multiple graphs
921// ============================================================================
923{
925
926 // Graph 1: simple cycle
928 auto a1 = add_node(g1, 1);
929 auto b1 = add_node(g1, 2);
930 add_arc(g1, a1, b1);
931 add_arc(g1, b1, a1);
932
933 EXPECT_EQ(tarjan.num_connected_components(g1), 1u);
934 EXPECT_TRUE(tarjan.has_cycle(g1));
935
936 // Graph 2: DAG
938 auto a2 = add_node(g2, 1);
939 auto b2 = add_node(g2, 2);
940 auto c2 = add_node(g2, 3);
941 add_arc(g2, a2, b2);
942 add_arc(g2, b2, c2);
943
944 EXPECT_EQ(tarjan.num_connected_components(g2), 3u);
945 EXPECT_FALSE(tarjan.has_cycle(g2));
946 EXPECT_TRUE(tarjan.is_dag(g2));
947}
948
949// ============================================================================
950// EXTENDED EXHAUSTIVE TESTS
951// ============================================================================
952
953// ============================================================================
954// Test: Star graph (hub with many spokes) - outward
955// ============================================================================
957{
958 TestDigraph g;
959 const size_t N = 50;
960
961 auto hub = add_node(g, 0);
962 for (size_t i = 1; i <= N; ++i)
963 {
964 auto spoke = add_node(g, static_cast<int>(i));
965 add_arc(g, hub, spoke); // hub -> spoke
966 }
967
969
970 // N+1 SCCs (each node is its own SCC)
971 EXPECT_EQ(tarjan.num_connected_components(g), N + 1);
972 EXPECT_FALSE(tarjan.has_cycle(g));
973 EXPECT_TRUE(tarjan.is_dag(g));
974 EXPECT_FALSE(tarjan.test_connectivity(g));
975}
976
977// ============================================================================
978// Test: Star graph (hub with many spokes) - inward
979// ============================================================================
981{
982 TestDigraph g;
983 const size_t N = 50;
984
985 auto hub = add_node(g, 0);
986 for (size_t i = 1; i <= N; ++i)
987 {
988 auto spoke = add_node(g, static_cast<int>(i));
989 add_arc(g, spoke, hub); // spoke -> hub
990 }
991
993
994 EXPECT_EQ(tarjan.num_connected_components(g), N + 1);
995 EXPECT_FALSE(tarjan.has_cycle(g));
996 EXPECT_TRUE(tarjan.is_dag(g));
997}
998
999// ============================================================================
1000// Test: Wheel graph (cycle with hub connected to all)
1001// ============================================================================
1003{
1004 TestDigraph g;
1005 const size_t N = 20;
1006
1007 auto hub = add_node(g, 0);
1008 std::vector<TestDigraph::Node *> rim(N);
1009
1010 for (size_t i = 0; i < N; ++i)
1011 {
1012 rim[i] = add_node(g, static_cast<int>(i + 1));
1013 add_arc(g, hub, rim[i]); // hub -> rim
1014 add_arc(g, rim[i], hub); // rim -> hub (bidirectional)
1015 }
1016
1017 // Create rim cycle
1018 for (size_t i = 0; i < N; ++i)
1019 add_arc(g, rim[i], rim[(i + 1) % N]);
1020
1022
1023 // All nodes should be in one SCC
1024 EXPECT_EQ(tarjan.num_connected_components(g), 1u);
1025 EXPECT_TRUE(tarjan.has_cycle(g));
1026 EXPECT_TRUE(tarjan.test_connectivity(g));
1027}
1028
1029// ============================================================================
1030// Test: Binary tree DAG
1031// ============================================================================
1033{
1034 TestDigraph g;
1035 const size_t DEPTH = 6; // 2^6 - 1 = 63 nodes
1036
1037 std::vector<TestDigraph::Node *> nodes;
1038 nodes.push_back(add_node(g, 0)); // root
1039
1040 for (size_t level = 0; level < DEPTH - 1; ++level)
1041 {
1042 size_t start = (1 << level) - 1;
1043 size_t end = (1 << (level + 1)) - 1;
1044
1045 for (size_t i = start; i < end && i < nodes.size(); ++i)
1046 {
1047 auto left = add_node(g, static_cast<int>(nodes.size()));
1048 nodes.push_back(left);
1049 add_arc(g, nodes[i], left);
1050
1051 auto right = add_node(g, static_cast<int>(nodes.size()));
1052 nodes.push_back(right);
1053 add_arc(g, nodes[i], right);
1054 }
1055 }
1056
1058
1059 // Each node is its own SCC (DAG)
1060 EXPECT_EQ(tarjan.num_connected_components(g), nodes.size());
1061 EXPECT_FALSE(tarjan.has_cycle(g));
1062 EXPECT_TRUE(tarjan.is_dag(g));
1063}
1064
1065// ============================================================================
1066// Test: Multiple self-loops
1067// ============================================================================
1069{
1070 TestDigraph g;
1071 const size_t N = 20;
1072
1073 for (size_t i = 0; i < N; ++i)
1074 {
1075 auto n = add_node(g, static_cast<int>(i));
1076 add_arc(g, n, n); // self-loop
1077 }
1078
1080
1081 // Each node is its own SCC (but each has a cycle)
1082 EXPECT_EQ(tarjan.num_connected_components(g), N);
1083 EXPECT_TRUE(tarjan.has_cycle(g));
1084 EXPECT_FALSE(tarjan.is_dag(g));
1085 EXPECT_FALSE(tarjan.test_connectivity(g));
1086}
1087
1088// ============================================================================
1089// Test: Chain of SCCs
1090// ============================================================================
1092{
1093 TestDigraph g;
1094 const size_t NUM_SCCS = 10;
1095 const size_t SCC_SIZE = 3;
1096
1097 std::vector<TestDigraph::Node *> first_of_scc;
1098
1099 for (size_t s = 0; s < NUM_SCCS; ++s)
1100 {
1101 std::vector<TestDigraph::Node *> scc_nodes(SCC_SIZE);
1102 for (size_t i = 0; i < SCC_SIZE; ++i)
1103 scc_nodes[i] = add_node(g, static_cast<int>(s * SCC_SIZE + i));
1104
1105 first_of_scc.push_back(scc_nodes[0]);
1106
1107 // Create cycle within SCC
1108 for (size_t i = 0; i < SCC_SIZE; ++i)
1109 add_arc(g, scc_nodes[i], scc_nodes[(i + 1) % SCC_SIZE]);
1110
1111 // Connect to previous SCC
1112 if (s > 0)
1113 add_arc(g, first_of_scc[s - 1], scc_nodes[0]);
1114 }
1115
1117
1118 EXPECT_EQ(tarjan.num_connected_components(g), NUM_SCCS);
1119 EXPECT_TRUE(tarjan.has_cycle(g));
1120 EXPECT_FALSE(tarjan.test_connectivity(g));
1121
1123 tarjan.connected_components(g, sizes);
1124 for (auto sz : sizes)
1125 EXPECT_EQ(sz, SCC_SIZE);
1126}
1127
1128// ============================================================================
1129// Test: Diamond pattern SCCs
1130// ============================================================================
1132{
1133 TestDigraph g;
1134
1135 // Top SCC
1136 auto t1 = add_node(g, 1);
1137 auto t2 = add_node(g, 2);
1138 add_arc(g, t1, t2);
1139 add_arc(g, t2, t1);
1140
1141 // Left SCC
1142 auto l1 = add_node(g, 3);
1143 auto l2 = add_node(g, 4);
1144 add_arc(g, l1, l2);
1145 add_arc(g, l2, l1);
1146
1147 // Right SCC
1148 auto r1 = add_node(g, 5);
1149 auto r2 = add_node(g, 6);
1150 add_arc(g, r1, r2);
1151 add_arc(g, r2, r1);
1152
1153 // Bottom SCC
1154 auto b1 = add_node(g, 7);
1155 auto b2 = add_node(g, 8);
1156 add_arc(g, b1, b2);
1157 add_arc(g, b2, b1);
1158
1159 // Connect in diamond pattern
1160 add_arc(g, t1, l1);
1161 add_arc(g, t2, r1);
1162 add_arc(g, l2, b1);
1163 add_arc(g, r2, b2);
1164
1166
1167 EXPECT_EQ(tarjan.num_connected_components(g), 4u);
1168 EXPECT_TRUE(tarjan.has_cycle(g));
1169 EXPECT_FALSE(tarjan.test_connectivity(g));
1170}
1171
1172// ============================================================================
1173// Test: Bipartite-like structure (no cycles)
1174// ============================================================================
1176{
1177 TestDigraph g;
1178 const size_t LEFT_SIZE = 10;
1179 const size_t RIGHT_SIZE = 10;
1180
1181 std::vector<TestDigraph::Node *> left(LEFT_SIZE);
1182 std::vector<TestDigraph::Node *> right(RIGHT_SIZE);
1183
1184 for (size_t i = 0; i < LEFT_SIZE; ++i)
1185 left[i] = add_node(g, static_cast<int>(i));
1186
1187 for (size_t i = 0; i < RIGHT_SIZE; ++i)
1188 right[i] = add_node(g, static_cast<int>(LEFT_SIZE + i));
1189
1190 // All left -> all right
1191 for (size_t i = 0; i < LEFT_SIZE; ++i)
1192 for (size_t j = 0; j < RIGHT_SIZE; ++j)
1193 add_arc(g, left[i], right[j]);
1194
1196
1197 EXPECT_EQ(tarjan.num_connected_components(g), LEFT_SIZE + RIGHT_SIZE);
1198 EXPECT_FALSE(tarjan.has_cycle(g));
1199 EXPECT_TRUE(tarjan.is_dag(g));
1200}
1201
1202// ============================================================================
1203// Test: Strongly connected bipartite (all edges both ways)
1204// ============================================================================
1206{
1207 TestDigraph g;
1208 const size_t LEFT_SIZE = 5;
1209 const size_t RIGHT_SIZE = 5;
1210
1211 std::vector<TestDigraph::Node *> left(LEFT_SIZE);
1212 std::vector<TestDigraph::Node *> right(RIGHT_SIZE);
1213
1214 for (size_t i = 0; i < LEFT_SIZE; ++i)
1215 left[i] = add_node(g, static_cast<int>(i));
1216
1217 for (size_t i = 0; i < RIGHT_SIZE; ++i)
1218 right[i] = add_node(g, static_cast<int>(LEFT_SIZE + i));
1219
1220 // All left <-> all right (bidirectional)
1221 for (size_t i = 0; i < LEFT_SIZE; ++i)
1222 for (size_t j = 0; j < RIGHT_SIZE; ++j)
1223 {
1224 add_arc(g, left[i], right[j]);
1225 add_arc(g, right[j], left[i]);
1226 }
1227
1229
1230 EXPECT_EQ(tarjan.num_connected_components(g), 1u);
1231 EXPECT_TRUE(tarjan.has_cycle(g));
1232 EXPECT_TRUE(tarjan.test_connectivity(g));
1233}
1234
1235// ============================================================================
1236// Test: Grid graph (no cycles, DAG)
1237// ============================================================================
1239{
1240 TestDigraph g;
1241 const size_t ROWS = 5;
1242 const size_t COLS = 5;
1243
1244 std::vector<std::vector<TestDigraph::Node *>> grid(ROWS,
1245 std::vector<TestDigraph::Node *>(COLS));
1246
1247 for (size_t r = 0; r < ROWS; ++r)
1248 for (size_t c = 0; c < COLS; ++c)
1249 grid[r][c] = add_node(g, static_cast<int>(r * COLS + c));
1250
1251 // Connect right and down only (DAG)
1252 for (size_t r = 0; r < ROWS; ++r)
1253 for (size_t c = 0; c < COLS; ++c)
1254 {
1255 if (c + 1 < COLS)
1256 add_arc(g, grid[r][c], grid[r][c + 1]);
1257 if (r + 1 < ROWS)
1258 add_arc(g, grid[r][c], grid[r + 1][c]);
1259 }
1260
1262
1263 EXPECT_EQ(tarjan.num_connected_components(g), ROWS * COLS);
1264 EXPECT_FALSE(tarjan.has_cycle(g));
1265 EXPECT_TRUE(tarjan.is_dag(g));
1266}
1267
1268// ============================================================================
1269// Test: Grid graph with back edges (cycles)
1270// ============================================================================
1272{
1273 TestDigraph g;
1274 const size_t ROWS = 4;
1275 const size_t COLS = 4;
1276
1277 std::vector<std::vector<TestDigraph::Node *>> grid(ROWS,
1278 std::vector<TestDigraph::Node *>(COLS));
1279
1280 for (size_t r = 0; r < ROWS; ++r)
1281 for (size_t c = 0; c < COLS; ++c)
1282 grid[r][c] = add_node(g, static_cast<int>(r * COLS + c));
1283
1284 // Connect in all 4 directions (bidirectional)
1285 for (size_t r = 0; r < ROWS; ++r)
1286 for (size_t c = 0; c < COLS; ++c)
1287 {
1288 if (c + 1 < COLS)
1289 {
1290 add_arc(g, grid[r][c], grid[r][c + 1]);
1291 add_arc(g, grid[r][c + 1], grid[r][c]);
1292 }
1293 if (r + 1 < ROWS)
1294 {
1295 add_arc(g, grid[r][c], grid[r + 1][c]);
1296 add_arc(g, grid[r + 1][c], grid[r][c]);
1297 }
1298 }
1299
1301
1302 // All nodes in one SCC
1303 EXPECT_EQ(tarjan.num_connected_components(g), 1u);
1304 EXPECT_TRUE(tarjan.has_cycle(g));
1305 EXPECT_TRUE(tarjan.test_connectivity(g));
1306}
1307
1308// ============================================================================
1309// Test: Very deep linear chain (stress test for recursion)
1310// ============================================================================
1312{
1313 TestDigraph g;
1314 const size_t DEPTH = 500; // Deep enough to stress but not overflow
1315
1316 std::vector<TestDigraph::Node *> nodes(DEPTH);
1317 for (size_t i = 0; i < DEPTH; ++i)
1318 nodes[i] = add_node(g, static_cast<int>(i));
1319
1320 for (size_t i = 0; i + 1 < DEPTH; ++i)
1321 add_arc(g, nodes[i], nodes[i + 1]);
1322
1324
1325 EXPECT_EQ(tarjan.num_connected_components(g), DEPTH);
1326 EXPECT_FALSE(tarjan.has_cycle(g));
1327 EXPECT_TRUE(tarjan.is_dag(g));
1328}
1329
1330// ============================================================================
1331// Test: Consistency between different connected_components overloads
1332// ============================================================================
1334{
1335 TestDigraph g;
1336
1337 // Create a complex graph
1338 auto a = add_node(g, 1);
1339 auto b = add_node(g, 2);
1340 auto c = add_node(g, 3);
1341 auto d = add_node(g, 4);
1342 auto e = add_node(g, 5);
1343
1344 // SCC1: {a, b, c}
1345 add_arc(g, a, b);
1346 add_arc(g, b, c);
1347 add_arc(g, c, a);
1348
1349 // SCC2: {d, e}
1350 add_arc(g, d, e);
1351 add_arc(g, e, d);
1352
1353 // Inter-SCC arcs
1354 add_arc(g, b, d);
1355
1357
1358 // Method 1: Get sizes
1360 tarjan.connected_components(g, sizes);
1361
1362 // Method 2: Get node lists
1363 auto node_lists = tarjan.connected_components(g);
1364
1365 // Method 3: Get blocks with arcs
1366 DynList<TestDigraph> blocks;
1368 tarjan.connected_components(g, blocks, inter_arcs);
1369
1370 // All should report same number of SCCs
1372 EXPECT_EQ(sizes.size(), blocks.size());
1373 EXPECT_EQ(sizes.size(), 2u);
1374
1375 // Sizes should match
1376 size_t total_from_sizes = 0;
1377 for (auto sz : sizes)
1378 total_from_sizes += sz;
1379
1380 size_t total_from_lists = 0;
1381 for (auto &list : node_lists)
1382 total_from_lists += list.size();
1383
1384 size_t total_from_blocks = 0;
1385 for (auto &block : blocks)
1386 total_from_blocks += block.get_num_nodes();
1387
1391
1392 // Inter-SCC arcs
1393 EXPECT_EQ(inter_arcs.size(), 1u);
1394}
1395
1396// ============================================================================
1397// Test: Cycle detection vs SCC count consistency
1398// ============================================================================
1400{
1401 // A graph has a cycle iff some SCC has size > 1 OR some node has self-loop
1402 TestDigraph g;
1403
1404 // 10 isolated nodes (no cycles)
1405 for (int i = 0; i < 10; ++i)
1406 add_node(g, i);
1407
1409
1410 EXPECT_FALSE(tarjan.has_cycle(g));
1411 EXPECT_EQ(tarjan.num_connected_components(g), 10u);
1412
1413 // All SCCs should have size 1
1415 tarjan.connected_components(g, sizes);
1416 for (auto sz : sizes)
1417 EXPECT_EQ(sz, 1u);
1418}
1419
1420// ============================================================================
1421// Test: Very large strongly connected component
1422// ============================================================================
1424{
1425 TestDigraph g;
1426 const size_t N = 2000;
1427
1428 std::vector<TestDigraph::Node *> nodes(N);
1429 for (size_t i = 0; i < N; ++i)
1430 nodes[i] = add_node(g, static_cast<int>(i));
1431
1432 // Create cycle: 0 -> 1 -> 2 -> ... -> N-1 -> 0
1433 for (size_t i = 0; i < N; ++i)
1434 add_arc(g, nodes[i], nodes[(i + 1) % N]);
1435
1436 // Add some random cross-edges to make it more connected
1437 for (size_t i = 0; i < N; i += 7)
1438 add_arc(g, nodes[i], nodes[(i + N / 3) % N]);
1439
1441
1442 // All nodes in one SCC
1443 EXPECT_EQ(tarjan.num_connected_components(g), 1u);
1444 EXPECT_TRUE(tarjan.has_cycle(g));
1445 EXPECT_TRUE(tarjan.test_connectivity(g));
1446
1447 // Verify size
1449 tarjan.connected_components(g, sizes);
1450 EXPECT_EQ(sizes.size(), 1u);
1452}
1453
1454// ============================================================================
1455// Test: Many small SCCs
1456// ============================================================================
1458{
1459 TestDigraph g;
1460 const size_t NUM_SCCS = 500;
1461
1462 for (size_t i = 0; i < NUM_SCCS; ++i)
1463 {
1464 auto a = add_node(g, static_cast<int>(i * 2));
1465 auto b = add_node(g, static_cast<int>(i * 2 + 1));
1466 add_arc(g, a, b);
1467 add_arc(g, b, a);
1468 }
1469
1471
1472 EXPECT_EQ(tarjan.num_connected_components(g), NUM_SCCS);
1473 EXPECT_TRUE(tarjan.has_cycle(g));
1474
1476 tarjan.connected_components(g, sizes);
1477 for (auto sz : sizes)
1478 EXPECT_EQ(sz, 2u);
1479}
1480
1481// ============================================================================
1482// Test: Path finding in cycle
1483// ============================================================================
1485{
1486 TestDigraph g;
1487 auto a = add_node(g, 1);
1488 auto b = add_node(g, 2);
1489 auto c = add_node(g, 3);
1490 auto d = add_node(g, 4);
1491
1492 add_arc(g, a, b);
1493 add_arc(g, b, c);
1494 add_arc(g, c, d);
1495 add_arc(g, d, b); // cycle: b -> c -> d -> b
1496
1498
1499 Path<TestDigraph> path(g);
1500 bool found = tarjan.compute_cycle(g, path);
1501
1503
1504 // Verify cycle: first == last
1505 EXPECT_EQ(path.get_first_node(), path.get_last_node());
1506
1507 // Verify path length (at least 2 nodes for a cycle representation)
1508 size_t len = 0;
1509 for (Path<TestDigraph>::Iterator it(path); it.has_curr(); it.next())
1510 ++len;
1511 EXPECT_GE(len, 2u);
1512}
1513
1514// ============================================================================
1515// Test: Empty graph edge cases
1516// ============================================================================
1518{
1519 TestDigraph g;
1521
1522 // Multiple calls on empty graph should all work
1523 EXPECT_EQ(tarjan.num_connected_components(g), 0u);
1524 EXPECT_FALSE(tarjan.has_cycle(g));
1525 EXPECT_TRUE(tarjan.is_dag(g));
1526 EXPECT_TRUE(tarjan.test_connectivity(g));
1527
1529 tarjan.connected_components(g, node_lists);
1531
1533 tarjan.connected_components(g, sizes);
1535}
1536
1537// ============================================================================
1538// Test: Graph with parallel arcs (multi-edges)
1539// ============================================================================
1541{
1542 TestDigraph g;
1543 auto a = add_node(g, 1);
1544 auto b = add_node(g, 2);
1545
1546 // Multiple arcs in same direction
1547 add_arc(g, a, b);
1548 add_arc(g, a, b);
1549 add_arc(g, a, b);
1550
1552
1553 // Still 2 SCCs (no back edge)
1554 EXPECT_EQ(tarjan.num_connected_components(g), 2u);
1555 EXPECT_FALSE(tarjan.has_cycle(g));
1556
1557 // Add back edges
1558 add_arc(g, b, a);
1559 add_arc(g, b, a);
1560
1562 EXPECT_EQ(tarjan2.num_connected_components(g), 1u);
1563 EXPECT_TRUE(tarjan2.has_cycle(g));
1564}
1565
1566// ============================================================================
1567// Test: Tournament graph (complete asymmetric digraph)
1568// ============================================================================
1570{
1571 TestDigraph g;
1572 const size_t N = 8;
1573
1574 std::vector<TestDigraph::Node *> nodes(N);
1575 for (size_t i = 0; i < N; ++i)
1576 nodes[i] = add_node(g, static_cast<int>(i));
1577
1578 // Tournament: for each pair (i,j) with i < j, add exactly one arc
1579 // Direction based on some pattern
1580 for (size_t i = 0; i < N; ++i)
1581 for (size_t j = i + 1; j < N; ++j)
1582 {
1583 if ((i + j) % 2 == 0)
1584 add_arc(g, nodes[i], nodes[j]);
1585 else
1586 add_arc(g, nodes[j], nodes[i]);
1587 }
1588
1590
1591 // Tournament graphs are interesting - may or may not be strongly connected
1592 // Just verify the algorithm completes successfully
1593 size_t num_sccs = tarjan.num_connected_components(g);
1594 EXPECT_GE(num_sccs, 1u);
1596}
1597
1598// ============================================================================
1599// Test: Dense random-like graph
1600// ============================================================================
1602{
1603 TestDigraph g;
1604 const size_t N = 50;
1605
1606 std::vector<TestDigraph::Node *> nodes(N);
1607 for (size_t i = 0; i < N; ++i)
1608 nodes[i] = add_node(g, static_cast<int>(i));
1609
1610 // Add many edges based on a pattern
1611 for (size_t i = 0; i < N; ++i)
1612 for (size_t j = 0; j < N; ++j)
1613 if (i != j && (i * 7 + j * 3) % 5 < 3) // ~60% edge density
1614 add_arc(g, nodes[i], nodes[j]);
1615
1617
1618 // Just verify it completes and gives consistent results
1619 auto sccs = tarjan.connected_components(g);
1620 size_t total = 0;
1621 for (auto &scc : sccs)
1622 total += scc.size();
1623 EXPECT_EQ(total, N);
1624}
1625
1626// ============================================================================
1627// Test: Compute_Cycle_In_Digraph with no cycle
1628// ============================================================================
1630{
1631 TestDigraph g;
1632 auto a = add_node(g, 1);
1633 auto b = add_node(g, 2);
1634 auto c = add_node(g, 3);
1635 add_arc(g, a, b);
1636 add_arc(g, b, c);
1637
1639
1640 Path<TestDigraph> path(g);
1641 bool found = finder(g, path);
1643
1644 auto path2 = finder(g);
1646
1647 auto path3 = finder(g, a);
1649}
Tarjan's algorithm for strongly connected components.
Determines if a digraph contains a cycle and constructs it.
Definition Tarjan.H:1021
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
Dynamic doubly linked list with O(1) size and bidirectional access.
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
T & get_first() const
Return the first item of the list.
Definition htlist.H:1675
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
Iterator on nodes and arcs of a path.
Definition tpl_graph.H:3201
Path on a graph.
Definition tpl_graph.H:2669
Node * get_first_node() const
Return the first node of path; throws overflow_error if path is empty.
Definition tpl_graph.H:3125
bool is_empty() const noexcept
Return true if the path is empty.
Definition tpl_graph.H:2813
Node * get_last_node() const
Return the last node of path; throws overflow_error if path is empty.
Definition tpl_graph.H:3132
Computes strongly connected components (SCCs) in a directed graph using Tarjan's algorithm.
Definition Tarjan.H:171
#define TEST(name)
#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
Container2< typename Container1::Item_Type > filter(Container1 &container, Operation &operation)
Filter elements that satisfy operation.
DynList< T > maps(const C &c, Op op)
Classic map operation.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
bool operator()(TestDigraph::Arc *a) const noexcept
TestDigraph::Arc * add_arc(TestDigraph &g, TestDigraph::Node *src, TestDigraph::Node *tgt, int value=0)
TestDigraph::Node * add_node(TestDigraph &g, int value)
Generic graph and digraph implementations.