Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
cut_nodes_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
38#include <gtest/gtest.h>
39
40#include <initializer_list>
41#include <tpl_array.H>
42#include <tpl_cut_nodes.H>
43#include <tpl_dynSetTree.H>
44#include <tpl_graph.H>
45#include <tpl_graph_utils.H>
46
47using namespace Aleph;
48
49namespace
50{
52
53 Array<Graph::Node *> make_nodes(Graph & g, int n)
54 {
55 auto nodes = Array<Graph::Node *>::create(static_cast<size_t>(n));
56 for (int i = 0; i < n; ++i)
57 nodes(static_cast<size_t>(i)) = g.insert_node(i);
58 return nodes;
59 }
60
62 {
64 for (typename DynDlist<Graph::Node *>::Iterator it(list); it.has_curr(); it.next_ne())
65 s.insert(it.get_curr()->get_info());
66 return s;
67 }
68
69 bool cut_infos_eq(const DynDlist<Graph::Node *> & list,
70 std::initializer_list<int> expected)
71 {
72 const auto got = cut_infos(list);
73 if (got.size() != expected.size())
74 return false;
75 for (const int x : expected)
76 if (not got.exist(x))
78 return true;
79 }
80
81 size_t count_nodes_with_color(const Graph & g, long color)
82 {
83 size_t n = 0;
84 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
85 {
86 auto p = it.get_curr();
87 if (get_color<Graph>(p) == color)
88 ++n;
89 }
90 return n;
91 }
92
93 size_t count_arcs_with_color(const Graph & g, long color)
94 {
95 size_t n = 0;
96 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
97 {
98 auto a = it.get_curr();
99 if (get_color<Graph>(a) == color)
100 ++n;
101 }
102 return n;
103 }
104
105 size_t count_cut_arcs(const Graph & g)
106 {
107 size_t n = 0;
108 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
109 if (is_an_cut_arc<Graph>(it.get_curr()))
110 ++n;
111 return n;
112 }
113
114} // namespace
115
117{
118 Graph g;
119 auto nodes = make_nodes(g, 4);
120 g.insert_arc(nodes[0], nodes[1], 1);
121 g.insert_arc(nodes[1], nodes[2], 1);
122 g.insert_arc(nodes[2], nodes[3], 1);
123
126 alg(nodes[0], cuts);
127
128 EXPECT_TRUE(cut_infos_eq(cuts, {1, 2}));
129}
130
132{
133 Graph g;
134 auto nodes = make_nodes(g, 4);
135 g.insert_arc(nodes[0], nodes[1], 1);
136 g.insert_arc(nodes[1], nodes[2], 1);
137 g.insert_arc(nodes[2], nodes[3], 1);
138 g.insert_arc(nodes[3], nodes[0], 1);
139
142 alg(nodes[0], cuts);
143
144 EXPECT_TRUE(cuts.is_empty());
145}
146
148{
149 Graph g;
150 auto nodes = make_nodes(g, 6);
151 const int center = 0;
152 for (int i = 1; i < 6; ++i)
153 g.insert_arc(nodes[center], nodes[i], 1);
154
157 alg(nodes[center], cuts);
158
160}
161
163{
164 Graph g;
165 auto nodes = make_nodes(g, 2);
166 g.insert_arc(nodes[0], nodes[1], 1);
167
170
171 EXPECT_THROW((void)alg.paint_subgraphs(), std::logic_error);
172}
173
175{
176 Graph g;
177 auto nodes = make_nodes(g, 5);
178 for (int i = 1; i < 5; ++i)
179 g.insert_arc(nodes[0], nodes[i], 1);
180
183 alg(nodes[0], cuts);
184
185 const long next_color = alg.paint_subgraphs();
186 EXPECT_EQ(next_color, 5L); // 4 leaf blocks => colors 1..4, next=5
187
190 for (int i = 1; i < 5; ++i)
192
195 alg.map_cut_graph(cut_graph, cross);
196
197 EXPECT_EQ(cut_graph.get_num_nodes(), 1U);
198 EXPECT_EQ(cut_graph.get_num_arcs(), 0U);
199
200 EXPECT_EQ(cross.size(), g.get_num_arcs());
201 for (typename DynDlist<Graph::Arc *>::Iterator it(cross); it.has_curr(); it.next_ne())
202 EXPECT_TRUE(is_a_cross_arc<Graph>(it.get_curr()));
203}
204
206{
207 Graph g;
208 auto nodes = make_nodes(g, 5);
209 for (int i = 1; i < 5; ++i)
210 g.insert_arc(nodes[0], nodes[i], 1);
211
214 alg(nodes[0], cuts);
215 (void)alg.paint_subgraphs();
216
217 // In this star, colors 1..4 correspond to single-node blocks.
218 for (long color = 1; color <= 4; ++color)
219 {
220 Graph sg;
221 alg.map_subgraph(sg, color);
222
225 }
226}
227
229{
230 Graph g;
231 auto nodes = make_nodes(g, 4);
232 g.insert_arc(nodes[0], nodes[1], 1);
233 g.insert_arc(nodes[1], nodes[2], 1);
234 g.insert_arc(nodes[2], nodes[3], 1);
235
238 alg(nodes[0], cuts);
239 (void)alg.paint_subgraphs();
240
243 alg.map_cut_graph(cut_graph, cross);
244
245 EXPECT_EQ(cut_graph.get_num_nodes(), cuts.size());
246 EXPECT_EQ(cut_graph.get_num_arcs(), count_cut_arcs(g));
247}
248
249// ============================================================================
250// Additional Graph Topologies
251// ============================================================================
252
254{
255 Graph g;
256 auto nodes = make_nodes(g, 4);
257 // Complete graph K4
258 for (int i = 0; i < 4; ++i)
259 for (int j = i + 1; j < 4; ++j)
260 g.insert_arc(nodes[i], nodes[j], 1);
261
264 alg(nodes[0], cuts);
265
266 EXPECT_TRUE(cuts.is_empty());
267}
268
270{
271 Graph g;
272 auto nodes = make_nodes(g, 7);
273 /*
274 * 0
275 * / \
276 * 1 2
277 * /| |\
278 * 3 4 5 6
279 */
280 g.insert_arc(nodes[0], nodes[1], 1);
281 g.insert_arc(nodes[0], nodes[2], 1);
282 g.insert_arc(nodes[1], nodes[3], 1);
283 g.insert_arc(nodes[1], nodes[4], 1);
284 g.insert_arc(nodes[2], nodes[5], 1);
285 g.insert_arc(nodes[2], nodes[6], 1);
286
289 alg(nodes[0], cuts);
290
291 EXPECT_TRUE(cut_infos_eq(cuts, {0, 1, 2}));
292}
293
295{
296 Graph g;
297 auto nodes = make_nodes(g, 6);
298 // Two triangles connected by a bridge (nodes[2]--nodes[3])
299 g.insert_arc(nodes[0], nodes[1], 1);
300 g.insert_arc(nodes[1], nodes[2], 1);
301 g.insert_arc(nodes[2], nodes[0], 1);
302 g.insert_arc(nodes[2], nodes[3], 1); // bridge
303 g.insert_arc(nodes[3], nodes[4], 1);
304 g.insert_arc(nodes[4], nodes[5], 1);
305 g.insert_arc(nodes[5], nodes[3], 1);
306
309 alg(nodes[0], cuts);
310
311 EXPECT_TRUE(cut_infos_eq(cuts, {2, 3}));
312}
313
315{
316 Graph g;
317 auto nodes = make_nodes(g, 4);
318 // 4-cycle (biconnected)
319 g.insert_arc(nodes[0], nodes[1], 1);
320 g.insert_arc(nodes[1], nodes[2], 1);
321 g.insert_arc(nodes[2], nodes[3], 1);
322 g.insert_arc(nodes[3], nodes[0], 1);
323
326 alg(nodes[0], cuts);
327
328 EXPECT_TRUE(cuts.is_empty());
329}
330
332{
333 Graph g;
334 auto nodes = make_nodes(g, 5);
335 // Root connects two separate components
336 g.insert_arc(nodes[0], nodes[1], 1);
337 g.insert_arc(nodes[0], nodes[2], 1);
338 g.insert_arc(nodes[1], nodes[2], 1); // triangle
339 g.insert_arc(nodes[0], nodes[3], 1);
340 g.insert_arc(nodes[3], nodes[4], 1); // separate component
341
344 alg(nodes[0], cuts);
345
346 {
347 const auto infos = cut_infos(cuts);
348 EXPECT_TRUE(infos.exist(0));
349 EXPECT_TRUE(infos.exist(3));
350 }
351}
352
353// ============================================================================
354// State Machine Tests
355// ============================================================================
356
358{
359 Graph g;
360 auto nodes = make_nodes(g, 3);
361 g.insert_arc(nodes[0], nodes[1], 1);
362
365 alg(nodes[0], cuts);
366
367 Graph sg;
368 EXPECT_THROW(alg.map_subgraph(sg, 1L), std::logic_error);
369}
370
372{
373 Graph g;
374 auto nodes = make_nodes(g, 3);
375 g.insert_arc(nodes[0], nodes[1], 1);
376
379 alg(nodes[0], cuts);
380
381 Graph cg;
383 EXPECT_THROW(alg.map_cut_graph(cg, cross), std::logic_error);
384}
385
387{
388 Graph g;
389 auto nodes = make_nodes(g, 3);
390 g.insert_arc(nodes[0], nodes[1], 1);
391 g.insert_arc(nodes[1], nodes[2], 1);
392
395
396 // Step 1: compute cut nodes
397 alg(nodes[0], cuts);
398 EXPECT_FALSE(cuts.is_empty());
399
400 // Step 2: paint
401 long num_colors = alg.paint_subgraphs();
403
404 // Step 3: map subgraph (should not throw)
405 Graph sg;
406 EXPECT_NO_THROW(alg.map_subgraph(sg, 1L));
407}
408
410{
411 Graph g;
412 auto nodes = make_nodes(g, 3);
413 g.insert_arc(nodes[0], nodes[1], 1);
414 g.insert_arc(nodes[1], nodes[2], 1);
415
417
419 alg(nodes[0], cuts1);
420 size_t first_count = cuts1.size();
421
423 alg(nodes[0], cuts2);
424
425 EXPECT_EQ(cuts1.size(), cuts2.size());
426 EXPECT_EQ(first_count, cuts2.size());
427}
428
429// ============================================================================
430// Operator() Overload Tests
431// ============================================================================
432
434{
435 Graph g;
436 auto nodes = make_nodes(g, 4);
437 g.insert_arc(nodes[0], nodes[1], 1);
438 g.insert_arc(nodes[1], nodes[2], 1);
439 g.insert_arc(nodes[2], nodes[3], 1);
440
443 alg(cuts); // No start node specified
444
445 EXPECT_TRUE(cut_infos_eq(cuts, {1, 2}));
446}
447
449{
450 Graph g;
451 auto nodes = make_nodes(g, 4);
452 g.insert_arc(nodes[0], nodes[1], 1);
453 g.insert_arc(nodes[1], nodes[2], 1);
454 g.insert_arc(nodes[2], nodes[3], 1);
455
458 alg(nodes[3], cuts); // Start from end
459
460 EXPECT_TRUE(cut_infos_eq(cuts, {1, 2}));
461}
462
463// ============================================================================
464// Painting Tests
465// ============================================================================
466
468{
469 Graph g;
470 auto nodes = make_nodes(g, 5);
471 // Star: center is cut node, leaves are separate blocks
472 for (int i = 1; i < 5; ++i)
473 g.insert_arc(nodes[0], nodes[i], 1);
474
477 alg(nodes[0], cuts);
478
479 long num_colors = alg.paint_subgraphs();
481
482 // Center (cut node) should have color 0
484
485 // Each leaf should have a unique color > 0
487 for (int i = 1; i < 5; ++i)
488 {
489 long c = get_color<Graph>(nodes[i]);
490 EXPECT_GT(c, 0L);
491 leaf_colors.insert(c);
492 }
493 EXPECT_EQ(leaf_colors.size(), 4U);
494}
495
497{
498 Graph g;
499 auto nodes = make_nodes(g, 3);
500 g.insert_arc(nodes[0], nodes[1], 1); // cross arc from cut node 1
501 g.insert_arc(nodes[1], nodes[2], 1); // cross arc from cut node 1
502
505 alg(nodes[0], cuts);
506 alg.paint_subgraphs();
507
508 // All arcs in this path should be cross arcs
509 size_t cross_count = 0;
510 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
511 if (is_a_cross_arc<Graph>(it.get_curr()))
512 ++cross_count;
513
515}
516
518{
519 Graph g;
520 auto nodes = make_nodes(g, 6);
521 // Two triangles with a bridge
522 g.insert_arc(nodes[0], nodes[1], 1);
523 g.insert_arc(nodes[1], nodes[2], 1);
524 g.insert_arc(nodes[2], nodes[0], 1);
525 auto bridge = g.insert_arc(nodes[2], nodes[3], 1);
526 g.insert_arc(nodes[3], nodes[4], 1);
527 g.insert_arc(nodes[4], nodes[5], 1);
528 g.insert_arc(nodes[5], nodes[3], 1);
529
532 alg(nodes[0], cuts);
533 alg.paint_subgraphs();
534
536}
537
539{
540 Graph g;
541 auto nodes = make_nodes(g, 7);
542 // Tree with 4 leaf components
543 g.insert_arc(nodes[0], nodes[1], 1);
544 g.insert_arc(nodes[0], nodes[2], 1);
545 g.insert_arc(nodes[1], nodes[3], 1);
546 g.insert_arc(nodes[1], nodes[4], 1);
547 g.insert_arc(nodes[2], nodes[5], 1);
548 g.insert_arc(nodes[2], nodes[6], 1);
549
552 alg(nodes[0], cuts);
553
554 long num_colors = alg.paint_subgraphs();
555
556 // 4 leaves, each a separate block
557 EXPECT_EQ(num_colors, 5L); // colors 1-4 used, next is 5
558}
559
560// ============================================================================
561// Subgraph Mapping Tests
562// ============================================================================
563
565{
566 Graph g;
567 auto nodes = make_nodes(g, 3);
568 g.insert_arc(nodes[0], nodes[1], 1);
569
572 alg(nodes[0], cuts);
573 alg.paint_subgraphs();
574
575 Graph sg;
576 EXPECT_THROW(alg.map_subgraph(sg, 999L), std::domain_error);
577}
578
580{
581 Graph g;
582 auto nodes = make_nodes(g, 5);
583 for (int i = 1; i < 5; ++i)
584 g.insert_arc(nodes[0], nodes[i], 1);
585
588 alg(nodes[0], cuts);
589 alg.paint_subgraphs();
590
591 // Each leaf is a single-node component
592 for (long color = 1; color <= 4; ++color)
593 {
594 Graph sg;
595 alg.map_subgraph(sg, color);
596
597 EXPECT_EQ(sg.get_num_nodes(), 1U);
598 EXPECT_EQ(sg.get_num_arcs(), 0U);
599 }
600}
601
603{
604 Graph g;
605 auto nodes = make_nodes(g, 3);
606 g.insert_arc(nodes[0], nodes[1], 1);
607 g.insert_arc(nodes[1], nodes[2], 1);
608
611 alg(nodes[0], cuts);
612 alg.paint_subgraphs();
613
614 Graph sg;
615 alg.map_subgraph(sg, 1L);
616
617 // Verify nodes are mapped
618 for (auto it = sg.get_node_it(); it.has_curr(); it.next_ne())
619 {
620 auto sg_node = it.get_curr();
622 EXPECT_NE(orig_node, nullptr);
623 }
624}
625
626// ============================================================================
627// Cut Graph Tests
628// ============================================================================
629
631{
632 Graph g;
633 auto nodes = make_nodes(g, 5);
634 g.insert_arc(nodes[0], nodes[1], 1);
635 g.insert_arc(nodes[1], nodes[2], 1);
636 g.insert_arc(nodes[2], nodes[3], 1);
637 g.insert_arc(nodes[3], nodes[4], 1);
638
641 alg(nodes[0], cuts);
642 alg.paint_subgraphs();
643
644 Graph cg;
646 alg.map_cut_graph(cg, cross);
647
648 EXPECT_EQ(cg.get_num_nodes(), cuts.size());
649}
650
652{
653 Graph g;
654 auto nodes = make_nodes(g, 3);
655 g.insert_arc(nodes[0], nodes[1], 1);
656 g.insert_arc(nodes[1], nodes[2], 1);
657
660 alg(nodes[0], cuts);
661 alg.paint_subgraphs();
662
663 Graph cg;
665 alg.map_cut_graph(cg, cross);
666
667 // Count cross arcs in original graph
668 size_t expected_cross = 0;
669 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
670 if (is_a_cross_arc<Graph>(it.get_curr()))
672
673 EXPECT_EQ(cross.size(), expected_cross);
674}
675
676// ============================================================================
677// compute_blocks() Tests
678// ============================================================================
679
681{
682 Graph g;
683 auto nodes = make_nodes(g, 2);
684 g.insert_arc(nodes[0], nodes[1], 1);
685
687
688 DynDlist<Graph> blocks;
689 Graph cg;
691
692 EXPECT_THROW(alg.compute_blocks(blocks, cg, cross), std::logic_error);
693}
694
696{
697 Graph g;
698 auto nodes = make_nodes(g, 5);
699 for (int i = 1; i < 5; ++i)
700 g.insert_arc(nodes[0], nodes[i], 1);
701
704 alg(nodes[0], cuts);
705
706 DynDlist<Graph> blocks;
707 Graph cg;
709
710 // compute_blocks should auto-paint
711 EXPECT_NO_THROW(alg.compute_blocks(blocks, cg, cross));
712 EXPECT_GE(blocks.size(), 4U); // at least 4 leaf blocks (may have empty slots)
713}
714
716{
717 Graph g;
718 auto nodes = make_nodes(g, 7);
719 g.insert_arc(nodes[0], nodes[1], 1);
720 g.insert_arc(nodes[0], nodes[2], 1);
721 g.insert_arc(nodes[1], nodes[3], 1);
722 g.insert_arc(nodes[1], nodes[4], 1);
723 g.insert_arc(nodes[2], nodes[5], 1);
724 g.insert_arc(nodes[2], nodes[6], 1);
725
728 alg(nodes[0], cuts);
729
730 DynDlist<Graph> blocks;
731 Graph cg;
733 alg.compute_blocks(blocks, cg, cross);
734
735 EXPECT_GE(blocks.size(), 4U); // at least 4 leaf components (may have empty slots)
736}
737
739{
740 Graph g;
741 auto nodes = make_nodes(g, 5);
742 g.insert_arc(nodes[0], nodes[1], 1);
743 g.insert_arc(nodes[1], nodes[2], 1);
744 g.insert_arc(nodes[2], nodes[3], 1);
745 g.insert_arc(nodes[3], nodes[4], 1);
746
749 alg(nodes[0], cuts);
750
751 DynDlist<Graph> blocks;
752 Graph cg;
754 alg.compute_blocks(blocks, cg, cross);
755
756 EXPECT_EQ(cg.get_num_nodes(), cuts.size());
757}
758
760{
761 Graph g;
762 auto nodes = make_nodes(g, 3);
763 g.insert_arc(nodes[0], nodes[1], 1);
764 g.insert_arc(nodes[1], nodes[2], 1);
765
768 alg(nodes[0], cuts);
769
770 DynDlist<Graph> blocks;
771 Graph cg;
773 alg.compute_blocks(blocks, cg, cross);
774
775 EXPECT_FALSE(cross.is_empty());
776}
777
778// ============================================================================
779// Edge Cases
780// ============================================================================
781
783{
784 Graph g;
785 auto nodes = make_nodes(g, 1);
786
789 alg(nodes[0], cuts);
790
791 EXPECT_TRUE(cuts.is_empty());
792}
793
795{
796 Graph g;
797 auto nodes = make_nodes(g, 2);
798 g.insert_arc(nodes[0], nodes[1], 1);
799
802 alg(nodes[0], cuts);
803
804 EXPECT_TRUE(cuts.is_empty());
805}
806
808{
809 Graph g;
810 auto nodes = make_nodes(g, 3);
811 g.insert_arc(nodes[0], nodes[1], 1);
812 g.insert_arc(nodes[1], nodes[2], 1);
813 g.insert_arc(nodes[2], nodes[0], 1);
814
817 alg(nodes[0], cuts);
818
819 EXPECT_TRUE(cuts.is_empty());
820}
821
823{
824 Graph g;
825 auto nodes = make_nodes(g, 3);
826 g.insert_arc(nodes[0], nodes[1], 1);
827 g.insert_arc(nodes[1], nodes[2], 1);
828 g.insert_arc(nodes[1], nodes[1], 99); // self-loop
829
832 alg(nodes[0], cuts);
833
835}
836
838{
839 Graph g;
840 auto nodes = make_nodes(g, 3);
841 g.insert_arc(nodes[0], nodes[1], 1);
842 g.insert_arc(nodes[0], nodes[1], 2); // parallel arc
843 g.insert_arc(nodes[1], nodes[2], 1);
844
847 alg(nodes[0], cuts);
848
850}
851
852// ============================================================================
853// Integration Tests
854// ============================================================================
855
857{
858 Graph g;
859 auto nodes = make_nodes(g, 5);
860 for (int i = 1; i < 5; ++i)
861 g.insert_arc(nodes[0], nodes[i], 1);
862
864
865 // Step 1: Detect cut nodes
867 alg(nodes[0], cuts);
868 EXPECT_FALSE(cuts.is_empty());
869
870 // Step 2: Paint
871 long num_colors = alg.paint_subgraphs();
873
874 // Step 3: Map all subgraphs
875 for (long color = 1; color < num_colors; ++color)
876 {
877 Graph sg;
878 EXPECT_NO_THROW(alg.map_subgraph(sg, color));
879 }
880
881 // Step 4: Map cut graph
882 Graph cg;
884 EXPECT_NO_THROW(alg.map_cut_graph(cg, cross));
885}
886
888{
889 Graph g;
890 auto nodes = make_nodes(g, 4);
891 g.insert_arc(nodes[0], nodes[1], 1);
892 g.insert_arc(nodes[1], nodes[2], 1);
893 g.insert_arc(nodes[2], nodes[3], 1);
894
896
897 for (int iter = 0; iter < 3; ++iter)
898 {
900 alg(nodes[0], cuts);
901 EXPECT_TRUE(cut_infos_eq(cuts, {1, 2}));
902 }
903}
904
906{
907 Graph g;
908 const int N = 100;
909 auto nodes = make_nodes(g, N);
910
911 // Create a path graph with N-2 cut nodes
912 for (int i = 0; i < N - 1; ++i)
913 g.insert_arc(nodes[i], nodes[i + 1], 1);
914
917 alg(nodes[0], cuts);
918
919 EXPECT_EQ(cuts.size(), static_cast<size_t>(N - 2));
920}
921
923{
924 Graph g;
925 auto nodes = make_nodes(g, 3);
926 g.insert_arc(nodes[0], nodes[1], 1);
927
930 alg(nodes[0], cuts);
931 alg.paint_subgraphs();
932
933 Graph sg;
934 // Attempt with invalid color should throw and clear sg
935 try
936 {
937 alg.map_subgraph(sg, 999L);
938 }
939 catch (const std::domain_error &)
940 {
941 // Graph should be cleared on exception
942 EXPECT_EQ(sg.get_num_nodes(), 0U);
943 EXPECT_EQ(sg.get_num_arcs(), 0U);
944 }
945}
946
947// ============================================================================
948// Bridges (Compute_Bridges / find_bridges)
949// ============================================================================
950
951namespace
952{
953 // Collect bridge arcs into a set for easy membership testing.
955 {
957 for (typename DynList<Graph::Arc *>::Iterator it(lst); it.has_curr(); it.next_ne())
958 s.insert(it.get_curr());
959 return s;
960 }
961} // namespace
962
964{
965 // 0 -- 1 -- 2 -- 3 : all 3 arcs are bridges
966 Graph g;
967 auto nodes = make_nodes(g, 4);
968 auto a01 = g.insert_arc(nodes[0], nodes[1], 1);
969 auto a12 = g.insert_arc(nodes[1], nodes[2], 1);
970 auto a23 = g.insert_arc(nodes[2], nodes[3], 1);
971
973 auto bridges = cb.find_bridges(nodes[0]);
974
975 auto bs = bridge_set(bridges);
976 EXPECT_EQ(bs.size(), 3U);
977 EXPECT_TRUE(bs.exist(a01));
978 EXPECT_TRUE(bs.exist(a12));
979 EXPECT_TRUE(bs.exist(a23));
980}
981
983{
984 // 0 -- 1 -- 2 -- 3 -- 0 : no bridges
985 Graph g;
986 auto nodes = make_nodes(g, 4);
987 g.insert_arc(nodes[0], nodes[1], 1);
988 g.insert_arc(nodes[1], nodes[2], 1);
989 g.insert_arc(nodes[2], nodes[3], 1);
990 g.insert_arc(nodes[3], nodes[0], 1);
991
992 auto bridges = find_bridges(g);
993 EXPECT_TRUE(bridges.is_empty());
994}
995
997{
998 // Triangles 0-1-2-0 and 3-4-5-3, connected by bridge 2--3
999 Graph g;
1000 auto nodes = make_nodes(g, 6);
1001 g.insert_arc(nodes[0], nodes[1], 1);
1002 g.insert_arc(nodes[1], nodes[2], 1);
1003 g.insert_arc(nodes[2], nodes[0], 1);
1004 auto bridge = g.insert_arc(nodes[2], nodes[3], 1); // bridge
1005 g.insert_arc(nodes[3], nodes[4], 1);
1006 g.insert_arc(nodes[4], nodes[5], 1);
1007 g.insert_arc(nodes[5], nodes[3], 1);
1008
1009 auto bridges = find_bridges(g, nodes[0]);
1010 EXPECT_EQ(bridges.size(), 1U);
1012}
1013
1015{
1016 /*
1017 * 0
1018 * / \
1019 * 1 2
1020 * / \
1021 * 3 4
1022 */
1023 Graph g;
1024 auto nodes = make_nodes(g, 5);
1025 auto a01 = g.insert_arc(nodes[0], nodes[1], 1);
1026 auto a02 = g.insert_arc(nodes[0], nodes[2], 1);
1027 auto a13 = g.insert_arc(nodes[1], nodes[3], 1);
1028 auto a14 = g.insert_arc(nodes[1], nodes[4], 1);
1029
1030 auto bridges = find_bridges(g);
1031 auto bs = bridge_set(bridges);
1032 EXPECT_EQ(bs.size(), 4U);
1033 EXPECT_TRUE(bs.exist(a01));
1034 EXPECT_TRUE(bs.exist(a02));
1035 EXPECT_TRUE(bs.exist(a13));
1036 EXPECT_TRUE(bs.exist(a14));
1037}
1038
1040{
1041 Graph g;
1042 auto nodes = make_nodes(g, 4);
1043 for (int i = 0; i < 4; ++i)
1044 for (int j = i + 1; j < 4; ++j)
1045 g.insert_arc(nodes[i], nodes[j], 1);
1046
1047 auto bridges = find_bridges(g);
1048 EXPECT_TRUE(bridges.is_empty());
1049}
1050
1052{
1053 Graph g;
1054 auto nodes = make_nodes(g, 1);
1055 auto bridges = find_bridges(g, nodes[0]);
1056 EXPECT_TRUE(bridges.is_empty());
1057}
1058
1060{
1061 Graph g;
1062 auto nodes = make_nodes(g, 2);
1063 auto b = g.insert_arc(nodes[0], nodes[1], 1);
1064
1065 auto bridges = find_bridges(g);
1066 EXPECT_EQ(bridges.size(), 1U);
1067 EXPECT_TRUE(bridge_set(bridges).exist(b));
1068}
1069
1071{
1072 // Two parallel arcs between nodes 0 and 1: neither is a bridge
1073 Graph g;
1074 auto nodes = make_nodes(g, 2);
1075 g.insert_arc(nodes[0], nodes[1], 1);
1076 g.insert_arc(nodes[0], nodes[1], 2); // parallel arc
1077
1078 auto bridges = find_bridges(g);
1079 EXPECT_TRUE(bridges.is_empty());
1080}
1081
1083{
1084 // Triangle + pendant edge: only pendant is a bridge
1085 Graph g;
1086 auto nodes = make_nodes(g, 4);
1087 g.insert_arc(nodes[0], nodes[1], 1);
1088 g.insert_arc(nodes[1], nodes[2], 1);
1089 g.insert_arc(nodes[2], nodes[0], 1);
1090 auto pend = g.insert_arc(nodes[2], nodes[3], 1); // bridge
1091
1093 auto b_class = cb.find_bridges(nodes[0]);
1094 auto b_free = find_bridges(g, nodes[0]);
1095
1096 EXPECT_EQ(b_class.size(), b_free.size());
1097 EXPECT_EQ(b_class.size(), 1U);
1099}
1100
1102{
1103 Graph g;
1104 auto nodes = make_nodes(g, 4);
1105 g.insert_arc(nodes[0], nodes[1], 1);
1106 g.insert_arc(nodes[1], nodes[2], 1);
1107 g.insert_arc(nodes[2], nodes[3], 1);
1108
1110 auto b1 = cb.find_bridges();
1111 auto b2 = cb(); // operator()()
1112
1113 EXPECT_EQ(b1.size(), b2.size());
1114 EXPECT_EQ(b1.size(), 3U);
1115}
1116
1118{
1119 // Two triangles joined by a single bridge
1120 Graph g;
1121 auto nodes = make_nodes(g, 6);
1122 g.insert_arc(nodes[0], nodes[1], 1);
1123 g.insert_arc(nodes[1], nodes[2], 1);
1124 g.insert_arc(nodes[2], nodes[0], 1);
1125 g.insert_arc(nodes[2], nodes[3], 1); // bridge
1126 g.insert_arc(nodes[3], nodes[4], 1);
1127 g.insert_arc(nodes[4], nodes[5], 1);
1128 g.insert_arc(nodes[5], nodes[3], 1);
1129
1131 auto b1 = cb.find_bridges();
1132 auto b2 = cb.find_bridges();
1133
1134 EXPECT_EQ(b1.size(), 1U);
1135 EXPECT_EQ(b2.size(), 1U);
1136}
1137
1139{
1140 // Path of 100 nodes: all 99 edges are bridges
1141 Graph g;
1142 const int N = 100;
1143 auto nodes = make_nodes(g, N);
1144 for (int i = 0; i < N - 1; ++i)
1145 g.insert_arc(nodes[i], nodes[i + 1], 1);
1146
1147 auto bridges = find_bridges(g);
1148 EXPECT_EQ(bridges.size(), static_cast<size_t>(N - 1));
1149}
1150
1152{
1153 // Path 0-1-2-3: every edge is also a "cut arc" after compute_cut_nodes+paint
1154 Graph g;
1155 auto nodes = make_nodes(g, 4);
1156 g.insert_arc(nodes[0], nodes[1], 1);
1157 g.insert_arc(nodes[1], nodes[2], 1);
1158 g.insert_arc(nodes[2], nodes[3], 1);
1159
1160 // Bridges via Compute_Bridges
1161 auto bridges = find_bridges(g, nodes[0]);
1162 EXPECT_EQ(bridges.size(), 3U);
1163
1164 // Articulation points via Compute_Cut_Nodes: nodes 1 and 2
1167 alg(nodes[0], cuts);
1168 EXPECT_EQ(cuts.size(), 2U); // nodes 1 and 2
1169}
1170
1172{
1173 // Star: center (0) -- leaves 1..4; all 4 arcs are bridges
1174 Graph g;
1175 auto nodes = make_nodes(g, 5);
1177 for (int i = 1; i < 5; ++i)
1178 arcs.append(g.insert_arc(nodes[0], nodes[i], 1));
1179
1180 auto bridges = find_bridges(g, nodes[0]);
1181 auto bs = bridge_set(bridges);
1182 EXPECT_EQ(bs.size(), 4U);
1183 arcs.for_each([&](Graph::Arc * a) { EXPECT_TRUE(bs.exist(a)); });
1184}
1185
1187{
1188 // Path 0-1-2 with a self-loop on node 1: bridges are still 0-1 and 1-2
1189 Graph g;
1190 auto nodes = make_nodes(g, 3);
1191 auto a01 = g.insert_arc(nodes[0], nodes[1], 1);
1192 auto a12 = g.insert_arc(nodes[1], nodes[2], 1);
1193 g.insert_arc(nodes[1], nodes[1], 99); // self-loop on 1
1194
1195 auto bridges = find_bridges(g, nodes[0]);
1196 auto bs = bridge_set(bridges);
1197 EXPECT_EQ(bs.size(), 2U);
1198 EXPECT_TRUE(bs.exist(a01));
1199 EXPECT_TRUE(bs.exist(a12));
1200}
1201
1203{
1204 // Two triangles connected by a bridge (2--3).
1205 // Regardless of where the DFS starts, only that arc is a bridge.
1206 Graph g;
1207 auto nodes = make_nodes(g, 6);
1208 g.insert_arc(nodes[0], nodes[1], 1);
1209 g.insert_arc(nodes[1], nodes[2], 1);
1210 g.insert_arc(nodes[2], nodes[0], 1);
1211 auto bridge = g.insert_arc(nodes[2], nodes[3], 1);
1212 g.insert_arc(nodes[3], nodes[4], 1);
1213 g.insert_arc(nodes[4], nodes[5], 1);
1214 g.insert_arc(nodes[5], nodes[3], 1);
1215
1216 for (int start = 0; start < 6; ++start)
1217 {
1218 auto bridges = find_bridges(g, nodes[start]);
1219 auto bs = bridge_set(bridges);
1220 EXPECT_EQ(bs.size(), 1U) << "start=" << start;
1221 EXPECT_TRUE(bs.exist(bridge)) << "start=" << start;
1222 }
1223}
1224
1226{
1227 // Triangle 0-1-2-0 + pendant 2-3.
1228 // The 3 triangle arcs must NOT appear; only the pendant is a bridge.
1229 Graph g;
1230 auto nodes = make_nodes(g, 4);
1231 auto t01 = g.insert_arc(nodes[0], nodes[1], 1); // triangle, not bridge
1232 auto t12 = g.insert_arc(nodes[1], nodes[2], 1); // triangle, not bridge
1233 auto t20 = g.insert_arc(nodes[2], nodes[0], 1); // triangle, not bridge
1234 auto pend = g.insert_arc(nodes[2], nodes[3], 1); // bridge
1235
1236 auto bridges = find_bridges(g);
1237 auto bs = bridge_set(bridges);
1238
1239 EXPECT_EQ(bs.size(), 1U);
1240 EXPECT_TRUE(bs.exist(pend));
1241 EXPECT_FALSE(bs.exist(t01));
1242 EXPECT_FALSE(bs.exist(t12));
1243 EXPECT_FALSE(bs.exist(t20));
1244}
1245
1247{
1248 // Empty graph: find_bridges() must return empty list without throwing.
1249 Graph g;
1250 EXPECT_TRUE(find_bridges(g).is_empty());
1251}
1252
1254{
1255 // Component A: path 0-1-2 (both arcs are bridges).
1256 // Component B: path 3-4-5 (both arcs are bridges).
1257 // The two components are not connected, so there are 4 bridges total.
1258 Graph g;
1259 auto nodes = make_nodes(g, 6);
1260 auto a01 = g.insert_arc(nodes[0], nodes[1], 1);
1261 auto a12 = g.insert_arc(nodes[1], nodes[2], 1);
1262 auto a34 = g.insert_arc(nodes[3], nodes[4], 1);
1263 auto a45 = g.insert_arc(nodes[4], nodes[5], 1);
1264
1265 // Start from a node in component A; component B must still be covered.
1266 auto bridges = find_bridges(g, nodes[0]);
1267 auto bs = bridge_set(bridges);
1268 EXPECT_EQ(bs.size(), 4U);
1269 EXPECT_TRUE(bs.exist(a01));
1270 EXPECT_TRUE(bs.exist(a12));
1271 EXPECT_TRUE(bs.exist(a34));
1272 EXPECT_TRUE(bs.exist(a45));
1273}
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:194
Find bridge edges (isthmuses) of a connected undirected graph.
Computation of cut nodes (articulation points) of a graph.
Iterator dynamic list.
Dynamic doubly linked list with O(1) size and bidirectional access.
const size_t & size() const noexcept
Return the number of elements (constant time)
Iterator on the items of list.
Definition htlist.H:1420
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
Dynamic set backed by balanced binary search trees with automatic memory management.
Key * insert(const Key &key)
Inserts a key into the dynamic set.
bool has_curr() const noexcept
Definition htlist.H:930
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Arc 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
auto get_arc_it() const noexcept
Obtains an iterator to the arc of graph.
Definition graph-dry.H:2808
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:784
auto get_node_it() const noexcept
Obtains an iterator to the nodes of graph.
Definition graph-dry.H:2786
#define TEST(name)
#define N
Definition fib.C:294
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
DynArray< Graph::Arc * > arcs
Definition graphpic.C:408
DynList< typename GT::Arc * > find_bridges(const GT &g, typename GT::Node *start, SA sa=SA())
Find all bridge edges in a connected undirected graph.
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.
static long & color(typename GT::Node *p)
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Dynamic array container with automatic resizing.
Articulation points (cut nodes), bridges, and biconnected components.
Dynamic set implementations based on balanced binary search trees.
Generic graph and digraph implementations.
Utility algorithms and operations for graphs.