Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
graph_functional_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
45#include <gtest/gtest.h>
46#include <tpl_graph.H>
47#include <tpl_sgraph.H>
48#include <tpl_agraph.H>
49
50using namespace Aleph;
51
52// ==================== Type definitions ====================
53
54// List-based implementations with Dlink (use Graph_Node / Graph_Arc)
59
60// Sparse list implementations with DynList (use Graph_Snode / Graph_Sarc)
65
66// Array-based implementations (use Graph_Anode / Graph_Aarc)
71
72// For backward compatibility with existing tests
75
76class GraphFunctionalTest : public ::testing::Test
77{
78protected:
81
84
87
88 void SetUp() override
89 {
90 // Build undirected graph:
91 // n1(1) --1.0-- n2(2) --2.0-- n3(3)
92 // | |
93 // 3.0 4.0
94 // | |
95 // n4(4) -------n5(5)
96
97 n1 = g.insert_node(1);
98 n2 = g.insert_node(2);
99 n3 = g.insert_node(3);
100 n4 = g.insert_node(4);
101 n5 = g.insert_node(5);
102
103 a1 = g.insert_arc(n1, n2, 1.0);
104 a2 = g.insert_arc(n2, n3, 2.0);
105 a3 = g.insert_arc(n1, n4, 3.0);
106 a4 = g.insert_arc(n2, n5, 4.0);
107
108 // Build directed graph:
109 // dn1(10) --1.5--> dn2(20) --2.5--> dn3(30)
110 // | ^ |
111 // | | |
112 // 0.5 3.5 4.5
113 // | | |
114 // v | v
115 // dn4(40) -----------+ dn3
116
117 dn1 = dg.insert_node(10);
118 dn2 = dg.insert_node(20);
119 dn3 = dg.insert_node(30);
120 dn4 = dg.insert_node(40);
121
122 da1 = dg.insert_arc(dn1, dn2, 1.5);
123 da2 = dg.insert_arc(dn2, dn3, 2.5);
124 da3 = dg.insert_arc(dn1, dn4, 0.5);
125 da4 = dg.insert_arc(dn4, dn2, 3.5);
126 da5 = dg.insert_arc(dn3, dn3, 4.5); // self-loop
127 }
128};
129
130// ==================== none_node tests ====================
131
133{
134 // No node has value > 100
135 EXPECT_TRUE(g.none_node([](auto p) { return p->get_info() > 100; }));
136 EXPECT_TRUE(dg.none_node([](auto p) { return p->get_info() > 100; }));
137}
138
140{
141 // Some nodes have value > 2
142 EXPECT_FALSE(g.none_node([](auto p) { return p->get_info() > 2; }));
143 EXPECT_FALSE(dg.none_node([](auto p) { return p->get_info() > 20; }));
144}
145
147{
148 TestGraph empty;
149 EXPECT_TRUE(empty.none_node([](auto) { return true; }));
150}
151
152// ==================== none_arc tests ====================
153
155{
156 // No arc has weight > 100
157 EXPECT_TRUE(g.none_arc([](auto a) { return a->get_info() > 100.0; }));
158 EXPECT_TRUE(dg.none_arc([](auto a) { return a->get_info() > 100.0; }));
159}
160
162{
163 // Some arcs have weight > 2
164 EXPECT_FALSE(g.none_arc([](auto a) { return a->get_info() > 2.0; }));
165 EXPECT_FALSE(dg.none_arc([](auto a) { return a->get_info() > 2.0; }));
166}
167
169{
170 // n1 has arcs with weights 1.0 and 3.0, none > 10
171 EXPECT_TRUE(g.none_arc(n1, [](auto a) { return a->get_info() > 10.0; }));
172
173 // n1 has arc with weight 3.0
174 EXPECT_FALSE(g.none_arc(n1, [](auto a) { return a->get_info() > 2.5; }));
175}
176
177// ==================== count_nodes tests ====================
178
180{
181 EXPECT_EQ(g.count_nodes(), 5u);
182 EXPECT_EQ(dg.count_nodes(), 4u);
183}
184
186{
187 // Count nodes with value > 2
188 EXPECT_EQ(g.count_nodes([](auto p) { return p->get_info() > 2; }), 3u);
189
190 // Count nodes with even value
191 EXPECT_EQ(g.count_nodes([](auto p) { return p->get_info() % 2 == 0; }), 2u);
192
193 // Count digraph nodes > 25
194 EXPECT_EQ(dg.count_nodes([](auto p) { return p->get_info() > 25; }), 2u);
195}
196
198{
199 EXPECT_EQ(g.count_nodes([](auto p) { return p->get_info() > 100; }), 0u);
200}
201
207
208// ==================== count_arcs tests ====================
209
211{
212 EXPECT_EQ(g.count_arcs(), 4u);
213 EXPECT_EQ(dg.count_arcs(), 5u);
214}
215
217{
218 // Count arcs with weight > 2
219 EXPECT_EQ(g.count_arcs([](auto a) { return a->get_info() > 2.0; }), 2u);
220
221 // Count arcs with weight <= 2
222 EXPECT_EQ(g.count_arcs([](auto a) { return a->get_info() <= 2.0; }), 2u);
223
224 // Count digraph arcs > 2
225 EXPECT_EQ(dg.count_arcs([](auto a) { return a->get_info() > 2.0; }), 3u);
226}
227
229{
230 // n2 has 3 adjacent arcs
231 EXPECT_EQ(g.count_arcs(n2), 3u);
232
233 // n1 has 2 adjacent arcs
234 EXPECT_EQ(g.count_arcs(n1), 2u);
235
236 // Count arcs adjacent to n2 with weight > 1.5
237 EXPECT_EQ(g.count_arcs(n2, [](auto a) { return a->get_info() > 1.5; }), 2u);
238}
239
245
246// ==================== sum_arcs tests ====================
247
249{
250 // n1 has arcs with weights 1.0 + 3.0 = 4.0
251 double sum = g.sum_arcs<double>(n1);
252 EXPECT_DOUBLE_EQ(sum, 4.0);
253
254 // n2 has arcs with weights 1.0 + 2.0 + 4.0 = 7.0
255 sum = g.sum_arcs<double>(n2);
256 EXPECT_DOUBLE_EQ(sum, 7.0);
257}
258
260{
261 auto isolated = g.insert_node(100);
262 double sum = g.sum_arcs<double>(isolated);
263 EXPECT_DOUBLE_EQ(sum, 0.0);
264}
265
267{
268 // Sum using custom extractor
269 double sum = g.sum_arcs<double>(n2, [](auto a) { return a->get_info() * 2; });
270 EXPECT_DOUBLE_EQ(sum, 14.0); // (1 + 2 + 4) * 2
271}
272
273// ==================== min_arc tests ====================
274
276{
277 // n2's minimum arc should be weight 1.0 (to n1)
278 auto min_a = g.min_arc(n2);
279 ASSERT_NE(min_a, nullptr);
280 EXPECT_DOUBLE_EQ(min_a->get_info(), 1.0);
281}
282
284{
285 // Global minimum arc is weight 1.0
286 auto min_a = g.min_arc();
287 ASSERT_NE(min_a, nullptr);
288 EXPECT_DOUBLE_EQ(min_a->get_info(), 1.0);
289
290 // Digraph global minimum is 0.5
291 auto min_da = dg.min_arc();
292 ASSERT_NE(min_da, nullptr);
293 EXPECT_DOUBLE_EQ(min_da->get_info(), 0.5);
294}
295
297{
298 auto isolated = g.insert_node(100);
299 auto min_a = g.min_arc(isolated);
300 EXPECT_EQ(min_a, nullptr);
301}
302
304{
305 TestGraph empty;
306 auto min_a = empty.min_arc();
307 EXPECT_EQ(min_a, nullptr);
308}
309
311{
312 // Find arc with maximum weight (reverse comparator)
313 auto max_via_min = g.min_arc(n2, [](auto a, auto b) {
314 return a->get_info() > b->get_info();
315 });
316 ASSERT_NE(max_via_min, nullptr);
317 EXPECT_DOUBLE_EQ(max_via_min->get_info(), 4.0);
318}
319
320// ==================== max_arc tests ====================
321
323{
324 // n2's maximum arc should be weight 4.0 (to n5)
325 auto max_a = g.max_arc(n2);
326 ASSERT_NE(max_a, nullptr);
327 EXPECT_DOUBLE_EQ(max_a->get_info(), 4.0);
328}
329
331{
332 // Global maximum arc is weight 4.0
333 auto max_a = g.max_arc();
334 ASSERT_NE(max_a, nullptr);
335 EXPECT_DOUBLE_EQ(max_a->get_info(), 4.0);
336
337 // Digraph global maximum is 4.5 (self-loop)
338 auto max_da = dg.max_arc();
339 ASSERT_NE(max_da, nullptr);
340 EXPECT_DOUBLE_EQ(max_da->get_info(), 4.5);
341}
342
344{
345 auto isolated = g.insert_node(100);
346 auto max_a = g.max_arc(isolated);
347 EXPECT_EQ(max_a, nullptr);
348}
349
350// ==================== partition_nodes tests ====================
351
353{
354 auto [high, low] = g.partition_nodes([](auto p) { return p->get_info() > 2; });
355
356 EXPECT_EQ(high.size(), 3u); // 3, 4, 5
357 EXPECT_EQ(low.size(), 2u); // 1, 2
358
359 // Verify contents
360 EXPECT_TRUE(high.exists([](auto p) { return p->get_info() == 3; }));
361 EXPECT_TRUE(high.exists([](auto p) { return p->get_info() == 4; }));
362 EXPECT_TRUE(high.exists([](auto p) { return p->get_info() == 5; }));
363
364 EXPECT_TRUE(low.exists([](auto p) { return p->get_info() == 1; }));
365 EXPECT_TRUE(low.exists([](auto p) { return p->get_info() == 2; }));
366}
367
369{
370 auto [match, nomatch] = g.partition_nodes([](auto p) { return p->get_info() > 0; });
371
372 EXPECT_EQ(match.size(), 5u);
373 EXPECT_EQ(nomatch.size(), 0u);
374}
375
377{
378 auto [match, nomatch] = g.partition_nodes([](auto p) { return p->get_info() > 100; });
379
380 EXPECT_EQ(match.size(), 0u);
381 EXPECT_EQ(nomatch.size(), 5u);
382}
383
385{
386 TestGraph empty;
387 auto [match, nomatch] = empty.partition_nodes([](auto) { return true; });
388
389 EXPECT_EQ(match.size(), 0u);
390 EXPECT_EQ(nomatch.size(), 0u);
391}
392
393// ==================== partition_arcs tests ====================
394
396{
397 auto [heavy, light] = g.partition_arcs([](auto a) { return a->get_info() > 2.0; });
398
399 EXPECT_EQ(heavy.size(), 2u); // 3.0, 4.0
400 EXPECT_EQ(light.size(), 2u); // 1.0, 2.0
401}
402
404{
405 auto [high, low] = dg.partition_arcs([](auto a) { return a->get_info() > 2.0; });
406
407 EXPECT_EQ(high.size(), 3u); // 2.5, 3.5, 4.5
408 EXPECT_EQ(low.size(), 2u); // 1.5, 0.5
409}
410
411// ==================== adjacent_nodes tests ====================
412
414{
415 // n2 is connected to n1, n3, n5
416 auto neighbors = g.adjacent_nodes(n2);
417 EXPECT_EQ(neighbors.size(), 3u);
418
419 EXPECT_TRUE(neighbors.exists([this](auto p) { return p == n1; }));
420 EXPECT_TRUE(neighbors.exists([this](auto p) { return p == n3; }));
421 EXPECT_TRUE(neighbors.exists([this](auto p) { return p == n5; }));
422}
423
425{
426 // dn1 has outgoing arcs to dn2 and dn4
427 auto neighbors = dg.adjacent_nodes(dn1);
428 EXPECT_EQ(neighbors.size(), 2u);
429
430 EXPECT_TRUE(neighbors.exists([this](auto p) { return p == dn2; }));
431 EXPECT_TRUE(neighbors.exists([this](auto p) { return p == dn4; }));
432}
433
435{
436 auto isolated = g.insert_node(100);
437 auto neighbors = g.adjacent_nodes(isolated);
438 EXPECT_EQ(neighbors.size(), 0u);
439}
440
442{
443 // dn3 has a self-loop, so itself is a neighbor
444 auto neighbors = dg.adjacent_nodes(dn3);
445 EXPECT_TRUE(neighbors.exists([this](auto p) { return p == dn3; }));
446}
447
448// ==================== Combination tests ====================
449
451{
452 auto pred = [](auto p) { return p->get_info() > 2; };
453
454 size_t count = g.count_nodes(pred);
455 auto [yes, no] = g.partition_nodes(pred);
456
458 EXPECT_EQ(g.vsize() - count, no.size());
459}
460
462{
463 auto pred = [](auto p) { return p->get_info() > 100; };
464
465 EXPECT_EQ(g.none_node(pred), not g.exists_node(pred));
466 EXPECT_EQ(g.none_arc([](auto a) { return a->get_info() > 100; }),
467 not g.exists_arc([](auto a) { return a->get_info() > 100; }));
468}
469
471{
472 auto min_a = g.min_arc();
473 auto max_a = g.max_arc();
474
475 ASSERT_NE(min_a, nullptr);
476 ASSERT_NE(max_a, nullptr);
477
478 // Min should be <= max
479 EXPECT_LE(min_a->get_info(), max_a->get_info());
480
481 // No arc should be smaller than min
482 EXPECT_TRUE(g.none_arc([min_a](auto a) {
483 return a->get_info() < min_a->get_info();
484 }));
485
486 // No arc should be larger than max
487 EXPECT_TRUE(g.none_arc([max_a](auto a) {
488 return a->get_info() > max_a->get_info();
489 }));
490}
491
492// ==================== Edge cases ====================
493
495{
497 auto n = single.insert_node(42);
498
499 EXPECT_EQ(single.count_nodes(), 1u);
500 EXPECT_EQ(single.count_arcs(), 0u);
501 EXPECT_TRUE(single.none_arc([](auto) { return true; }));
502 EXPECT_EQ(single.adjacent_nodes(n).size(), 0u);
503 EXPECT_EQ(single.min_arc(), nullptr);
504 EXPECT_EQ(single.max_arc(), nullptr);
505}
506
508{
509 auto n = g.insert_node(99);
510 auto self = g.insert_arc(n, n, 10.0);
511
512 // Self-loop should be counted
513 EXPECT_EQ(g.count_arcs(n), 1u);
514
515 // min/max of single arc
516 EXPECT_EQ(g.min_arc(n), self);
517 EXPECT_EQ(g.max_arc(n), self);
518
519 // Sum of self-loop
520 EXPECT_DOUBLE_EQ(g.sum_arcs<double>(n), 10.0);
521}
522
523// ==================== nodes_map / arcs_map tests ====================
524
526{
527 // Map node values to strings
528 auto strings = nodes_map<TestGraph, std::string>(g, [](auto p) {
529 return std::to_string(p->get_info());
530 });
531
532 EXPECT_EQ(strings.size(), 5u);
533 EXPECT_TRUE(strings.exists([](const auto& s) { return s == "1"; }));
534 EXPECT_TRUE(strings.exists([](const auto& s) { return s == "5"; }));
535}
536
538{
539 // Map arc weights to integers
540 auto weights = arcs_map<TestGraph, int>(g, [](auto a) {
541 return static_cast<int>(a->get_info());
542 });
543
544 EXPECT_EQ(weights.size(), 4u);
545 EXPECT_TRUE(weights.exists([](int w) { return w == 1; }));
546 EXPECT_TRUE(weights.exists([](int w) { return w == 4; }));
547}
548
550{
551 // Map arcs adjacent to n2 to their weights
552 auto weights = arcs_map<TestGraph, double>(g, n2, [](auto a) {
553 return a->get_info();
554 });
555
556 EXPECT_EQ(weights.size(), 3u); // n2 has 3 adjacent arcs
557}
558
559// ==================== filter_nodes / filter_arcs tests ====================
560
562{
563 // Filter nodes with value > 2
564 auto filtered = g.filter_nodes([](auto p) { return p->get_info() > 2; });
565
566 EXPECT_EQ(filtered.size(), 3u);
567 EXPECT_TRUE(filtered.exists([](auto p) { return p->get_info() == 3; }));
568 EXPECT_TRUE(filtered.exists([](auto p) { return p->get_info() == 4; }));
569 EXPECT_TRUE(filtered.exists([](auto p) { return p->get_info() == 5; }));
570}
571
573{
574 // Filter arcs with weight > 2
575 auto filtered = g.filter_arcs([](auto a) { return a->get_info() > 2.0; });
576
577 EXPECT_EQ(filtered.size(), 2u);
578}
579
581{
582 // Filter arcs adjacent to n2 with weight > 1.5
583 auto filtered = g.filter_arcs(n2, [](auto a) { return a->get_info() > 1.5; });
584
585 EXPECT_EQ(filtered.size(), 2u); // weights 2.0 and 4.0
586}
587
588// ==================== all_nodes / all_arcs method tests ====================
589
591{
592 // All nodes have positive values
593 EXPECT_TRUE(g.all_nodes([](auto p) { return p->get_info() > 0; }));
594
595 // Not all nodes have value > 3
596 EXPECT_FALSE(g.all_nodes([](auto p) { return p->get_info() > 3; }));
597}
598
600{
601 // All arcs have positive weights
602 EXPECT_TRUE(g.all_arcs([](auto a) { return a->get_info() > 0.0; }));
603
604 // Not all arcs have weight > 2
605 EXPECT_FALSE(g.all_arcs([](auto a) { return a->get_info() > 2.0; }));
606}
607
609{
610 // All arcs from n1 have weight > 0
611 EXPECT_TRUE(g.all_arcs(n1, [](auto a) { return a->get_info() > 0.0; }));
612
613 // Not all arcs from n1 have weight > 2
614 EXPECT_FALSE(g.all_arcs(n1, [](auto a) { return a->get_info() > 2.0; }));
615}
616
617// ==================== exists_node / exists_arc method tests ====================
618
620{
621 // Exists node with value 3
622 EXPECT_TRUE(g.exists_node([](auto p) { return p->get_info() == 3; }));
623
624 // No node with value 100
625 EXPECT_FALSE(g.exists_node([](auto p) { return p->get_info() == 100; }));
626}
627
629{
630 // Exists arc with weight 3.0
631 EXPECT_TRUE(g.exists_arc([](auto a) { return a->get_info() == 3.0; }));
632
633 // No arc with weight 100.0
634 EXPECT_FALSE(g.exists_arc([](auto a) { return a->get_info() == 100.0; }));
635}
636
638{
639 // n1 has arc with weight 3.0
640 EXPECT_TRUE(g.exists_arc(n1, [](auto a) { return a->get_info() == 3.0; }));
641
642 // n1 has no arc with weight 100.0
643 EXPECT_FALSE(g.exists_arc(n1, [](auto a) { return a->get_info() == 100.0; }));
644}
645
646// ==================== map_in_arcs / map_out_arcs tests (digraph) ====================
647
649{
650 // Map outgoing arcs from dn1 to weights using method
651 double sum = 0;
652 dg.for_each_out_arc(dn1, [&sum](auto a) { sum += a->get_info(); });
653 EXPECT_DOUBLE_EQ(sum, 2.0); // 1.5 + 0.5
654
655 // Verify out_arcs collection
656 auto outs = dg.out_arcs(dn1);
657 EXPECT_EQ(outs.size(), 2u);
658}
659
661{
662 // Note: List_Digraph doesn't maintain incoming arc lists
663 // in_arcs() returns empty for this implementation
664 // This is expected behavior for adjacency-list based digraphs
665
666 // Verify out_arcs works (outgoing)
667 auto outs = dg.out_arcs(dn1);
668 EXPECT_EQ(outs.size(), 2u); // dn1 has 2 outgoing arcs
669}
670
671// ==================== foldl_in_arcs / foldl_out_arcs tests ====================
672
674{
675 // Sum outgoing arc weights from dn1 using for_each
676 double sum = 0.0;
677 dg.for_each_out_arc(dn1, [&sum](auto a) { sum += a->get_info(); });
678
679 EXPECT_DOUBLE_EQ(sum, 2.0); // 1.5 + 0.5
680}
681
683{
684 // Verify outgoing arc traversal works correctly
685 double sum = 0.0;
686 dg.for_each_out_arc(dn1, [&sum](auto a) { sum += a->get_info(); });
687
688 EXPECT_DOUBLE_EQ(sum, 2.0); // 1.5 + 0.5
689}
690
691// ==================== filter_in_arcs / filter_out_arcs tests ====================
692
694{
695 // Filter outgoing arcs from dn1 with weight > 1.0
696 auto filtered = dg.filter_out_arcs(dn1, [](auto a) {
697 return a->get_info() > 1.0;
698 });
699
700 EXPECT_EQ(filtered.size(), 1u); // Only 1.5
701}
702
704{
705 // Filter incoming arcs to dn2 with weight > 2.0
706 auto filtered = dg.filter_in_arcs(dn2, [](auto a) {
707 return a->get_info() > 2.0;
708 });
709
710 EXPECT_EQ(filtered.size(), 0u);
711}
712
713// ==================== search_node / find_node tests ====================
714
716{
717 // Search node with value 3
718 auto found = g.search_node([](auto p) { return p->get_info() == 3; });
719 ASSERT_NE(found, nullptr);
720 EXPECT_EQ(found->get_info(), 3);
721
722 // Search non-existent
723 auto not_found = g.search_node([](auto p) { return p->get_info() == 100; });
724 EXPECT_EQ(not_found, nullptr);
725}
726
728{
729 // Find node with value 3
730 auto found = g.find_node(3);
731 ASSERT_NE(found, nullptr);
732 EXPECT_EQ(found->get_info(), 3);
733
734 // Find non-existent
735 auto not_found = g.find_node(100);
736 EXPECT_EQ(not_found, nullptr);
737}
738
739// ==================== search_arc / find_arc tests ====================
740
742{
743 // Search arc with weight 3.0
744 auto found = g.search_arc([](auto a) { return a->get_info() == 3.0; });
745 ASSERT_NE(found, nullptr);
746 EXPECT_DOUBLE_EQ(found->get_info(), 3.0);
747
748 // Search non-existent
749 auto not_found = g.search_arc([](auto a) { return a->get_info() == 100.0; });
750 EXPECT_EQ(not_found, nullptr);
751}
752
754{
755 // Find arc with weight 3.0
756 auto found = g.find_arc(3.0);
757 ASSERT_NE(found, nullptr);
758 EXPECT_DOUBLE_EQ(found->get_info(), 3.0);
759
760 // Find non-existent
761 auto not_found = g.find_arc(100.0);
762 EXPECT_EQ(not_found, nullptr);
763}
764
766{
767 // Search arc from n1 with weight 3.0
768 auto found = g.search_arc(n1, [](auto a) { return a->get_info() == 3.0; });
769 ASSERT_NE(found, nullptr);
770 EXPECT_DOUBLE_EQ(found->get_info(), 3.0);
771
772 // Search non-existent from n1
773 auto not_found = g.search_arc(n1, [](auto a) { return a->get_info() == 100.0; });
774 EXPECT_EQ(not_found, nullptr);
775}
776
777// ==================== traverse_nodes / traverse_arcs tests ====================
778
780{
781 int count = 0;
782 bool completed = g.traverse_nodes([&count](auto p) {
783 ++count;
784 return p->get_info() < 3; // Stop when value >= 3
785 });
786
787 EXPECT_FALSE(completed); // Should have stopped early
788 EXPECT_LT(count, 5); // Didn't visit all nodes
789}
790
792{
793 int count = 0;
794 bool completed = g.traverse_nodes([&count](auto) {
795 ++count;
796 return true; // Always continue
797 });
798
800 EXPECT_EQ(count, 5);
801}
802
804{
805 int count = 0;
806 bool completed = g.traverse_arcs([&count](auto a) {
807 ++count;
808 return a->get_info() < 3.0; // Stop when weight >= 3.0
809 });
810
812 EXPECT_LT(count, 4);
813}
814
816{
817 int count = 0;
818 bool completed = g.traverse_arcs(n2, [&count](auto) {
819 ++count;
820 return true;
821 });
822
824 EXPECT_EQ(count, 3); // n2 has 3 adjacent arcs
825}
826
827// ==================== for_each method tests ====================
828
830{
831 int sum = 0;
832 g.for_each_node([&sum](auto p) { sum += p->get_info(); });
833 EXPECT_EQ(sum, 15); // 1+2+3+4+5
834}
835
837{
838 double sum = 0;
839 g.for_each_arc([&sum](auto a) { sum += a->get_info(); });
840 EXPECT_DOUBLE_EQ(sum, 10.0); // 1+2+3+4
841}
842
844{
845 double sum = 0;
846 g.for_each_arc(n2, [&sum](auto a) { sum += a->get_info(); });
847 EXPECT_DOUBLE_EQ(sum, 7.0); // 1+2+4
848}
849
850// ==================== foldl method tests ====================
851
853{
854 // Using free function since method requires std::function
856 std::function<int(const int&, TestGraph::Node*)>(
857 [](const int& acc, auto p) { return acc + p->get_info(); }
858 ));
859 EXPECT_EQ(sum, 15);
860}
861
863{
864 // Using free function since method requires std::function
865 double sum = foldl_arcs<TestGraph, double>(g, 0.0,
866 std::function<double(const double&, TestGraph::Arc*)>(
867 [](const double& acc, auto a) { return acc + a->get_info(); }
868 ));
869 EXPECT_DOUBLE_EQ(sum, 10.0);
870}
871
873{
874 // Manual fold using for_each_arc
875 double sum = 0.0;
876 g.for_each_arc(n2, [&sum](auto a) { sum += a->get_info(); });
877 EXPECT_DOUBLE_EQ(sum, 7.0);
878}
879
880// ==================== nodes() / arcs() collection tests ====================
881
883{
884 auto all = g.nodes();
885 EXPECT_EQ(all.size(), 5u);
886}
887
889{
890 auto all = g.arcs();
891 EXPECT_EQ(all.size(), 4u);
892}
893
895{
896 auto adj = g.arcs(n2);
897 EXPECT_EQ(adj.size(), 3u);
898}
899
900// ==================== in_nodes / out_nodes / in_arcs / out_arcs tests ====================
901
903{
904 auto outs = dg.out_nodes(dn1);
905 EXPECT_EQ(outs.size(), 2u); // dn2, dn4
906}
907
909{
910 // Note: List_Digraph doesn't track incoming arcs
911 // in_nodes returns empty for adjacency-list implementations
912 auto ins = dg.in_nodes(dn2);
913 EXPECT_EQ(ins.size(), 0u); // No incoming arcs tracked
914}
915
917{
918 auto outs = dg.out_arcs(dn1);
919 EXPECT_EQ(outs.size(), 2u);
920}
921
923{
924 // Note: List_Digraph doesn't track incoming arcs
925 auto ins = dg.in_arcs(dn2);
926 EXPECT_EQ(ins.size(), 0u); // No incoming arcs tracked
927}
928
929// ==================== in_degree / out_degree tests ====================
930
932{
933 // Note: List_Digraph doesn't track incoming arcs
934 // in_degree returns 0 for all nodes in adjacency-list implementations
935 EXPECT_EQ(dg.in_degree(dn1), 0u);
936 EXPECT_EQ(dg.in_degree(dn2), 0u); // Would be 2 if tracked
937 EXPECT_EQ(dg.in_degree(dn3), 1u); // Only self-loop counted (out=in for self)
938}
939
941{
942 EXPECT_EQ(dg.out_degree(dn1), 2u); // To dn2, dn4
943 EXPECT_EQ(dg.out_degree(dn2), 1u); // To dn3
944 EXPECT_EQ(dg.out_degree(dn3), 1u); // Self-loop
945}
946
948{
949 EXPECT_EQ(g.degree(n1), 2u);
950 EXPECT_EQ(g.degree(n2), 3u);
951 EXPECT_EQ(g.degree(n3), 1u);
952}
953
954// =============================================================================
955// TYPED TESTS: Test all graph implementations (List and Array)
956// =============================================================================
957
958template <typename GraphType>
959class UndirectedGraphTest : public ::testing::Test
960{
961protected:
963 typename GraphType::Node *n1, *n2, *n3;
964 typename GraphType::Arc *a1, *a2;
965
966 void SetUp() override
967 {
968 // Simple triangle: n1 -- n2 -- n3 -- n1
969 n1 = g.insert_node(1);
970 n2 = g.insert_node(2);
971 n3 = g.insert_node(3);
972
973 a1 = g.insert_arc(n1, n2, 1.0);
974 a2 = g.insert_arc(n2, n3, 2.0);
975 }
976};
977
978template <typename GraphType>
979class DirectedGraphTest : public ::testing::Test
980{
981protected:
983 typename GraphType::Node *n1, *n2, *n3;
984 typename GraphType::Arc *a1, *a2;
985
986 void SetUp() override
987 {
988 // Simple chain: n1 -> n2 -> n3
989 n1 = g.insert_node(10);
990 n2 = g.insert_node(20);
991 n3 = g.insert_node(30);
992
993 a1 = g.insert_arc(n1, n2, 1.5);
994 a2 = g.insert_arc(n2, n3, 2.5);
995 }
996};
997
998// Define type lists for parameterized tests
999// All three implementations are tested: List, Sparse, and Array
1000using UndirectedGraphTypes = ::testing::Types<ListGraph, SparseGraph, ArrayGraph>;
1001using DirectedGraphTypes = ::testing::Types<ListDigraph, SparseDigraph, ArrayDigraph>;
1002
1005
1006// ==================== Undirected Graph Tests (List & Array) ====================
1007
1009{
1010 EXPECT_EQ(this->g.get_num_nodes(), 3u);
1011 size_t count = this->g.count_nodes([](auto) { return true; });
1012 EXPECT_EQ(count, 3u);
1013}
1014
1016{
1017 EXPECT_EQ(this->g.get_num_arcs(), 2u);
1018 size_t count = this->g.count_arcs([](auto) { return true; });
1019 EXPECT_EQ(count, 2u);
1020}
1021
1023{
1024 EXPECT_TRUE(this->g.exists_node([](auto p) { return p->get_info() == 2; }));
1025 EXPECT_FALSE(this->g.exists_node([](auto p) { return p->get_info() == 99; }));
1026}
1027
1029{
1030 EXPECT_TRUE(this->g.exists_arc([](auto a) { return a->get_info() == 1.0; }));
1031 EXPECT_FALSE(this->g.exists_arc([](auto a) { return a->get_info() == 99.0; }));
1032}
1033
1035{
1036 EXPECT_TRUE(this->g.none_node([](auto p) { return p->get_info() > 100; }));
1037 EXPECT_FALSE(this->g.none_node([](auto p) { return p->get_info() == 1; }));
1038}
1039
1041{
1042 EXPECT_TRUE(this->g.none_arc([](auto a) { return a->get_info() > 100.0; }));
1043 EXPECT_FALSE(this->g.none_arc([](auto a) { return a->get_info() == 1.0; }));
1044}
1045
1047{
1048 EXPECT_TRUE(this->g.all_nodes([](auto p) { return p->get_info() > 0; }));
1049 EXPECT_FALSE(this->g.all_nodes([](auto p) { return p->get_info() > 2; }));
1050}
1051
1053{
1054 EXPECT_TRUE(this->g.all_arcs([](auto a) { return a->get_info() > 0.0; }));
1055 EXPECT_FALSE(this->g.all_arcs([](auto a) { return a->get_info() > 1.5; }));
1056}
1057
1059{
1060 auto filtered = this->g.filter_nodes([](auto p) { return p->get_info() > 1; });
1061 EXPECT_EQ(filtered.size(), 2u);
1062}
1063
1065{
1066 auto filtered = this->g.filter_arcs([](auto a) { return a->get_info() > 1.0; });
1067 EXPECT_EQ(filtered.size(), 1u);
1068}
1069
1071{
1072 auto found = this->g.search_node([](auto p) { return p->get_info() == 2; });
1073 ASSERT_NE(found, nullptr);
1074 EXPECT_EQ(found->get_info(), 2);
1075
1076 auto not_found = this->g.search_node([](auto p) { return p->get_info() == 99; });
1077 EXPECT_EQ(not_found, nullptr);
1078}
1079
1081{
1082 auto found = this->g.search_arc([](auto a) { return a->get_info() == 2.0; });
1083 ASSERT_NE(found, nullptr);
1084 EXPECT_DOUBLE_EQ(found->get_info(), 2.0);
1085}
1086
1088{
1089 auto [matching, not_matching] = this->g.partition_nodes(
1090 [](auto p) { return p->get_info() > 1; });
1091 EXPECT_EQ(matching.size(), 2u);
1093}
1094
1096{
1097 auto [matching, not_matching] = this->g.partition_arcs(
1098 [](auto a) { return a->get_info() > 1.0; });
1099 EXPECT_EQ(matching.size(), 1u);
1101}
1102
1104{
1105 int sum = 0;
1106 this->g.for_each_node([&sum](auto p) { sum += p->get_info(); });
1107 EXPECT_EQ(sum, 6); // 1 + 2 + 3
1108}
1109
1111{
1112 double sum = 0;
1113 this->g.for_each_arc([&sum](auto a) { sum += a->get_info(); });
1114 EXPECT_DOUBLE_EQ(sum, 3.0); // 1.0 + 2.0
1115}
1116
1118{
1119 EXPECT_EQ(this->g.degree(this->n1), 1u);
1120 EXPECT_EQ(this->g.degree(this->n2), 2u);
1121 EXPECT_EQ(this->g.degree(this->n3), 1u);
1122}
1123
1125{
1126 auto adj = this->g.adjacent_nodes(this->n2);
1127 EXPECT_EQ(adj.size(), 2u);
1128}
1129
1131{
1132 auto min = this->g.min_arc();
1133 ASSERT_NE(min, nullptr);
1134 EXPECT_DOUBLE_EQ(min->get_info(), 1.0);
1135}
1136
1138{
1139 auto max = this->g.max_arc();
1140 ASSERT_NE(max, nullptr);
1141 EXPECT_DOUBLE_EQ(max->get_info(), 2.0);
1142}
1143
1144// ==================== Directed Graph Tests (List & Array) ====================
1145
1147{
1148 EXPECT_EQ(this->g.get_num_nodes(), 3u);
1149 size_t count = this->g.count_nodes([](auto) { return true; });
1150 EXPECT_EQ(count, 3u);
1151}
1152
1154{
1155 EXPECT_EQ(this->g.get_num_arcs(), 2u);
1156 size_t count = this->g.count_arcs([](auto) { return true; });
1157 EXPECT_EQ(count, 2u);
1158}
1159
1161{
1162 EXPECT_TRUE(this->g.exists_node([](auto p) { return p->get_info() == 20; }));
1163 EXPECT_FALSE(this->g.exists_node([](auto p) { return p->get_info() == 99; }));
1164}
1165
1167{
1168 EXPECT_TRUE(this->g.exists_arc([](auto a) { return a->get_info() == 1.5; }));
1169 EXPECT_FALSE(this->g.exists_arc([](auto a) { return a->get_info() == 99.0; }));
1170}
1171
1173{
1174 EXPECT_TRUE(this->g.none_node([](auto p) { return p->get_info() > 100; }));
1175 EXPECT_FALSE(this->g.none_node([](auto p) { return p->get_info() == 10; }));
1176}
1177
1179{
1180 EXPECT_TRUE(this->g.none_arc([](auto a) { return a->get_info() > 100.0; }));
1181 EXPECT_FALSE(this->g.none_arc([](auto a) { return a->get_info() == 1.5; }));
1182}
1183
1185{
1186 EXPECT_TRUE(this->g.all_nodes([](auto p) { return p->get_info() > 0; }));
1187 EXPECT_FALSE(this->g.all_nodes([](auto p) { return p->get_info() > 20; }));
1188}
1189
1191{
1192 EXPECT_TRUE(this->g.all_arcs([](auto a) { return a->get_info() > 0.0; }));
1193 EXPECT_FALSE(this->g.all_arcs([](auto a) { return a->get_info() > 2.0; }));
1194}
1195
1197{
1198 auto filtered = this->g.filter_nodes([](auto p) { return p->get_info() > 10; });
1199 EXPECT_EQ(filtered.size(), 2u);
1200}
1201
1203{
1204 auto filtered = this->g.filter_arcs([](auto a) { return a->get_info() > 1.5; });
1205 EXPECT_EQ(filtered.size(), 1u);
1206}
1207
1209{
1210 auto found = this->g.search_node([](auto p) { return p->get_info() == 20; });
1211 ASSERT_NE(found, nullptr);
1212 EXPECT_EQ(found->get_info(), 20);
1213}
1214
1216{
1217 auto found = this->g.search_arc([](auto a) { return a->get_info() == 2.5; });
1218 ASSERT_NE(found, nullptr);
1219 EXPECT_DOUBLE_EQ(found->get_info(), 2.5);
1220}
1221
1223{
1224 auto [matching, not_matching] = this->g.partition_nodes(
1225 [](auto p) { return p->get_info() > 10; });
1226 EXPECT_EQ(matching.size(), 2u);
1228}
1229
1231{
1232 auto [matching, not_matching] = this->g.partition_arcs(
1233 [](auto a) { return a->get_info() > 1.5; });
1234 EXPECT_EQ(matching.size(), 1u);
1236}
1237
1239{
1240 int sum = 0;
1241 this->g.for_each_node([&sum](auto p) { sum += p->get_info(); });
1242 EXPECT_EQ(sum, 60); // 10 + 20 + 30
1243}
1244
1246{
1247 double sum = 0;
1248 this->g.for_each_arc([&sum](auto a) { sum += a->get_info(); });
1249 EXPECT_DOUBLE_EQ(sum, 4.0); // 1.5 + 2.5
1250}
1251
1253{
1254 EXPECT_EQ(this->g.out_degree(this->n1), 1u);
1255 EXPECT_EQ(this->g.out_degree(this->n2), 1u);
1256 EXPECT_EQ(this->g.out_degree(this->n3), 0u);
1257}
1258
1260{
1261 auto outs = this->g.out_nodes(this->n1);
1262 EXPECT_EQ(outs.size(), 1u);
1263}
1264
1266{
1267 auto outs = this->g.out_arcs(this->n1);
1268 EXPECT_EQ(outs.size(), 1u);
1269}
1270
1272{
1273 auto min = this->g.min_arc();
1274 ASSERT_NE(min, nullptr);
1275 EXPECT_DOUBLE_EQ(min->get_info(), 1.5);
1276}
1277
1279{
1280 auto max = this->g.max_arc();
1281 ASSERT_NE(max, nullptr);
1282 EXPECT_DOUBLE_EQ(max->get_info(), 2.5);
1283}
1284
1285int main(int argc, char **argv)
1286{
1287 ::testing::InitGoogleTest(&argc, argv);
1288 return RUN_ALL_TESTS();
1289}
1290
int main()
long double w
Definition btreepic.C:153
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
Node of Array_Graph
Definition tpl_agraph.H:64
Arc for graphs implemented with simple adjacency lists.
Definition tpl_sgraph.H:197
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
Graph class implemented with singly-linked adjacency lists.
Definition tpl_sgraph.H:274
bool exists(Operation &op) const
Test for existence in the container of an element satisfying a criteria.
Definition ah-dry.H:846
size_t count_nodes(Operation op=[](Node *) { return true;}) const
Count the nodes satisfying a condition.
Definition graph-dry.H:2296
bool none_node(Operation &op) const
Determine if no node satisfies a condition.
Definition graph-dry.H:2220
size_t count_arcs(Operation op=[](Arc *) { return true;}) const
Count the arcs satisfying a condition.
Definition graph-dry.H:2320
Arc * min_arc(Node *p, Compare cmp=[](Arc *a, Arc *b) { return a->get_info()< b->get_info();}) const
Find the minimum arc adjacent to a node.
Definition graph-dry.H:2390
std::pair< DynList< Node * >, DynList< Node * > > partition_nodes(Operation op) const
Partition nodes into two groups based on a predicate.
Definition graph-dry.H:2482
TestDigraph::Node * dn2
TestDigraph::Node * dn3
TestDigraph::Node * dn4
TestDigraph::Node * dn1
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_max_function > > max(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4110
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_min_function > > min(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4111
TEST_F(GraphFunctionalTest, NoneNodeReturnsTrueWhenNoMatch)
List_Digraph< IntNode, DoubleArc > ListDigraph
List_Graph< IntNode, DoubleArc > ListGraph
::testing::Types< ListGraph, SparseGraph, ArrayGraph > UndirectedGraphTypes
::testing::Types< ListDigraph, SparseDigraph, ArrayDigraph > DirectedGraphTypes
TYPED_TEST(UndirectedGraphTest, CountNodes)
TYPED_TEST_SUITE(UndirectedGraphTest, UndirectedGraphTypes)
Freq_Node * pred
Predecessor node in level-order traversal.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
bool all(Container &container, Operation &operation)
Return true if all elements satisfy a predicate.
bool completed() const noexcept
Return true if all underlying iterators are finished.
Definition ah-zip.H:140
static long & low(typename GT::Node *p)
Internal helper: low-link value stored directly in NODE_COOKIE(p).
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
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Node belonging to a graph implemented with a double linked adjacency list.
Definition tpl_graph.H:121
Node for graphs implemented with simple adjacency lists.
Definition tpl_sgraph.H:93
Array-based graph implementation.
Generic graph and digraph implementations.
Simple graph implementation with adjacency lists.