Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
graph_traverse_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 <vector>
40#include <set>
41#include <graph-traverse.H>
42#include <tpl_graph.H>
43
44using namespace Aleph;
45
46// Test graph types
49
50// =============================================================================
51// Test Fixtures
52// =============================================================================
53
54class GraphTraverseTest : public ::testing::Test
55{
56protected:
58 std::vector<TestGraph::Node *> nodes;
59
60 void SetUp() override
61 {
62 // Create a simple connected graph:
63 // 0 --- 1
64 // | |
65 // 2 --- 3 --- 4
66 for (int i = 0; i < 5; ++i)
67 nodes.push_back(g.insert_node(i));
68
69 g.insert_arc(nodes[0], nodes[1], 1.0);
70 g.insert_arc(nodes[0], nodes[2], 2.0);
71 g.insert_arc(nodes[1], nodes[3], 3.0);
72 g.insert_arc(nodes[2], nodes[3], 4.0);
73 g.insert_arc(nodes[3], nodes[4], 5.0);
74 }
75};
76
77class DisconnectedGraphTest : public ::testing::Test
78{
79protected:
81 std::vector<TestGraph::Node *> nodes;
82
83 void SetUp() override
84 {
85 // Create two disconnected components:
86 // Component 1: 0 --- 1 --- 2
87 // Component 2: 3 --- 4
88 for (int i = 0; i < 5; ++i)
89 nodes.push_back(g.insert_node(i));
90
91 g.insert_arc(nodes[0], nodes[1], 1.0);
92 g.insert_arc(nodes[1], nodes[2], 2.0);
93 g.insert_arc(nodes[3], nodes[4], 3.0);
94 }
95};
96
97class TreeGraphTest : public ::testing::Test
98{
99protected:
101 std::vector<TestGraph::Node *> nodes;
102
103 void SetUp() override
104 {
105 /*
106 * Create a tree:
107 * 0
108 * /|\
109 * 1 2 3
110 * /| |
111 * 4 5 6
112 */
113 for (int i = 0; i < 7; ++i)
114 nodes.push_back(g.insert_node(i));
115
116 g.insert_arc(nodes[0], nodes[1], 1.0);
117 g.insert_arc(nodes[0], nodes[2], 2.0);
118 g.insert_arc(nodes[0], nodes[3], 3.0);
119 g.insert_arc(nodes[1], nodes[4], 4.0);
120 g.insert_arc(nodes[1], nodes[5], 5.0);
121 g.insert_arc(nodes[3], nodes[6], 6.0);
122 }
123};
124
125class CyclicGraphTest : public ::testing::Test
126{
127protected:
129 std::vector<TestGraph::Node *> nodes;
130
131 void SetUp() override
132 {
133 // Create a graph with a cycle:
134 // 0 --- 1
135 // | |
136 // 3 --- 2
137 for (int i = 0; i < 4; ++i)
138 nodes.push_back(g.insert_node(i));
139
140 g.insert_arc(nodes[0], nodes[1], 1.0);
141 g.insert_arc(nodes[1], nodes[2], 2.0);
142 g.insert_arc(nodes[2], nodes[3], 3.0);
143 g.insert_arc(nodes[3], nodes[0], 4.0);
144 }
145};
146
147class DigraphTraverseTest : public ::testing::Test
148{
149protected:
151 std::vector<TestDigraph::Node *> nodes;
152
153 void SetUp() override
154 {
155 // Create a simple directed graph:
156 // 0 --> 1 --> 2
157 // | ^
158 // v |
159 // 3 ----------+
160 for (int i = 0; i < 4; ++i)
161 nodes.push_back(g.insert_node(i));
162
163 g.insert_arc(nodes[0], nodes[1], 1.0);
164 g.insert_arc(nodes[1], nodes[2], 2.0);
165 g.insert_arc(nodes[0], nodes[3], 3.0);
166 g.insert_arc(nodes[3], nodes[2], 4.0);
167 }
168};
169
170// =============================================================================
171// DFS Traversal Tests
172// =============================================================================
173
175{
176 using Itor = Node_Arc_Iterator<TestGraph>;
178
179 std::set<int> visited;
180 auto op = [&visited](TestGraph::Node *node) {
181 visited.insert(node->get_info());
182 return true;
183 };
184
185 size_t count = dfs(nodes[0], op);
186
187 EXPECT_EQ(count, 5);
188 EXPECT_EQ(visited.size(), 5);
189 for (int i = 0; i < 5; ++i)
190 EXPECT_TRUE(visited.count(i) > 0);
191}
192
194{
195 using Itor = Node_Arc_Iterator<TestGraph>;
197
198 int first_visited = -1;
199 auto op = [&first_visited](TestGraph::Node *node) {
200 if (first_visited == -1)
201 first_visited = node->get_info();
202 return true;
203 };
204
205 dfs(nodes[2], op);
206
208}
209
211{
212 using Itor = Node_Arc_Iterator<TestGraph>;
214
215 int visit_count = 0;
216 auto op = [&visit_count](TestGraph::Node *) {
217 ++visit_count;
218 return visit_count < 3; // Stop after visiting 3 nodes
219 };
220
221 size_t count = dfs(nodes[0], op);
222
223 EXPECT_EQ(count, 3);
225}
226
227// =============================================================================
228// BFS Traversal Tests
229// =============================================================================
230
232{
233 using Itor = Node_Arc_Iterator<TestGraph>;
235
236 std::set<int> visited;
237 auto op = [&visited](TestGraph::Node *node) {
238 visited.insert(node->get_info());
239 return true;
240 };
241
242 size_t count = bfs(nodes[0], op);
243
244 EXPECT_EQ(count, 5);
245 EXPECT_EQ(visited.size(), 5);
246}
247
249{
250 using Itor = Node_Arc_Iterator<TestGraph>;
252
253 std::vector<int> visit_order;
254 auto op = [&visit_order](TestGraph::Node *node) {
255 visit_order.push_back(node->get_info());
256 return true;
257 };
258
259 bfs(nodes[0], op);
260
261 // First should be root (0)
262 EXPECT_EQ(visit_order[0], 0);
263
264 // Level 1 nodes (1, 2, 3) should come before level 2 nodes (4, 5, 6)
265 std::set<int> level1 = {1, 2, 3};
266 std::set<int> level2 = {4, 5, 6};
267
268 // Indices 1, 2, 3 should be level 1 nodes
269 for (int i = 1; i <= 3; ++i)
270 EXPECT_TRUE(level1.count(visit_order[i]) > 0);
271
272 // Indices 4, 5, 6 should be level 2 nodes
273 for (int i = 4; i <= 6; ++i)
274 EXPECT_TRUE(level2.count(visit_order[i]) > 0);
275}
276
278{
279 using Itor = Node_Arc_Iterator<TestGraph>;
281
282 int visit_count = 0;
283 auto op = [&visit_count](TestGraph::Node *) {
284 ++visit_count;
285 return visit_count < 2;
286 };
287
288 size_t count = bfs(nodes[0], op);
289
290 EXPECT_EQ(count, 2);
291}
292
293// =============================================================================
294// Disconnected Graph Tests
295// =============================================================================
296
298{
299 using Itor = Node_Arc_Iterator<TestGraph>;
301
302 std::set<int> visited;
303 auto op = [&visited](TestGraph::Node *node) {
304 visited.insert(node->get_info());
305 return true;
306 };
307
308 // Start from component 1
309 size_t count = dfs(nodes[0], op);
310
311 EXPECT_EQ(count, 3); // Only nodes 0, 1, 2
312 EXPECT_TRUE(visited.count(0) > 0);
313 EXPECT_TRUE(visited.count(1) > 0);
314 EXPECT_TRUE(visited.count(2) > 0);
315 EXPECT_FALSE(visited.count(3) > 0);
316 EXPECT_FALSE(visited.count(4) > 0);
317}
318
320{
321 using Itor = Node_Arc_Iterator<TestGraph>;
323
324 std::set<int> visited;
325 auto op = [&visited](TestGraph::Node *node) {
326 visited.insert(node->get_info());
327 return true;
328 };
329
330 // Start from component 2
331 size_t count = dfs(nodes[3], op);
332
333 EXPECT_EQ(count, 2); // Only nodes 3, 4
334 EXPECT_TRUE(visited.count(3) > 0);
335 EXPECT_TRUE(visited.count(4) > 0);
336}
337
338// =============================================================================
339// Cyclic Graph Tests
340// =============================================================================
341
343{
344 using Itor = Node_Arc_Iterator<TestGraph>;
346
347 std::set<int> visited;
348 auto op = [&visited](TestGraph::Node *node) {
349 visited.insert(node->get_info());
350 return true;
351 };
352
353 size_t count = dfs(nodes[0], op);
354
355 // Should visit all 4 nodes exactly once despite cycle
356 EXPECT_EQ(count, 4);
357 EXPECT_EQ(visited.size(), 4);
358}
359
361{
362 using Itor = Node_Arc_Iterator<TestGraph>;
364
365 std::set<int> visited;
366 auto op = [&visited](TestGraph::Node *node) {
367 visited.insert(node->get_info());
368 return true;
369 };
370
371 size_t count = bfs(nodes[0], op);
372
373 EXPECT_EQ(count, 4);
374 EXPECT_EQ(visited.size(), 4);
375}
376
377// =============================================================================
378// exec() Method Tests (with arc information)
379// =============================================================================
380
382{
383 using Itor = Node_Arc_Iterator<TestGraph>;
385
386 std::vector<std::pair<int, bool>> visits; // (node_info, has_arc)
387 auto op = [&visits](TestGraph::Node *node, TestGraph::Arc *arc) {
388 visits.push_back({node->get_info(), arc != nullptr});
389 return true;
390 };
391
392 size_t count = dfs.exec(nodes[0], op);
393
394 EXPECT_EQ(count, 5);
395
396 // First node should have nullptr arc
397 EXPECT_EQ(visits[0].first, 0);
399
400 // All other nodes should have an arc
401 for (size_t i = 1; i < visits.size(); ++i)
403}
404
406{
407 using Itor = Node_Arc_Iterator<TestGraph>;
409
410 int visit_count = 0;
411 auto op = [&visit_count](TestGraph::Node *, TestGraph::Arc *) {
412 ++visit_count;
413 return visit_count < 2;
414 };
415
416 size_t count = bfs.exec(nodes[0], op);
417
418 EXPECT_EQ(count, 2);
419}
420
421// =============================================================================
422// Dual Operation Tests (node_op and arc_op)
423// =============================================================================
424
426{
427 using Itor = Node_Arc_Iterator<TestGraph>;
429
430 std::set<int> visited_nodes;
431 int arc_count = 0;
432
433 auto node_op = [&visited_nodes](TestGraph::Node *node) {
434 visited_nodes.insert(node->get_info());
435 return true;
436 };
437
438 auto arc_op = [&arc_count](TestGraph::Arc *) {
439 ++arc_count;
440 return true;
441 };
442
443 auto [nodes_visited, arcs_visited] = dfs(nodes[0], node_op, arc_op);
444
447 // The graph has 5 arcs, DFS visits all of them (tree + back edges)
450}
451
453{
454 using Itor = Node_Arc_Iterator<TestGraph>;
456
457 int node_count = 0;
458 int arc_count = 0;
459
460 auto node_op = [&node_count](TestGraph::Node *) {
461 ++node_count;
462 return node_count < 2; // Stop after 2 nodes
463 };
464
465 auto arc_op = [&arc_count](TestGraph::Arc *) {
466 ++arc_count;
467 return true;
468 };
469
470 auto [nodes_visited, arcs_visited] = dfs(nodes[0], node_op, arc_op);
471
473}
474
476{
477 using Itor = Node_Arc_Iterator<TestGraph>;
479
480 int node_count = 0;
481 int arc_count = 0;
482
483 auto node_op = [&node_count](TestGraph::Node *) {
484 ++node_count;
485 return true;
486 };
487
488 auto arc_op = [&arc_count](TestGraph::Arc *) {
489 ++arc_count;
490 return arc_count < 2; // Stop after 2 arcs
491 };
492
493 auto [nodes_visited, arcs_visited] = dfs(nodes[0], node_op, arc_op);
494
496}
497
498// =============================================================================
499// Digraph Tests (using Node_Arc_Iterator with matching filter)
500// =============================================================================
501
503{
507
508 std::set<int> visited;
509 auto op = [&visited](TestDigraph::Node *node) {
510 visited.insert(node->get_info());
511 return true;
512 };
513
514 size_t count = dfs(nodes[0], op);
515
516 // Should reach all 4 nodes following directed edges
517 EXPECT_EQ(count, 4);
518 EXPECT_EQ(visited.size(), 4);
519}
520
522{
526
527 std::vector<int> visit_order;
528 auto op = [&visit_order](TestDigraph::Node *node) {
529 visit_order.push_back(node->get_info());
530 return true;
531 };
532
533 bfs(nodes[0], op);
534
536 EXPECT_EQ(visit_order[0], 0); // Start node first
537}
538
539// =============================================================================
540// Single Node Graph Tests
541// =============================================================================
542
544{
545 TestGraph g;
546 auto node = g.insert_node(42);
547
548 using Itor = Node_Arc_Iterator<TestGraph>;
550
551 int visited_value = -1;
552 auto op = [&visited_value](TestGraph::Node *n) {
553 visited_value = n->get_info();
554 return true;
555 };
556
557 size_t count = dfs(node, op);
558
559 EXPECT_EQ(count, 1);
561}
562
564{
565 TestGraph g;
566 auto node = g.insert_node(42);
567
568 using Itor = Node_Arc_Iterator<TestGraph>;
570
571 int visited_value = -1;
572 auto op = [&visited_value](TestGraph::Node *n) {
573 visited_value = n->get_info();
574 return true;
575 };
576
577 size_t count = bfs(node, op);
578
579 EXPECT_EQ(count, 1);
581}
582
583// =============================================================================
584// Arc Filter Tests
585// =============================================================================
586
587template <class GT>
589{
590 bool operator()(typename GT::Arc *arc) const
591 {
592 return static_cast<int>(arc->get_info()) % 2 == 0;
593 }
594};
595
597{
600
601 std::set<int> visited;
602 auto op = [&visited](TestGraph::Node *node) {
603 visited.insert(node->get_info());
604 return true;
605 };
606
607 size_t count = dfs(nodes[0], op);
608
609 // With even arc filter, only arcs with weights 2.0 and 4.0 are visible
610 // From node 0: only arc to node 2 (weight 2.0) is visible
611 // From node 2: only arc to node 3 (weight 4.0) is visible
612 // Node 3 has arc to node 4 (weight 5.0 - odd, filtered out)
613 EXPECT_LE(count, 5);
614 EXPECT_TRUE(visited.count(0) > 0);
615 EXPECT_TRUE(visited.count(2) > 0);
616}
617
618// =============================================================================
619// Linear Chain Tests
620// =============================================================================
621
623{
624 TestGraph g;
625 std::vector<TestGraph::Node *> nodes;
626
627 // Create linear chain: 0 -- 1 -- 2 -- 3 -- 4
628 for (int i = 0; i < 5; ++i)
629 nodes.push_back(g.insert_node(i));
630
631 for (int i = 0; i < 4; ++i)
632 g.insert_arc(nodes[i], nodes[i + 1], static_cast<double>(i));
633
634 using Itor = Node_Arc_Iterator<TestGraph>;
636
637 std::vector<int> visit_order;
638 auto op = [&visit_order](TestGraph::Node *node) {
639 visit_order.push_back(node->get_info());
640 return true;
641 };
642
643 dfs(nodes[0], op);
644
646 EXPECT_EQ(visit_order[0], 0); // Must start at 0
647}
648
650{
651 TestGraph g;
652 std::vector<TestGraph::Node *> nodes;
653
654 for (int i = 0; i < 5; ++i)
655 nodes.push_back(g.insert_node(i));
656
657 for (int i = 0; i < 4; ++i)
658 g.insert_arc(nodes[i], nodes[i + 1], static_cast<double>(i));
659
660 using Itor = Node_Arc_Iterator<TestGraph>;
662
663 std::vector<int> visit_order;
664 auto op = [&visit_order](TestGraph::Node *node) {
665 visit_order.push_back(node->get_info());
666 return true;
667 };
668
669 bfs(nodes[0], op);
670
671 // BFS on a linear chain visits in order
673 for (int i = 0; i < 5; ++i)
674 EXPECT_EQ(visit_order[i], i);
675}
676
677// =============================================================================
678// Complete Graph Tests
679// =============================================================================
680
682{
683 TestGraph g;
684 std::vector<TestGraph::Node *> nodes;
685
686 // Create K4 (complete graph with 4 nodes)
687 for (int i = 0; i < 4; ++i)
688 nodes.push_back(g.insert_node(i));
689
690 for (int i = 0; i < 4; ++i)
691 for (int j = i + 1; j < 4; ++j)
692 g.insert_arc(nodes[i], nodes[j], static_cast<double>(i * 10 + j));
693
694 using Itor = Node_Arc_Iterator<TestGraph>;
696
697 std::set<int> visited;
698 auto op = [&visited](TestGraph::Node *node) {
699 visited.insert(node->get_info());
700 return true;
701 };
702
703 size_t count = dfs(nodes[0], op);
704
705 EXPECT_EQ(count, 4);
706 EXPECT_EQ(visited.size(), 4);
707}
708
709// =============================================================================
710// Stress Tests
711// =============================================================================
712
714{
715 TestGraph g;
716 std::vector<TestGraph::Node *> nodes;
717
718 const int N = 1000;
719
720 // Create a chain of 1000 nodes
721 for (int i = 0; i < N; ++i)
722 nodes.push_back(g.insert_node(i));
723
724 for (int i = 0; i < N - 1; ++i)
725 g.insert_arc(nodes[i], nodes[i + 1], 1.0);
726
727 using Itor = Node_Arc_Iterator<TestGraph>;
729
730 int count = 0;
731 auto op = [&count](TestGraph::Node *) {
732 ++count;
733 return true;
734 };
735
736 size_t result = dfs(nodes[0], op);
737
738 EXPECT_EQ(result, N);
739 EXPECT_EQ(count, N);
740}
741
743{
744 TestGraph g;
745 std::vector<TestGraph::Node *> nodes;
746
747 const int N = 1000;
748
749 // Create a star graph (central node connected to all others)
750 for (int i = 0; i < N; ++i)
751 nodes.push_back(g.insert_node(i));
752
753 for (int i = 1; i < N; ++i)
754 g.insert_arc(nodes[0], nodes[i], 1.0);
755
756 using Itor = Node_Arc_Iterator<TestGraph>;
758
759 int count = 0;
760 auto op = [&count](TestGraph::Node *) {
761 ++count;
762 return true;
763 };
764
765 size_t result = bfs(nodes[0], op);
766
767 EXPECT_EQ(result, N);
768 EXPECT_EQ(count, N);
769}
770
771// =============================================================================
772// Main
773// =============================================================================
774
775int main(int argc, char **argv)
776{
777 ::testing::InitGoogleTest(&argc, argv);
778 return RUN_ALL_TESTS();
779}
int main()
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
typename BaseGraph::Node Node
Definition graph-dry.H:3851
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
void SetUp() override
std::vector< TestGraph::Node * > nodes
std::vector< TestDigraph::Node * > nodes
std::vector< TestGraph::Node * > nodes
ArcInfo & get_info() noexcept
Return a modifiable reference to the arc data.
Definition graph-dry.H:595
NodeInfo & get_info() noexcept
Return a modifiable reference to the data contained in the node.
Definition graph-dry.H:494
std::vector< TestGraph::Node * > nodes
Traverse a graph depth-first or breadth-first and execute a visit function.
size_t exec(typename GT::Node *start, Op &op)
Execute operation op(curr, arc) where curr is the visited node and arc is the incoming arc.
Tree graph for graph_to_tree conversion testing.
void SetUp() override
std::vector< TestGraph::Node * > nodes
#define TEST(name)
#define N
Definition fib.C:294
Graph traversal algorithms (DFS, BFS).
TEST_F(GraphTraverseTest, DFSVisitsAllNodes)
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
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 *arc) const
Generic graph and digraph implementations.