Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
test_kruskal.cc
Go to the documentation of this file.
1
13#include <gtest/gtest.h>
14
15#include <limits>
16#include <random>
17#include <set>
18#include <vector>
19
20#include <Kruskal.H>
21#include <tpl_graph.H>
22#include <tpl_sgraph.H>
23#include <tpl_agraph.H>
24
25using namespace Aleph;
26
27// Graph types for tests
29using Node = GT::Node;
30using Arc = GT::Arc;
31
32// Array-based graph type
35using AArc = AGT::Arc;
36
37namespace {
38
39// Helper to count painted arcs
40template <class G>
41size_t count_painted_arcs(const G &g)
42{
43 size_t n = 0;
44 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
45 if (IS_ARC_VISITED(it.get_current_arc_ne(), Aleph::Spanning_Tree))
46 ++n;
47 return n;
48}
49
50// Helper to compute total weight of tree
51template <class G>
52int tree_total_weight(const G &tree)
53{
54 int total = 0;
55 for (auto it = tree.get_arc_it(); it.has_curr(); it.next_ne())
56 total += it.get_current_arc_ne()->get_info();
57 return total;
58}
59
60// Helper to compute total weight of painted arcs
61template <class G>
62int painted_total_weight(const G &g)
63{
64 int total = 0;
65 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
66 {
67 auto arc = it.get_current_arc_ne();
69 total += arc->get_info();
70 }
71 return total;
72}
73
74// Custom distance accessor for testing
75template <class G>
76struct Scaled_Dist
77{
78 using Distance_Type = int;
79 static const Distance_Type Zero_Distance = 0;
80 static const Distance_Type Max_Distance = std::numeric_limits<int>::max();
81
82 int factor = 1;
83 Scaled_Dist() = default;
84 explicit Scaled_Dist(int f) : factor(f) {}
85
86 int operator()(typename G::Arc *a) const noexcept
87 {
88 return a->get_info() * factor;
89 }
90};
91
92// Custom arc filter that only accepts arcs with weight <= threshold
93template <class G>
94struct Weight_Filter
95{
96 int threshold;
97
98 Weight_Filter(int t = std::numeric_limits<int>::max()) : threshold(t) {}
99
100 bool operator()(typename G::Arc *a) const noexcept
101 {
102 return a->get_info() <= threshold;
103 }
104};
105
106// Check if tree is connected (for undirected graphs)
107template <class G>
108bool is_tree_connected(const G &tree)
109{
110 if (tree.get_num_nodes() == 0)
111 return true;
112 if (tree.get_num_nodes() == 1)
113 return true;
114
115 // A tree with V nodes should have V-1 arcs
116 if (tree.get_num_arcs() != tree.get_num_nodes() - 1)
117 return false;
118
119 // BFS to check connectivity
120 tree.reset_nodes();
121 auto first = tree.get_first_node();
122 NODE_BITS(first).set_bit(Aleph::Spanning_Tree, true);
123
125 queue.append(first);
126 size_t visited = 1;
127
128 while (not queue.is_empty())
129 {
130 auto curr = queue.remove_first();
131 for (Node_Arc_Iterator<G> it(curr); it.has_curr(); it.next_ne())
132 {
133 auto tgt = it.get_tgt_node_ne();
135 {
136 NODE_BITS(tgt).set_bit(Aleph::Spanning_Tree, true);
137 queue.append(tgt);
138 ++visited;
139 }
140 }
141 }
142
143 return visited == tree.get_num_nodes();
144}
145
146} // namespace
147
148// ========== TEST 1: Simple Connected Graph ==========
150
151 GT g;
152 Node* n0 = g.insert_node(0);
153 Node* n1 = g.insert_node(1);
154 Node* n2 = g.insert_node(2);
155 Node* n3 = g.insert_node(3);
156
157 g.insert_arc(n0, n1, 1);
158 g.insert_arc(n0, n2, 4);
159 g.insert_arc(n1, n2, 2);
160 g.insert_arc(n1, n3, 5);
161 g.insert_arc(n2, n3, 3);
162
164 GT tree;
165 kruskal(g, tree);
166
167 // Tree should have same number of nodes as g
169
170 // Tree should have n-1 arcs (spanning tree)
171 ASSERT_EQ(tree.get_num_arcs(), g.get_num_nodes() - 1);
172
173 // MST weight: edges (0,1)=1 + (1,2)=2 + (2,3)=3 = 6
175}
176
177// ========== TEST 2: Single Node Graph ==========
179
180 GT g;
181 g.insert_node(0);
182
184 GT tree;
185 kruskal(g, tree);
186
187 ASSERT_EQ(tree.get_num_nodes(), 1u);
188 ASSERT_EQ(tree.get_num_arcs(), 0u);
189}
190
191// ========== TEST 3: Two Nodes One Arc ==========
193
194 GT g;
195 Node* n0 = g.insert_node(0);
196 Node* n1 = g.insert_node(1);
197 g.insert_arc(n0, n1, 5);
198
200 GT tree;
201 kruskal(g, tree);
202
203 ASSERT_EQ(tree.get_num_nodes(), 2u);
204 ASSERT_EQ(tree.get_num_arcs(), 1u);
206}
207
208// ========== TEST 4: Linear Chain ==========
210
211 GT g;
212 const int num_nodes = 5;
213 std::vector<Node*> nodes;
214
215 for (int i = 0; i < num_nodes; ++i)
216 nodes.push_back(g.insert_node(i));
217
218 // Chain: 0--1--2--3--4 with weights 1,2,3,4
219 for (int i = 0; i < num_nodes - 1; ++i)
220 g.insert_arc(nodes[i], nodes[i+1], i + 1);
221
223 GT tree;
224 kruskal(g, tree);
225
226 // Tree is the chain itself
227 ASSERT_EQ(tree.get_num_nodes(), static_cast<size_t>(num_nodes));
228 ASSERT_EQ(tree.get_num_arcs(), static_cast<size_t>(num_nodes - 1));
229 // Total weight: 1+2+3+4 = 10
230 ASSERT_EQ(tree_total_weight(tree), 10);
231}
232
233// ========== TEST 5: Complete Graph K4 ==========
235
236 GT g;
237 Node* n0 = g.insert_node(0);
238 Node* n1 = g.insert_node(1);
239 Node* n2 = g.insert_node(2);
240 Node* n3 = g.insert_node(3);
241
242 // K4 with distinct weights
243 g.insert_arc(n0, n1, 1); // min
244 g.insert_arc(n0, n2, 2); // 2nd min
245 g.insert_arc(n0, n3, 10);
246 g.insert_arc(n1, n2, 5);
247 g.insert_arc(n1, n3, 3); // 3rd min
248 g.insert_arc(n2, n3, 7);
249
251 GT tree;
252 kruskal(g, tree);
253
254 ASSERT_EQ(tree.get_num_nodes(), 4u);
255 ASSERT_EQ(tree.get_num_arcs(), 3u);
256 // MST: (0,1)=1 + (0,2)=2 + (1,3)=3 = 6
258}
259
260// ========== TEST 6: Star Graph ==========
262
263 GT g;
264 Node* center = g.insert_node(0);
265 const int num_leaves = 5;
266 std::vector<Node*> leaves;
267
268 for (int i = 1; i <= num_leaves; ++i)
269 {
270 auto leaf = g.insert_node(i);
271 leaves.push_back(leaf);
272 g.insert_arc(center, leaf, i);
273 }
274
276 GT tree;
277 kruskal(g, tree);
278
279 // Star tree: center connects to all leaves
280 ASSERT_EQ(tree.get_num_nodes(), static_cast<size_t>(num_leaves + 1));
281 ASSERT_EQ(tree.get_num_arcs(), static_cast<size_t>(num_leaves));
282 // Total weight: 1+2+3+4+5 = 15
283 ASSERT_EQ(tree_total_weight(tree), 15);
284}
285
286// ========== TEST 7: Grid Graph 3x3 ==========
288
289 GT g;
290 std::vector<std::vector<Node*>> grid(3, std::vector<Node*>(3));
291
292 // Create 3x3 grid
293 int id = 0;
294 for (int i = 0; i < 3; ++i)
295 for (int j = 0; j < 3; ++j)
296 grid[i][j] = g.insert_node(id++);
297
298 // Add horizontal edges (weight = row + 1)
299 for (int i = 0; i < 3; ++i)
300 for (int j = 0; j < 2; ++j)
301 g.insert_arc(grid[i][j], grid[i][j+1], i + 1);
302
303 // Add vertical edges (weight = column + 5)
304 for (int i = 0; i < 2; ++i)
305 for (int j = 0; j < 3; ++j)
306 g.insert_arc(grid[i][j], grid[i+1][j], j + 5);
307
309 GT tree;
310 kruskal(g, tree);
311
312 // 9 nodes, should have 8 arcs
313 ASSERT_EQ(tree.get_num_nodes(), 9u);
314 ASSERT_EQ(tree.get_num_arcs(), 8u);
316}
317
318// ========== TEST 8: Diamond Graph ==========
320
321 GT g;
322 Node* n0 = g.insert_node(0);
323 Node* n1 = g.insert_node(1);
324 Node* n2 = g.insert_node(2);
325 Node* n3 = g.insert_node(3);
326
327 // Diamond shape with two paths
328 g.insert_arc(n0, n1, 1);
329 g.insert_arc(n0, n2, 2);
330 g.insert_arc(n1, n3, 3);
331 g.insert_arc(n2, n3, 1);
332
334 GT tree;
335 kruskal(g, tree);
336
337 ASSERT_EQ(tree.get_num_nodes(), 4u);
338 ASSERT_EQ(tree.get_num_arcs(), 3u);
339 // MST: (0,1)=1 + (2,3)=1 + (0,2)=2 = 4
341}
342
343// ========== TEST 9: All Equal Weights ==========
345
346 GT g;
347 Node* n0 = g.insert_node(0);
348 Node* n1 = g.insert_node(1);
349 Node* n2 = g.insert_node(2);
350 Node* n3 = g.insert_node(3);
351
352 // All edges have weight 5
353 g.insert_arc(n0, n1, 5);
354 g.insert_arc(n0, n2, 5);
355 g.insert_arc(n0, n3, 5);
356 g.insert_arc(n1, n2, 5);
357 g.insert_arc(n1, n3, 5);
358 g.insert_arc(n2, n3, 5);
359
361 GT tree;
362 kruskal(g, tree);
363
364 // Any spanning tree is valid
365 ASSERT_EQ(tree.get_num_nodes(), 4u);
366 ASSERT_EQ(tree.get_num_arcs(), 3u);
367 // Total weight: 3 * 5 = 15
368 ASSERT_EQ(tree_total_weight(tree), 15);
369}
370
371// ========== TEST 10: Unique Weights ==========
373
374 GT g;
375 Node* n0 = g.insert_node(0);
376 Node* n1 = g.insert_node(1);
377 Node* n2 = g.insert_node(2);
378 Node* n3 = g.insert_node(3);
379
380 // All edges have unique weights
381 g.insert_arc(n0, n1, 1);
382 g.insert_arc(n0, n2, 3);
383 g.insert_arc(n0, n3, 5);
384 g.insert_arc(n1, n2, 2);
385 g.insert_arc(n1, n3, 6);
386 g.insert_arc(n2, n3, 4);
387
389 GT tree;
390 kruskal(g, tree);
391
392 // Deterministic MST: (0,1)=1 + (1,2)=2 + (2,3)=4 = 7
394}
395
396// ========== TEST 11: Zero Weight Edges ==========
398
399 GT g;
400 Node* n0 = g.insert_node(0);
401 Node* n1 = g.insert_node(1);
402 Node* n2 = g.insert_node(2);
403
404 g.insert_arc(n0, n1, 0);
405 g.insert_arc(n1, n2, 0);
406
408 GT tree;
409 kruskal(g, tree);
410
411 ASSERT_EQ(tree.get_num_nodes(), 3u);
412 ASSERT_EQ(tree.get_num_arcs(), 2u);
414}
415
416// ========== TEST 12: Large Weights ==========
418
419 GT g;
420 Node* n0 = g.insert_node(0);
421 Node* n1 = g.insert_node(1);
422 Node* n2 = g.insert_node(2);
423
424 g.insert_arc(n0, n1, 1000000);
425 g.insert_arc(n1, n2, 2000000);
426 g.insert_arc(n0, n2, 1500000);
427
429 GT tree;
430 kruskal(g, tree);
431
432 ASSERT_EQ(tree.get_num_arcs(), 2u);
433 // MST: (0,1)=1000000 + (0,2)=1500000 = 2500000
434 ASSERT_EQ(tree_total_weight(tree), 2500000);
435}
436
437// ========== TEST 13: Parallel Edges ==========
439
440 GT g;
441 Node* n0 = g.insert_node(0);
442 Node* n1 = g.insert_node(1);
443
444 // Multiple edges between same nodes
445 g.insert_arc(n0, n1, 10);
446 g.insert_arc(n0, n1, 5); // minimum
447 g.insert_arc(n0, n1, 8);
448
450 GT tree;
451 kruskal(g, tree);
452
453 ASSERT_EQ(tree.get_num_nodes(), 2u);
454 ASSERT_EQ(tree.get_num_arcs(), 1u);
455 // Should pick the minimum weight edge
457}
458
459// ========== TEST 14: Self Loop ==========
461
462 GT g;
463 Node* n0 = g.insert_node(0);
464 Node* n1 = g.insert_node(1);
465
466 g.insert_arc(n0, n0, 1); // Self-loop
467 g.insert_arc(n0, n1, 2);
468
470 GT tree;
471 kruskal(g, tree);
472
473 ASSERT_EQ(tree.get_num_nodes(), 2u);
474 // Self-loops should be ignored (would create cycle)
475 ASSERT_EQ(tree.get_num_arcs(), 1u);
477}
478
479// ========== TEST 15: Cycle Graph ==========
481
482 GT g;
483 const int num_nodes = 5;
484 std::vector<Node*> nodes;
485
486 for (int i = 0; i < num_nodes; ++i)
487 nodes.push_back(g.insert_node(i));
488
489 // Create cycle: 0--1--2--3--4--0
490 for (int i = 0; i < num_nodes; ++i)
491 g.insert_arc(nodes[i], nodes[(i+1) % num_nodes], i + 1);
492
494 GT tree;
495 kruskal(g, tree);
496
497 // Should remove one edge to break the cycle
498 ASSERT_EQ(tree.get_num_nodes(), static_cast<size_t>(num_nodes));
499 ASSERT_EQ(tree.get_num_arcs(), static_cast<size_t>(num_nodes - 1));
500 // MST: picks 4 smallest edges = 1+2+3+4 = 10
501 ASSERT_EQ(tree_total_weight(tree), 10);
502}
503
504// ========== TEST 16: Paint Mode ==========
506
507 GT g;
508 Node* n0 = g.insert_node(0);
509 Node* n1 = g.insert_node(1);
510 Node* n2 = g.insert_node(2);
511 Node* n3 = g.insert_node(3);
512
513 g.insert_arc(n0, n1, 1);
514 g.insert_arc(n0, n2, 4);
515 g.insert_arc(n1, n2, 2);
516 g.insert_arc(n1, n3, 5);
517 g.insert_arc(n2, n3, 3);
518
520 kruskal(g); // Paint mode
521
522 // Verify spanning tree arcs are painted
524 // MST weight: 1+2+3 = 6
526}
527
528// ========== TEST 17: Tree Building Mode ==========
530
531 GT g;
532 Node* n0 = g.insert_node(0);
533 Node* n1 = g.insert_node(1);
534 Node* n2 = g.insert_node(2);
535
536 g.insert_arc(n0, n1, 1);
537 g.insert_arc(n1, n2, 2);
538 g.insert_arc(n0, n2, 5);
539
541 GT tree;
542 kruskal(g, tree);
543
544 ASSERT_EQ(tree.get_num_nodes(), 3u);
545 ASSERT_EQ(tree.get_num_arcs(), 2u);
547}
548
549// ========== TEST 18: Tree Has V-1 Arcs ==========
551
552 GT g;
553 const int num_nodes = 10;
554 std::vector<Node*> nodes;
555
556 for (int i = 0; i < num_nodes; ++i)
557 nodes.push_back(g.insert_node(i));
558
559 // Create a complete graph
560 int weight = 1;
561 for (int i = 0; i < num_nodes; ++i)
562 for (int j = i + 1; j < num_nodes; ++j)
563 g.insert_arc(nodes[i], nodes[j], weight++);
564
566 GT tree;
567 kruskal(g, tree);
568
569 ASSERT_EQ(tree.get_num_nodes(), static_cast<size_t>(num_nodes));
570 ASSERT_EQ(tree.get_num_arcs(), static_cast<size_t>(num_nodes - 1));
571}
572
573// ========== TEST 19: All Nodes in Tree ==========
575
576 GT g;
577 Node* n0 = g.insert_node(0);
578 Node* n1 = g.insert_node(1);
579 Node* n2 = g.insert_node(2);
580 Node* n3 = g.insert_node(3);
581
582 g.insert_arc(n0, n1, 1);
583 g.insert_arc(n1, n2, 2);
584 g.insert_arc(n2, n3, 3);
585
587 GT tree;
588 kruskal(g, tree);
589
590 // Verify all node info values are present
591 std::set<int> node_infos;
592 for (typename GT::Node_Iterator it(tree); it.has_curr(); it.next_ne())
593 node_infos.insert(it.get_curr()->get_info());
594
595 ASSERT_TRUE(node_infos.count(0));
596 ASSERT_TRUE(node_infos.count(1));
597 ASSERT_TRUE(node_infos.count(2));
598 ASSERT_TRUE(node_infos.count(3));
599}
600
601// ========== TEST 20: Tree is Connected ==========
603
604 GT g;
605 const int num_nodes = 8;
606 std::vector<Node*> nodes;
607
608 for (int i = 0; i < num_nodes; ++i)
609 nodes.push_back(g.insert_node(i));
610
611 // Create random-ish connected graph
612 std::mt19937 rng(12345);
613 for (int i = 0; i < num_nodes - 1; ++i)
614 g.insert_arc(nodes[i], nodes[i+1], rng() % 10 + 1);
615
616 // Add some cross edges
617 g.insert_arc(nodes[0], nodes[4], rng() % 10 + 1);
618 g.insert_arc(nodes[2], nodes[6], rng() % 10 + 1);
619
621 GT tree;
622 kruskal(g, tree);
623
625}
626
627// ========== TEST 21: Bit Flags Correctly Set ==========
629
630 GT g;
631 Node* n0 = g.insert_node(0);
632 Node* n1 = g.insert_node(1);
633 Node* n2 = g.insert_node(2);
634
635 Arc* a01 = g.insert_arc(n0, n1, 1);
636 Arc* a12 = g.insert_arc(n1, n2, 2);
637 Arc* a02 = g.insert_arc(n0, n2, 10); // Should not be in MST
638
640 kruskal(g);
641
645}
646
647// ========== TEST 22: Node Mapping Correct ==========
649
650 GT g;
651 Node* n0 = g.insert_node(100);
652 Node* n1 = g.insert_node(200);
653 Node* n2 = g.insert_node(300);
654
655 g.insert_arc(n0, n1, 1);
656 g.insert_arc(n1, n2, 2);
657
659 GT tree;
660 kruskal(g, tree);
661
662 // Check that tree nodes have same info as original nodes
663 std::set<int> tree_infos;
664 for (typename GT::Node_Iterator it(tree); it.has_curr(); it.next_ne())
665 tree_infos.insert(it.get_curr()->get_info());
666
667 ASSERT_TRUE(tree_infos.count(100));
668 ASSERT_TRUE(tree_infos.count(200));
669 ASSERT_TRUE(tree_infos.count(300));
670}
671
672// ========== TEST 23: Arc Mapping Correct ==========
674
675 GT g;
676 Node* n0 = g.insert_node(0);
677 Node* n1 = g.insert_node(1);
678 Node* n2 = g.insert_node(2);
679
680 g.insert_arc(n0, n1, 5);
681 g.insert_arc(n1, n2, 7);
682
684 GT tree;
685 kruskal(g, tree);
686
687 // Check that tree arcs have same weights as original arcs
688 std::set<int> tree_weights;
689 for (typename GT::Arc_Iterator it(tree); it.has_curr(); it.next_ne())
690 tree_weights.insert(it.get_curr()->get_info());
691
692 ASSERT_TRUE(tree_weights.count(5));
693 ASSERT_TRUE(tree_weights.count(7));
694}
695
696// ========== TEST 24: Digraph Rejection ==========
698
700
701 DGT dg;
702 auto n0 = dg.insert_node(0);
703 auto n1 = dg.insert_node(1);
704 dg.insert_arc(n0, n1, 1);
705
707 EXPECT_THROW(kruskal(dg), std::domain_error);
708}
709
710// ========== TEST 27: Empty Graph ==========
712
713 GT g; // Empty graph
714
716 GT tree;
717 kruskal(g, tree);
718
719 ASSERT_EQ(tree.get_num_nodes(), 0u);
720 ASSERT_EQ(tree.get_num_arcs(), 0u);
721}
722
723// ========== TEST 28: Custom Distance Functor ==========
725
726 GT g;
727 Node* n0 = g.insert_node(0);
728 Node* n1 = g.insert_node(1);
729 Node* n2 = g.insert_node(2);
730
731 g.insert_arc(n0, n1, 2); // scaled = 4
732 g.insert_arc(n1, n2, 3); // scaled = 6
733 g.insert_arc(n0, n2, 4); // scaled = 8
734
735 // Use default constructor and let it pick the default Distance
737 GT tree;
738 kruskal(g, tree);
739
740 // MST should still pick (0,1) and (1,2)
741 ASSERT_EQ(tree.get_num_arcs(), 2u);
742 // Original weights: 2 + 3 = 5
744}
745
746// ========== TEST 29: Array Graph Type ==========
748
749 AGT g;
750 ANode* n0 = g.insert_node(0);
751 ANode* n1 = g.insert_node(1);
752 ANode* n2 = g.insert_node(2);
753
754 g.insert_arc(n0, n1, 1);
755 g.insert_arc(n1, n2, 2);
756 g.insert_arc(n0, n2, 5);
757
759 AGT tree;
760 kruskal(g, tree);
761
762 ASSERT_EQ(tree.get_num_nodes(), 3u);
763 ASSERT_EQ(tree.get_num_arcs(), 2u);
765}
766
767// ========== TEST 30: Multiple Calls on Same Instance ==========
769
770 GT g1;
771 Node* n0 = g1.insert_node(0);
772 Node* n1 = g1.insert_node(1);
773 g1.insert_arc(n0, n1, 5);
774
775 GT g2;
776 Node* m0 = g2.insert_node(0);
777 Node* m1 = g2.insert_node(1);
778 Node* m2 = g2.insert_node(2);
779 g2.insert_arc(m0, m1, 1);
780 g2.insert_arc(m1, m2, 2);
781
783
784 GT tree1;
785 kruskal(g1, tree1);
786 ASSERT_EQ(tree1.get_num_arcs(), 1u);
787
788 GT tree2;
789 kruskal(g2, tree2);
790 ASSERT_EQ(tree2.get_num_arcs(), 2u);
791}
792
793// ========== TEST 31: Large Connected Graph ==========
795
796 GT g;
797 const int num_nodes = 100;
798 std::vector<Node*> nodes;
799
800 for (int i = 0; i < num_nodes; ++i)
801 nodes.push_back(g.insert_node(i));
802
803 // Create a chain to ensure connectivity
804 for (int i = 0; i < num_nodes - 1; ++i)
805 g.insert_arc(nodes[i], nodes[i+1], i + 1);
806
807 // Add random edges
808 std::mt19937 rng(99999);
809 std::uniform_int_distribution<int> ndist(0, num_nodes - 1);
810 std::uniform_int_distribution<int> wdist(1, 1000);
811
812 for (int e = 0; e < num_nodes * 3; ++e)
813 {
814 int u = ndist(rng);
815 int v = ndist(rng);
816 if (u != v)
817 g.insert_arc(nodes[u], nodes[v], wdist(rng));
818 }
819
821 GT tree;
822 kruskal(g, tree);
823
824 ASSERT_EQ(tree.get_num_nodes(), static_cast<size_t>(num_nodes));
825 ASSERT_EQ(tree.get_num_arcs(), static_cast<size_t>(num_nodes - 1));
827}
828
829// ========== TEST 32: Paint Then Build Tree ==========
831
832 GT g;
833 Node* n0 = g.insert_node(0);
834 Node* n1 = g.insert_node(1);
835 Node* n2 = g.insert_node(2);
836
837 g.insert_arc(n0, n1, 1);
838 g.insert_arc(n1, n2, 2);
839 g.insert_arc(n0, n2, 5);
840
842
843 // First paint
844 kruskal(g);
846
847 // Then build tree
848 GT tree;
849 kruskal(g, tree);
850 int tree_weight = tree_total_weight(tree);
851
852 // Both should have same weight
854}
855
856// ========== TEST 33: Disconnected Graph Creates Forest ==========
858
859 GT g;
860 // Component 1
861 Node* n0 = g.insert_node(0);
862 Node* n1 = g.insert_node(1);
863 g.insert_arc(n0, n1, 1);
864
865 // Component 2 (disconnected)
866 Node* n2 = g.insert_node(2);
867 Node* n3 = g.insert_node(3);
868 g.insert_arc(n2, n3, 2);
869
871 GT tree;
872 kruskal(g, tree);
873
874 // Should create a forest with 2 trees
875 ASSERT_EQ(tree.get_num_nodes(), 4u);
876 ASSERT_EQ(tree.get_num_arcs(), 2u); // Two separate edges
877}
878
879// ========== TEST 34: Verify Minimum Weight Property ==========
881
882 GT g;
883 Node* n0 = g.insert_node(0);
884 Node* n1 = g.insert_node(1);
885 Node* n2 = g.insert_node(2);
886 Node* n3 = g.insert_node(3);
887
888 // Create graph with known MST
889 g.insert_arc(n0, n1, 1); // in MST
890 g.insert_arc(n0, n2, 3); // in MST
891 g.insert_arc(n0, n3, 10); // not in MST
892 g.insert_arc(n1, n2, 5); // not in MST
893 g.insert_arc(n1, n3, 4); // in MST
894 g.insert_arc(n2, n3, 6); // not in MST
895
897 GT tree;
898 kruskal(g, tree);
899
900 // MST weight should be: 1 + 3 + 4 = 8
902
903 // Verify this is minimum by checking against all spanning tree possibilities
904 // (For this simple graph, we can verify manually)
905 ASSERT_LT(tree_total_weight(tree), 1 + 3 + 6); // alt tree
906 ASSERT_LT(tree_total_weight(tree), 1 + 5 + 4); // alt tree
907 ASSERT_LT(tree_total_weight(tree), 1 + 10 + 6); // alt tree
908}
909
910// ========== TEST 35: Repeated Edges Same Nodes ==========
912
913 GT g;
914 Node* n0 = g.insert_node(0);
915 Node* n1 = g.insert_node(1);
916 Node* n2 = g.insert_node(2);
917
918 // Multiple edges between pairs
919 g.insert_arc(n0, n1, 10);
920 g.insert_arc(n0, n1, 1); // min
921 g.insert_arc(n1, n2, 8);
922 g.insert_arc(n1, n2, 2); // min
923
925 GT tree;
926 kruskal(g, tree);
927
928 // Should pick minimum edges
929 ASSERT_EQ(tree_total_weight(tree), 3); // 1 + 2
930}
931
932// ========== TEST 36: is_painted() Getter ==========
934
935 GT g;
936 Node* n0 = g.insert_node(0);
937 Node* n1 = g.insert_node(1);
938 Node* n2 = g.insert_node(2);
939
940 g.insert_arc(n0, n1, 1);
941 g.insert_arc(n1, n2, 2);
942
944
945 // Before painting
946 ASSERT_FALSE(kruskal.is_painted());
947
948 // After painting
949 kruskal(g);
950 ASSERT_TRUE(kruskal.is_painted());
951
952 // After painting to tree (should still be true)
953 GT tree;
954 kruskal(g, tree);
955 ASSERT_TRUE(kruskal.is_painted());
956}
957
958// ========== GoogleTest main ==========
959int main(int argc, char **argv)
960{
961 ::testing::InitGoogleTest(&argc, argv);
962 return RUN_ALL_TESTS();
963}
Kruskal's minimum spanning tree algorithm.
int main()
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
int num_nodes
Definition btreepic.C:410
virtual Node * insert_node(Node *p)
Definition tpl_agraph.H:393
__Graph_Node Node
Definition tpl_agraph.H:277
Arc * insert_arc(Node *src, Node *tgt, void *a)
Definition tpl_agraph.H:456
__Graph_Arc Arc
Definition tpl_agraph.H:278
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
T & append(const T &item)
Append a new item by copy.
Definition htlist.H:1562
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
Computes the minimum spanning tree of a graph using Kruskal's algorithm.
Definition Kruskal.H:90
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
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
constexpr size_t get_num_arcs() const noexcept
Definition graph-dry.H:778
#define TEST(name)
static mt19937 rng
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 IS_ARC_VISITED(p, bit)
Determine whether the bit field is or not set to one.
#define NODE_BITS(p)
Get the control bits of a node.
@ Spanning_Tree
Definition aleph-graph.H:79
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
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
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1119
bool operator()(typename GT::Arc *a) const
Definition karger.cc:553
AGT::Node ANode
AGT::Arc AArc
Array-based graph implementation.
Generic graph and digraph implementations.
Simple graph implementation with adjacency lists.