Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
graph_copy_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
52#include <gtest/gtest.h>
53#include <tpl_graph.H>
54#include <vector>
55#include <type_traits>
56
57using namespace Aleph;
58
59// ==================== Type definitions ====================
60
61struct IntNode : public Graph_Node<int>
62{
63 using Graph_Node<int>::Graph_Node;
64};
65
66struct DoubleArc : public Graph_Arc<double>
67{
68 using Graph_Arc<double>::Graph_Arc;
69};
70
73
74// ==================== Test Fixtures ====================
75
76class GraphCopyTest : public ::testing::Test
77{
78protected:
81 std::vector<TestGraph::Node*> nodes;
82 std::vector<TestDigraph::Node*> dnodes;
83
84 void SetUp() override
85 {
86 nodes.clear();
87 dnodes.clear();
88 }
89};
90
91// ==================== Basic Tests ====================
92
94{
96
97 EXPECT_EQ(copy.num_nodes(), 0);
98 EXPECT_EQ(copy.num_arcs(), 0);
99 EXPECT_EQ(copy.get_graph().get_num_nodes(), 0);
100 EXPECT_EQ(copy.get_graph().get_num_arcs(), 0);
101}
102
104{
105 auto* n = g.insert_node(42);
106
108
109 EXPECT_EQ(copy.num_nodes(), 1);
110 EXPECT_EQ(copy.num_arcs(), 0);
111
112 auto* copy_n = copy.get_copy(n);
113 ASSERT_NE(copy_n, nullptr);
114 EXPECT_EQ(copy_n->get_info(), 42);
115 EXPECT_NE(copy_n, n); // Should be a different pointer
116}
117
119{
120 auto* n1 = g.insert_node(1);
121 auto* n2 = g.insert_node(2);
122 g.insert_arc(n1, n2, 3.14);
123
125
126 EXPECT_EQ(copy.num_nodes(), 2);
127 EXPECT_EQ(copy.num_arcs(), 1);
128
129 auto* c1 = copy.get_copy(n1);
130 auto* c2 = copy.get_copy(n2);
131
132 EXPECT_EQ(c1->get_info(), 1);
133 EXPECT_EQ(c2->get_info(), 2);
134
135 // Verify arc exists in copy
136 bool found_arc = false;
137 for (auto it = copy.get_graph().get_arc_it(); it.has_curr(); it.next())
138 {
139 auto* arc = it.get_curr();
140 auto* src = copy.get_graph().get_src_node(arc);
141 auto* tgt = copy.get_graph().get_tgt_node(arc);
142 if ((src == c1 && tgt == c2) || (src == c2 && tgt == c1))
143 {
144 found_arc = true;
145 EXPECT_DOUBLE_EQ(arc->get_info(), 3.14);
146 }
147 }
149}
150
152{
153 auto* n1 = dg.insert_node(1);
154 auto* n2 = dg.insert_node(2);
155 dg.insert_arc(n1, n2, 1.0);
156 dg.insert_arc(n2, n1, 2.0);
157
159
160 EXPECT_EQ(copy.num_nodes(), 2);
161 EXPECT_EQ(copy.num_arcs(), 2);
162
163 auto* c1 = copy.get_copy(n1);
164 auto* c2 = copy.get_copy(n2);
165
166 // Verify both directed arcs
167 int arc_count = 0;
168 for (auto it = copy.get_graph().get_arc_it(); it.has_curr(); it.next())
169 {
170 auto* arc = it.get_curr();
171 auto* src = copy.get_graph().get_src_node(arc);
172 auto* tgt = copy.get_graph().get_tgt_node(arc);
173
174 if (src == c1 && tgt == c2)
175 {
176 EXPECT_DOUBLE_EQ(arc->get_info(), 1.0);
177 arc_count++;
178 }
179 else if (src == c2 && tgt == c1)
180 {
181 EXPECT_DOUBLE_EQ(arc->get_info(), 2.0);
182 arc_count++;
183 }
184 }
186}
187
188// ==================== Lookup Tests ====================
189
191{
192 auto* n1 = g.insert_node(1);
194
195 // Create a node not in the original graph
197 auto* other_node = other.insert_node(999);
198
199 EXPECT_THROW(copy.get_copy(other_node), std::domain_error);
200
201 // Original node should work
202 EXPECT_NO_THROW(copy.get_copy(n1));
203}
204
206{
207 auto* n1 = g.insert_node(1);
209
211 auto* other_node = other.insert_node(999);
212
213 EXPECT_EQ(copy.search_copy(other_node), nullptr);
214 EXPECT_NE(copy.search_copy(n1), nullptr);
215}
216
218{
219 auto* n1 = g.insert_node(1);
221
223 auto* other_node = other.insert_node(999);
224
225 EXPECT_TRUE(copy.has_copy(n1));
226 EXPECT_FALSE(copy.has_copy(other_node));
227}
228
229// ==================== Unmapped Node Tests ====================
230
232{
233 g.insert_node(1);
235
236 EXPECT_EQ(copy.num_nodes(), 1);
237 EXPECT_EQ(copy.get_graph().get_num_nodes(), 1);
238
239 // Insert unmapped node (like dummy node in Johnson)
240 auto* dummy = copy.insert_unmapped_node(999);
241
242 // Mapping count doesn't change
243 EXPECT_EQ(copy.num_nodes(), 1);
244 // But graph has more nodes
245 EXPECT_EQ(copy.get_graph().get_num_nodes(), 2);
246
247 EXPECT_EQ(dummy->get_info(), 999);
248}
249
251{
252 auto* n1 = g.insert_node(1);
253 auto* n2 = g.insert_node(2);
254
256
257 auto* c1 = copy.get_copy(n1);
258 auto* dummy = copy.insert_unmapped_node(0);
259
260 // Connect dummy to all nodes (like Johnson's algorithm)
261 copy.insert_arc(dummy, c1, 0.0);
262 copy.insert_arc(dummy, copy.get_copy(n2), 0.0);
263
264 EXPECT_EQ(copy.get_graph().get_num_arcs(), 2);
265}
266
268{
269 auto* n1 = g.insert_node(1);
271
272 auto* dummy = copy.insert_unmapped_node(999);
273 auto* c1 = copy.get_copy(n1);
274 copy.insert_arc(dummy, c1, 0.0);
275
276 EXPECT_EQ(copy.get_graph().get_num_nodes(), 2);
277 EXPECT_EQ(copy.get_graph().get_num_arcs(), 1);
278
279 copy.remove_node(dummy);
280
281 EXPECT_EQ(copy.get_graph().get_num_nodes(), 1);
282 EXPECT_EQ(copy.get_graph().get_num_arcs(), 0);
283
284 // Original mapping still works
285 EXPECT_EQ(copy.get_copy(n1), c1);
286}
287
288// ==================== Complex Graph Tests ====================
289
291{
292 auto* n1 = g.insert_node(1);
293 auto* n2 = g.insert_node(2);
294 auto* n3 = g.insert_node(3);
295 g.insert_arc(n1, n2, 1.0);
296 g.insert_arc(n2, n3, 2.0);
297 g.insert_arc(n3, n1, 3.0);
298
300
301 EXPECT_EQ(copy.num_nodes(), 3);
302 EXPECT_EQ(copy.num_arcs(), 3);
303
304 auto* c1 = copy.get_copy(n1);
305 auto* c2 = copy.get_copy(n2);
306 auto* c3 = copy.get_copy(n3);
307
308 // All should be different
309 EXPECT_NE(c1, c2);
310 EXPECT_NE(c2, c3);
311 EXPECT_NE(c1, c3);
312
313 // Values should match
314 EXPECT_EQ(c1->get_info(), 1);
315 EXPECT_EQ(c2->get_info(), 2);
316 EXPECT_EQ(c3->get_info(), 3);
317}
318
320{
321 // Component 1
322 auto* n1 = g.insert_node(1);
323 auto* n2 = g.insert_node(2);
324 g.insert_arc(n1, n2, 1.0);
325
326 // Component 2 (isolated)
327 auto* n3 = g.insert_node(3);
328 auto* n4 = g.insert_node(4);
329 g.insert_arc(n3, n4, 2.0);
330
332
333 EXPECT_EQ(copy.num_nodes(), 4);
334 EXPECT_EQ(copy.num_arcs(), 2);
335
336 // All nodes should be mapped
337 EXPECT_TRUE(copy.has_copy(n1));
338 EXPECT_TRUE(copy.has_copy(n2));
339 EXPECT_TRUE(copy.has_copy(n3));
340 EXPECT_TRUE(copy.has_copy(n4));
341}
342
344{
345 auto* n1 = dg.insert_node(1);
346 dg.insert_arc(n1, n1, 5.0);
347
349
350 EXPECT_EQ(copy.num_nodes(), 1);
351 EXPECT_EQ(copy.num_arcs(), 1);
352
353 auto* c1 = copy.get_copy(n1);
354
355 // Verify self-loop
356 bool found = false;
357 for (auto it = copy.get_graph().get_arc_it(); it.has_curr(); it.next())
358 {
359 auto* arc = it.get_curr();
360 auto* src = copy.get_graph().get_src_node(arc);
361 auto* tgt = copy.get_graph().get_tgt_node(arc);
362 if (src == c1 && tgt == c1)
363 {
364 found = true;
365 EXPECT_DOUBLE_EQ(arc->get_info(), 5.0);
366 }
367 }
369}
370
372{
373 auto* n1 = dg.insert_node(1);
374 auto* n2 = dg.insert_node(2);
375 dg.insert_arc(n1, n2, 1.0);
376 dg.insert_arc(n1, n2, 2.0);
377 dg.insert_arc(n1, n2, 3.0);
378
380
381 EXPECT_EQ(copy.num_nodes(), 2);
382 EXPECT_EQ(copy.num_arcs(), 3);
383}
384
385// ==================== ForEach Tests ====================
386
388{
389 g.insert_node(1);
390 g.insert_node(2);
391 g.insert_node(3);
392
394
395 int count = 0;
396 int sum_orig = 0;
397 int sum_copy = 0;
398
399 copy.for_each_mapping([&](TestGraph::Node* orig, TestGraph::Node* cp) {
400 count++;
401 sum_orig += orig->get_info();
402 sum_copy += cp->get_info();
403 });
404
405 EXPECT_EQ(count, 3);
406 EXPECT_EQ(sum_orig, 6); // 1+2+3
407 EXPECT_EQ(sum_copy, 6); // Same values in copy
408}
409
410// ==================== Integration with Algorithms ====================
411
413{
414 // Create a graph suitable for shortest path algorithms
415 auto* s = dg.insert_node(0); // source
416 auto* a = dg.insert_node(1);
417 auto* b = dg.insert_node(2);
418 auto* t = dg.insert_node(3); // target
419
420 dg.insert_arc(s, a, 1.0);
421 dg.insert_arc(s, b, 4.0);
422 dg.insert_arc(a, b, 2.0);
423 dg.insert_arc(a, t, 6.0);
424 dg.insert_arc(b, t, 3.0);
425
427
428 // Verify structure is preserved
429 EXPECT_EQ(copy.get_graph().get_num_nodes(), dg.get_num_nodes());
430 EXPECT_EQ(copy.get_graph().get_num_arcs(), dg.get_num_arcs());
431
432 // Get copied nodes for algorithm use
433 auto* cs = copy.get_copy(s);
434 auto* ct = copy.get_copy(t);
435
436 EXPECT_EQ(cs->get_info(), 0);
437 EXPECT_EQ(ct->get_info(), 3);
438
439 // Verify out-degree is preserved
440 int orig_out_degree = 0;
441 for (auto it = dg.get_out_it(s); it.has_curr(); it.next())
443
444 int copy_out_degree = 0;
445 for (auto it = copy.get_graph().get_out_it(cs); it.has_curr(); it.next())
447
449}
450
452{
453 // Create a larger graph to stress test
454 const int N = 100;
455 std::vector<TestDigraph::Node*> ns;
456
457 for (int i = 0; i < N; ++i)
458 ns.push_back(dg.insert_node(i));
459
460 // Create some edges
461 for (int i = 0; i < N - 1; ++i)
462 dg.insert_arc(ns[i], ns[i + 1], static_cast<double>(i));
463
464 // Add some cross edges
465 for (int i = 0; i < N - 10; i += 10)
466 dg.insert_arc(ns[i], ns[i + 10], static_cast<double>(i * 10));
467
469
470 EXPECT_EQ(copy.num_nodes(), N);
471
472 // Verify all nodes are mapped correctly
473 for (int i = 0; i < N; ++i)
474 {
475 auto* cp = copy.get_copy(ns[i]);
476 ASSERT_NE(cp, nullptr);
477 EXPECT_EQ(cp->get_info(), i);
478 }
479}
480
481// ==================== Clear Test ====================
482
484{
485 auto* n1 = g.insert_node(1);
486 auto* n2 = g.insert_node(2);
487 g.insert_arc(n1, n2, 1.0);
488
490
491 EXPECT_EQ(copy.num_nodes(), 2);
492 EXPECT_EQ(copy.get_graph().get_num_nodes(), 2);
493
494 copy.clear();
495
496 EXPECT_EQ(copy.num_nodes(), 0);
497 EXPECT_EQ(copy.get_graph().get_num_nodes(), 0);
498 EXPECT_EQ(copy.get_graph().get_num_arcs(), 0);
499}
500
502{
503 // Build a graph with some nodes and arcs
504 auto* n1 = g.insert_node(1);
505 auto* n2 = g.insert_node(2);
506 auto* n3 = g.insert_node(3);
507 g.insert_arc(n1, n3, 10);
508
509 // Create a copy
511 EXPECT_EQ(copy1.num_nodes(), 3);
512 EXPECT_EQ(copy1.get_graph().get_num_nodes(), 3);
513 EXPECT_EQ(copy1.get_graph().get_num_arcs(), 1);
514
515 // Move construct from copy1
517 EXPECT_EQ(copy2.num_nodes(), 3);
518 EXPECT_EQ(copy2.get_graph().get_num_nodes(), 3);
519 EXPECT_EQ(copy2.get_graph().get_num_arcs(), 1);
520
521 // Verify copy1 was moved from (should be empty or in valid state)
522 EXPECT_EQ(copy1.get_graph().get_num_nodes(), 0);
523}
524
526{
527 // Build a graph with some nodes and arcs
528 auto* n1 = g.insert_node(1);
529 auto* n2 = g.insert_node(2);
530 auto* n3 = g.insert_node(3);
531 g.insert_arc(n1, n3, 10);
532
533 // Create two copies
536
537 EXPECT_EQ(copy1.num_nodes(), 3);
538 EXPECT_EQ(copy2.num_nodes(), 0);
539
540 // Move assign copy1 to copy2
541 copy2 = std::move(copy1);
542 EXPECT_EQ(copy2.num_nodes(), 3);
543 EXPECT_EQ(copy2.get_graph().get_num_nodes(), 3);
544 EXPECT_EQ(copy2.get_graph().get_num_arcs(), 1);
545
546 // Verify copy1 was moved from
547 EXPECT_EQ(copy1.get_graph().get_num_nodes(), 0);
548}
549
551{
552 // Verify that move operations are declared noexcept
553 EXPECT_TRUE(std::is_nothrow_move_constructible<GraphCopyWithMapping<TestGraph>>::value);
554 EXPECT_TRUE(std::is_nothrow_move_assignable<GraphCopyWithMapping<TestGraph>>::value);
555}
556
557// ==================== Main ====================
558
559int main(int argc, char **argv)
560{
561 ::testing::InitGoogleTest(&argc, argv);
562 return RUN_ALL_TESTS();
563}
int main()
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
Graph copy with explicit node mapping.
Definition tpl_graph.H:3881
void SetUp() override
std::vector< TestDigraph::Node * > dnodes
std::vector< TestGraph::Node * > nodes
#define N
Definition fib.C:294
TEST_F(GraphCopyTest, EmptyGraph)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Itor2 copy(Itor1 sourceBeg, const Itor1 &sourceEnd, Itor2 destBeg)
Copy elements from one range to another.
Definition ahAlgo.H:584
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
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
Generic graph and digraph implementations.