GCC Code Coverage Report


Directory: ./
File: include/na64util/tsort.hh
Date: 2025-09-01 06:19:01
Exec Total Coverage
Lines: 31 32 96.9%
Functions: 4 10 40.0%
Branches: 27 28 96.4%

Line Branch Exec Source
1 #pragma once
2
3 #include <unordered_map>
4 #include <unordered_set>
5 #include <vector>
6 #include <algorithm>
7 #include <set>
8 #include <cstdint>
9
10 #include <iostream> // XXX
11
12 namespace na64dp {
13 namespace util {
14
15 /**\brief Implements Kahn's algorithm for topological sort
16 *
17 * A rather straightforward implementation of Kahn's algorithm for topological
18 * sort.
19 *
20 * \note Instances are disposable. Graph is invalidated after `sorted()` returns.
21 *
22 * \todo poor loops detection
23 * \todo isolated nodes are lost
24 * \todo profile, optimize?
25 */
26 template< typename T
27 , typename HashT=std::hash<T>
28 , typename EqualT=std::equal_to<T>
29 , typename CompareT=std::less<T>
30 >
31 class DAG : private std::unordered_multimap<T, T, HashT, EqualT> {
32 public:
33 typedef std::unordered_multimap<T, T, HashT, EqualT> Parent;
34 typedef typename Parent::value_type Edge;
35
36 using Parent::empty;
37
38 ///\brief Returns sorted sequence and invalidates the object
39 ///
40 /// \note that if this graph has edges after this methood done, it means
41 /// the graph contains cycles (yet, returned result is not empty).
42 8 std::vector<T> sorted() {
43 8 std::vector<T> result;
44 8 std::unordered_set<T, HashT> freeNodes;
45 8 std::unordered_map<T, uint32_t, HashT> indegree;
46 {
47 // get free nodes as subtraction of the sets; it's crucial to use
48 // sorted container here (`set_difference()` assumes sorted)
49 8 std::set<T, CompareT> from, to;
50
2/2
✓ Branch 2 taken 4 times.
✓ Branch 7 taken 4 times.
8 std::transform( Parent::begin(), Parent::end()
51 , std::inserter(from, from.begin())
52 42 , []( const Edge & edge ) {return edge.first;} );
53
2/2
✓ Branch 2 taken 4 times.
✓ Branch 7 taken 4 times.
8 std::transform( Parent::begin(), Parent::end()
54 , std::inserter(to, to.begin())
55 42 , []( const Edge & edge ) {return edge.second;} );
56
2/2
✓ Branch 2 taken 4 times.
✓ Branch 9 taken 4 times.
8 std::set_difference( from.begin(), from.end()
57 , to.begin(), to.end()
58 , std::inserter(freeNodes, freeNodes.begin())
59 );
60
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
8 if(freeNodes.empty()) return result; // trivial, or perfect cyclic graph.
61 8 std::unordered_set<T, HashT> ins;
62
2/2
✓ Branch 2 taken 4 times.
✓ Branch 9 taken 4 times.
8 std::set_difference( to.begin(), to.end()
63 , from.begin(), from.end()
64 , std::inserter(ins, ins.end()));
65
2/2
✓ Branch 2 taken 4 times.
✓ Branch 7 taken 4 times.
8 std::transform( ins.begin(), ins.end()
66 , std::inserter(indegree, indegree.begin())
67 21 , []( const T & k ) { return std::pair<T, uint32_t>{k, 0}; } );
68
3/3
✓ Branch 4 taken 42 times.
✓ Branch 8 taken 42 times.
✓ Branch 9 taken 4 times.
92 for( const auto & p : *this ) { ++indegree[p.second]; }
69 8 }
70
71
2/2
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 4 times.
78 while(!freeNodes.empty()) {
72
1/1
✓ Branch 3 taken 7 times.
70 T n = *freeNodes.begin();
73
1/1
✓ Branch 2 taken 35 times.
70 freeNodes.erase(freeNodes.begin());
74
1/1
✓ Branch 1 taken 35 times.
70 result.push_back(n);
75
1/1
✓ Branch 1 taken 35 times.
70 auto er = Parent::equal_range(n);
76
2/2
✓ Branch 2 taken 42 times.
✓ Branch 3 taken 35 times.
154 for( auto it = er.first; it != er.second; ++it ) {
77
3/3
✓ Branch 2 taken 42 times.
✓ Branch 4 taken 31 times.
✓ Branch 5 taken 11 times.
84 if( ! (--indegree[it->second]) ) {
78
1/1
✓ Branch 2 taken 31 times.
62 freeNodes.insert(it->second);
79 }
80 }
81
1/1
✓ Branch 3 taken 35 times.
70 Parent::erase(er.first, er.second);
82 }
83 8 return result;
84 8 }
85
86 /// Adds edge "from-to"
87 84 void add( const T & from, const T & to ) {
88 84 Parent::emplace(from, to);
89 84 }
90
91 /// Returns (remaining) edges, useful for debug
92 const Parent & edges() { return *this; }
93 };
94
95 } // namespace ::na64dp::util
96 } // namespace na64dp
97
98