Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
topological_sort_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
38#include <gtest/gtest.h>
39#include <set>
40#include <map>
41#include <string>
42
43#include <topological_sort.H>
44#include <tpl_graph.H>
45
48
49template <class List>
51{
52 // Build position map
53 std::map<TestDigraph::Node*, size_t> pos;
54 size_t idx = 0;
55 for (auto it = order.get_it(); it.has_curr(); it.next_ne(), ++idx)
56 pos[it.get_curr()] = idx;
57
58 // Check all arcs: src must come before tgt
59 for (Aleph::Arc_Iterator<TestDigraph> it(g); it.has_curr(); it.next_ne())
60 {
61 auto arc = it.get_curr();
62 auto src = g.get_src_node(arc);
63 auto tgt = g.get_tgt_node(arc);
64
65 if (pos.find(src) == pos.end() || pos.find(tgt) == pos.end())
66 return false;
67
68 if (pos[src] >= pos[tgt])
69 return false;
70 }
71
72 return true;
73}
74
75// ==================== Topological_Sort (DFS) Tests ====================
76
78{
81
82 auto result = sorter.perform<Aleph::DynList>(g);
83 EXPECT_TRUE(result.is_empty());
84}
85
87{
89 auto n = g.insert_node("A");
90
92 auto result = sorter.perform<Aleph::DynList>(g);
93
94 ASSERT_EQ(result.size(), 1);
95 EXPECT_EQ(result.get_first(), n);
96}
97
99{
100 TestDigraph g;
101 auto a = g.insert_node("A");
102 auto b = g.insert_node("B");
103 g.insert_arc(a, b);
104
106 auto result = sorter.perform<Aleph::DynList>(g);
107
108 ASSERT_EQ(result.size(), 2);
110 // A must come before B
111 EXPECT_EQ(result.get_first(), a);
112 EXPECT_EQ(result.get_last(), b);
113}
114
116{
117 TestDigraph g;
118 auto a = g.insert_node("A");
119 auto b = g.insert_node("B");
120 auto c = g.insert_node("C");
121 auto d = g.insert_node("D");
122
123 g.insert_arc(a, b);
124 g.insert_arc(b, c);
125 g.insert_arc(c, d);
126
128 auto result = sorter.perform<Aleph::DynList>(g);
129
130 ASSERT_EQ(result.size(), 4);
132}
133
135{
136 /*
137 * A
138 * / \
139 * B C
140 * \ /
141 * D
142 */
143 TestDigraph g;
144 auto a = g.insert_node("A");
145 auto b = g.insert_node("B");
146 auto c = g.insert_node("C");
147 auto d = g.insert_node("D");
148
149 g.insert_arc(a, b);
150 g.insert_arc(a, c);
151 g.insert_arc(b, d);
152 g.insert_arc(c, d);
153
155 auto result = sorter.perform<Aleph::DynList>(g);
156
157 ASSERT_EQ(result.size(), 4);
159}
160
162{
163 TestDigraph g;
164 auto a = g.insert_node("A");
165 auto b = g.insert_node("B");
166 auto c = g.insert_node("C");
167 auto d = g.insert_node("D");
168
169 g.insert_arc(a, b); // Component 1
170 g.insert_arc(c, d); // Component 2
171
173 auto result = sorter.perform<Aleph::DynList>(g);
174
175 ASSERT_EQ(result.size(), 4);
177}
178
180{
181 // A -> B -> D
182 // | | |
183 // v v v
184 // C -> E -> F
185 TestDigraph g;
186 auto a = g.insert_node("A");
187 auto b = g.insert_node("B");
188 auto c = g.insert_node("C");
189 auto d = g.insert_node("D");
190 auto e = g.insert_node("E");
191 auto f = g.insert_node("F");
192
193 g.insert_arc(a, b);
194 g.insert_arc(a, c);
195 g.insert_arc(b, d);
196 g.insert_arc(b, e);
197 g.insert_arc(c, e);
198 g.insert_arc(d, f);
199 g.insert_arc(e, f);
200
202 auto result = sorter.perform<Aleph::DynList>(g);
203
204 ASSERT_EQ(result.size(), 6);
206}
207
209{
210 TestDigraph g;
211 auto a = g.insert_node("A");
212 auto b = g.insert_node("B");
213 g.insert_arc(a, b);
214
217 sorter(g, result);
218
219 ASSERT_EQ(result.size(), 2);
221}
222
223// ==================== Q_Topological_Sort (BFS/Kahn) Tests ====================
224
226{
227 TestDigraph g;
229
230 auto result = sorter.perform<Aleph::DynList>(g);
231 EXPECT_TRUE(result.is_empty());
232}
233
235{
236 TestDigraph g;
237 auto n = g.insert_node("A");
238
240 auto result = sorter.perform<Aleph::DynList>(g);
241
242 ASSERT_EQ(result.size(), 1);
243 EXPECT_EQ(result.get_first(), n);
244}
245
247{
248 TestDigraph g;
249 auto a = g.insert_node("A");
250 auto b = g.insert_node("B");
251 g.insert_arc(a, b);
252
254 auto result = sorter.perform<Aleph::DynList>(g);
255
256 ASSERT_EQ(result.size(), 2);
258}
259
261{
262 TestDigraph g;
263 auto a = g.insert_node("A");
264 auto b = g.insert_node("B");
265 auto c = g.insert_node("C");
266 auto d = g.insert_node("D");
267
268 g.insert_arc(a, b);
269 g.insert_arc(b, c);
270 g.insert_arc(c, d);
271
273 auto result = sorter.perform<Aleph::DynList>(g);
274
275 ASSERT_EQ(result.size(), 4);
277}
278
280{
281 TestDigraph g;
282 auto a = g.insert_node("A");
283 auto b = g.insert_node("B");
284 auto c = g.insert_node("C");
285 auto d = g.insert_node("D");
286
287 g.insert_arc(a, b);
288 g.insert_arc(a, c);
289 g.insert_arc(b, d);
290 g.insert_arc(c, d);
291
293 auto result = sorter.perform<Aleph::DynList>(g);
294
295 ASSERT_EQ(result.size(), 4);
297}
298
299// ==================== Ranks Tests ====================
300
302{
303 TestDigraph g;
305
306 auto result = sorter.ranks(g);
307 EXPECT_TRUE(result.is_empty());
308}
309
311{
312 TestDigraph g;
313 auto n = g.insert_node("A");
314
316 auto result = sorter.ranks(g);
317
318 ASSERT_EQ(result.size(), 1);
319 ASSERT_EQ(result.get_first().size(), 1);
320 EXPECT_EQ(result.get_first().get_first(), n);
321}
322
324{
325 TestDigraph g;
326 g.insert_node("A");
327 g.insert_node("B");
328 g.insert_node("C");
329
330 auto nodes = g.nodes();
331 g.insert_arc(nodes[0], nodes[1]);
332 g.insert_arc(nodes[1], nodes[2]);
333
335 auto result = sorter.ranks(g);
336
337 // Each node should be in its own rank
338 ASSERT_EQ(result.size(), 3);
339 for (auto it = result.get_it(); it.has_curr(); it.next_ne())
340 EXPECT_EQ(it.get_curr().size(), 1);
341}
342
344{
345 /*
346 * A
347 * /|\
348 * B C D
349 */
350 TestDigraph g;
351 auto a = g.insert_node("A");
352 auto b = g.insert_node("B");
353 auto c = g.insert_node("C");
354 auto d = g.insert_node("D");
355
356 g.insert_arc(a, b);
357 g.insert_arc(a, c);
358 g.insert_arc(a, d);
359
361 auto result = sorter.ranks(g);
362
363 ASSERT_EQ(result.size(), 2);
364
365 // First rank should have A
366 EXPECT_EQ(result.get_first().size(), 1);
367 EXPECT_EQ(result.get_first().get_first(), a);
368
369 // Second rank should have B, C, D (in some order)
370 EXPECT_EQ(result.get_last().size(), 3);
371}
372
374{
375 /*
376 * A <- rank 0
377 * / \
378 * B C <- rank 1
379 * \ /
380 * D <- rank 2
381 */
382 TestDigraph g;
383 auto a = g.insert_node("A");
384 auto b = g.insert_node("B");
385 auto c = g.insert_node("C");
386 auto d = g.insert_node("D");
387
388 g.insert_arc(a, b);
389 g.insert_arc(a, c);
390 g.insert_arc(b, d);
391 g.insert_arc(c, d);
392
394 auto result = sorter.ranks(g);
395
396 ASSERT_EQ(result.size(), 3);
397
398 auto it = result.get_it();
399
400 // Rank 0: A
401 EXPECT_EQ(it.get_curr().size(), 1);
402 EXPECT_EQ(it.get_curr().get_first(), a);
403 it.next_ne();
404
405 // Rank 1: B, C
406 EXPECT_EQ(it.get_curr().size(), 2);
407 it.next_ne();
408
409 // Rank 2: D
410 EXPECT_EQ(it.get_curr().size(), 1);
411 EXPECT_EQ(it.get_curr().get_first(), d);
412}
413
415{
416 // Two separate chains: A->B and C->D
417 TestDigraph g;
418 auto a = g.insert_node("A");
419 auto b = g.insert_node("B");
420 auto c = g.insert_node("C");
421 auto d = g.insert_node("D");
422
423 g.insert_arc(a, b);
424 g.insert_arc(c, d);
425
427 auto result = sorter.ranks(g);
428
429 ASSERT_EQ(result.size(), 2);
430
431 // Rank 0: A, C (both sources)
432 EXPECT_EQ(result.get_first().size(), 2);
433
434 // Rank 1: B, D
435 EXPECT_EQ(result.get_last().size(), 2);
436}
437
438// ==================== Operator Overloads Tests ====================
439
441{
442 TestDigraph g;
443 auto a = g.insert_node("A");
444 auto b = g.insert_node("B");
445 g.insert_arc(a, b);
446
449 sorter(g, result);
450
451 ASSERT_EQ(result.size(), 2);
452}
453
455{
456 TestDigraph g;
457 auto a = g.insert_node("A");
458 auto b = g.insert_node("B");
459 g.insert_arc(a, b);
460
463 sorter(g, result);
464
465 ASSERT_EQ(result.size(), 2);
466}
467
469{
470 TestDigraph g;
471 auto a = g.insert_node("A");
472 auto b = g.insert_node("B");
473 g.insert_arc(a, b);
474
477 sorter(g, result);
478
479 ASSERT_EQ(result.size(), 2);
480}
481
482// ==================== Stress Tests ====================
483
485{
486 TestDigraph g;
487 constexpr size_t N = 1000;
488
489 // Create a long chain
491 for (size_t i = 0; i < N; ++i)
492 nodes.append(g.insert_node(std::to_string(i)));
493
494 for (size_t i = 0; i < N - 1; ++i)
495 g.insert_arc(nodes(i), nodes(i + 1));
496
497 // DFS version
498 {
500 auto result = sorter.perform<Aleph::DynList>(g);
501 ASSERT_EQ(result.size(), N);
503 }
504
505 // BFS version
506 {
508 auto result = sorter.perform<Aleph::DynList>(g);
509 ASSERT_EQ(result.size(), N);
511 }
512}
513
515{
516 TestDigraph g;
517 constexpr size_t WIDTH = 100;
518
519 // Single source with many sinks
520 auto source = g.insert_node("source");
521 for (size_t i = 0; i < WIDTH; ++i)
522 {
523 auto sink = g.insert_node(std::to_string(i));
524 g.insert_arc(source, sink);
525 }
526
528 auto ranks = sorter.ranks(g);
529
530 ASSERT_EQ(ranks.size(), 2);
531 EXPECT_EQ(ranks.get_first().size(), 1);
532 EXPECT_EQ(ranks.get_last().size(), WIDTH);
533}
534
535// ==================== Comparison Tests ====================
536
538{
539 // Complex DAG
540 TestDigraph g;
541 auto a = g.insert_node("A");
542 auto b = g.insert_node("B");
543 auto c = g.insert_node("C");
544 auto d = g.insert_node("D");
545 auto e = g.insert_node("E");
546 auto f = g.insert_node("F");
547
548 g.insert_arc(a, b);
549 g.insert_arc(a, c);
550 g.insert_arc(b, d);
551 g.insert_arc(c, d);
552 g.insert_arc(d, e);
553 g.insert_arc(d, f);
554
556 auto dfs_result = dfs_sorter.perform<Aleph::DynList>(g);
557
559 auto bfs_result = bfs_sorter.perform<Aleph::DynList>(g);
560
561 // Both should have all nodes
564
565 // Both should produce valid orderings (may differ)
568}
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:333
T & get_first() noexcept
return a modifiable reference to the first element.
Definition tpl_array.H:340
T & get_last() noexcept
return a modifiable reference to the last element.
Definition tpl_array.H:348
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
T & append()
Allocate a new entry to the end of array.
Dynamic doubly linked list with O(1) size and bidirectional access.
const size_t & size() const noexcept
Return the number of elements (constant time)
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
Computes topological ordering using breadth-first search (Kahn's algorithm).
Computes topological ordering using depth-first search.
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:190
#define TEST(name)
#define N
Definition fib.C:294
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Array< size_t > ranks(const Array< T > &array)
Computes the rank of each element in an Array.
Definition ahSort.H:524
DynList< T > maps(const C &c, Op op)
Classic map operation.
#define WIDTH(p)
Definition ntreepic.C:361
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1164
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Topological sorting algorithms for directed acyclic graphs (DAGs).
bool is_valid_topological_order(const TestDigraph &g, const List &order)
Generic graph and digraph implementations.