/src/solidity/libsolutil/TarjanSCC.h
Line | Count | Source |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | |
19 | | #pragma once |
20 | | |
21 | | #include <liblangutil/Exceptions.h> |
22 | | |
23 | | #include <algorithm> |
24 | | #include <concepts> |
25 | | #include <cstddef> |
26 | | #include <limits> |
27 | | #include <vector> |
28 | | |
29 | | namespace solidity::util |
30 | | { |
31 | | |
32 | | namespace detail |
33 | | { |
34 | | |
35 | | /// Callers must supply a dense `[0, N)` remapping; this struct conflates the node's |
36 | | /// identity with its position in `adjacency` for performance. |
37 | | template<std::unsigned_integral NodeIndex> |
38 | | struct TarjanSCC |
39 | | { |
40 | | static constexpr std::size_t undefined = std::numeric_limits<std::size_t>::max(); |
41 | | |
42 | | struct Frame |
43 | | { |
44 | | NodeIndex node; |
45 | | std::size_t childIdx; |
46 | | }; |
47 | | |
48 | | std::vector<std::vector<NodeIndex>> const& adjacency; |
49 | | /// numbers the nodes consecutively in the order in which they are discovered |
50 | | std::vector<std::size_t> discoveryIndex; |
51 | | /// lowlink[v] corresponds to the smallest index of a node reachable through v's DFS subtree |
52 | | std::vector<std::size_t> lowlink; |
53 | | std::vector<bool> onStack; |
54 | | std::vector<NodeIndex> nodeStack; |
55 | | std::vector<Frame> workStack; |
56 | | std::size_t nextIndex = 0; |
57 | | std::vector<std::vector<NodeIndex>> sccs; |
58 | | |
59 | | explicit TarjanSCC(std::vector<std::vector<NodeIndex>> const& _adjacency): |
60 | 3.68M | adjacency(_adjacency), |
61 | 3.68M | discoveryIndex(_adjacency.size(), undefined), |
62 | 3.68M | lowlink(_adjacency.size(), 0), |
63 | 3.68M | onStack(_adjacency.size(), false) |
64 | 3.68M | {}Unexecuted instantiation: solidity::util::detail::TarjanSCC<unsigned int>::TarjanSCC(std::__1::vector<std::__1::vector<unsigned int, std::__1::allocator<unsigned int> >, std::__1::allocator<std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > > > const&) solidity::util::detail::TarjanSCC<unsigned long>::TarjanSCC(std::__1::vector<std::__1::vector<unsigned long, std::__1::allocator<unsigned long> >, std::__1::allocator<std::__1::vector<unsigned long, std::__1::allocator<unsigned long> > > > const&) Line | Count | Source | 60 | 3.68M | adjacency(_adjacency), | 61 | 3.68M | discoveryIndex(_adjacency.size(), undefined), | 62 | 3.68M | lowlink(_adjacency.size(), 0), | 63 | 3.68M | onStack(_adjacency.size(), false) | 64 | 3.68M | {} |
|
65 | | |
66 | | void enter(NodeIndex const _v) |
67 | 28.9M | { |
68 | 28.9M | solAssert(_v < adjacency.size()); |
69 | 28.9M | discoveryIndex[_v] = nextIndex; |
70 | 28.9M | lowlink[_v] = nextIndex; |
71 | 28.9M | ++nextIndex; |
72 | 28.9M | nodeStack.push_back(_v); |
73 | 28.9M | onStack[_v] = true; |
74 | 28.9M | workStack.push_back({_v, 0}); |
75 | 28.9M | } Unexecuted instantiation: solidity::util::detail::TarjanSCC<unsigned int>::enter(unsigned int) solidity::util::detail::TarjanSCC<unsigned long>::enter(unsigned long) Line | Count | Source | 67 | 28.9M | { | 68 | | solAssert(_v < adjacency.size()); | 69 | 28.9M | discoveryIndex[_v] = nextIndex; | 70 | 28.9M | lowlink[_v] = nextIndex; | 71 | 28.9M | ++nextIndex; | 72 | 28.9M | nodeStack.push_back(_v); | 73 | 28.9M | onStack[_v] = true; | 74 | 28.9M | workStack.push_back({_v, 0}); | 75 | 28.9M | } |
|
76 | | |
77 | | void emitSCC(NodeIndex const _root) |
78 | 28.7M | { |
79 | 28.7M | solAssert(!nodeStack.empty()); |
80 | 28.7M | std::vector<NodeIndex> scc; |
81 | 28.7M | NodeIndex w; |
82 | 28.7M | do |
83 | 28.9M | { |
84 | 28.9M | w = nodeStack.back(); |
85 | 28.9M | nodeStack.pop_back(); |
86 | 28.9M | onStack[w] = false; |
87 | 28.9M | scc.push_back(w); |
88 | 28.9M | } |
89 | 28.9M | while (w != _root); |
90 | 28.7M | sccs.push_back(std::move(scc)); |
91 | 28.7M | } Unexecuted instantiation: solidity::util::detail::TarjanSCC<unsigned int>::emitSCC(unsigned int) solidity::util::detail::TarjanSCC<unsigned long>::emitSCC(unsigned long) Line | Count | Source | 78 | 28.7M | { | 79 | 28.7M | solAssert(!nodeStack.empty()); | 80 | 28.7M | std::vector<NodeIndex> scc; | 81 | 28.7M | NodeIndex w; | 82 | 28.7M | do | 83 | 28.9M | { | 84 | 28.9M | w = nodeStack.back(); | 85 | 28.9M | nodeStack.pop_back(); | 86 | 28.9M | onStack[w] = false; | 87 | 28.9M | scc.push_back(w); | 88 | 28.9M | } | 89 | 28.9M | while (w != _root); | 90 | 28.7M | sccs.push_back(std::move(scc)); | 91 | 28.7M | } |
|
92 | | |
93 | | std::vector<std::vector<NodeIndex>> run() && |
94 | 3.68M | { |
95 | 32.6M | for (NodeIndex root = 0; root < discoveryIndex.size(); ++root) |
96 | 28.9M | { |
97 | 28.9M | if (discoveryIndex[root] != undefined) |
98 | 19.6M | continue; |
99 | | |
100 | | // start new DFS tree |
101 | 9.31M | enter(root); |
102 | 85.6M | while (!workStack.empty()) |
103 | 76.3M | { |
104 | 76.3M | NodeIndex const v = workStack.back().node; |
105 | 76.3M | std::size_t const childIdx = workStack.back().childIdx; |
106 | 76.3M | if (childIdx < adjacency[v].size()) |
107 | 47.4M | { |
108 | | // process next outgoing edge |
109 | 47.4M | NodeIndex const w = adjacency[v][childIdx]; |
110 | | // advance v's currently handled edge before pushing w, so v resumes at the next child when w finishes |
111 | 47.4M | workStack.back().childIdx = childIdx + 1; |
112 | 47.4M | if (discoveryIndex[w] == undefined) |
113 | | // Successor w has not yet been visited; recurse on it |
114 | 19.6M | enter(w); |
115 | 27.7M | else if (onStack[w]) |
116 | | // Successor w is in stack S and hence in the current SCC |
117 | 470k | lowlink[v] = std::min(lowlink[v], discoveryIndex[w]); |
118 | 47.4M | } |
119 | 28.9M | else |
120 | 28.9M | { |
121 | | // if v is a root node |
122 | 28.9M | if (lowlink[v] == discoveryIndex[v]) |
123 | | // generate an SCC and pop the node stack |
124 | 28.7M | emitSCC(v); |
125 | | |
126 | | // pop v and propagate its lowlink to the parent (post-recursion update) |
127 | 28.9M | workStack.pop_back(); |
128 | 28.9M | if (!workStack.empty()) |
129 | 19.6M | { |
130 | 19.6M | NodeIndex const parent = workStack.back().node; |
131 | 19.6M | lowlink[parent] = std::min(lowlink[parent], lowlink[v]); |
132 | 19.6M | } |
133 | 28.9M | } |
134 | 76.3M | } |
135 | 9.31M | } |
136 | 3.68M | return std::move(sccs); |
137 | 3.68M | } Unexecuted instantiation: solidity::util::detail::TarjanSCC<unsigned int>::run() && solidity::util::detail::TarjanSCC<unsigned long>::run() && Line | Count | Source | 94 | 3.68M | { | 95 | 32.6M | for (NodeIndex root = 0; root < discoveryIndex.size(); ++root) | 96 | 28.9M | { | 97 | 28.9M | if (discoveryIndex[root] != undefined) | 98 | 19.6M | continue; | 99 | | | 100 | | // start new DFS tree | 101 | 9.31M | enter(root); | 102 | 85.6M | while (!workStack.empty()) | 103 | 76.3M | { | 104 | 76.3M | NodeIndex const v = workStack.back().node; | 105 | 76.3M | std::size_t const childIdx = workStack.back().childIdx; | 106 | 76.3M | if (childIdx < adjacency[v].size()) | 107 | 47.4M | { | 108 | | // process next outgoing edge | 109 | 47.4M | NodeIndex const w = adjacency[v][childIdx]; | 110 | | // advance v's currently handled edge before pushing w, so v resumes at the next child when w finishes | 111 | 47.4M | workStack.back().childIdx = childIdx + 1; | 112 | 47.4M | if (discoveryIndex[w] == undefined) | 113 | | // Successor w has not yet been visited; recurse on it | 114 | 19.6M | enter(w); | 115 | 27.7M | else if (onStack[w]) | 116 | | // Successor w is in stack S and hence in the current SCC | 117 | 470k | lowlink[v] = std::min(lowlink[v], discoveryIndex[w]); | 118 | 47.4M | } | 119 | 28.9M | else | 120 | 28.9M | { | 121 | | // if v is a root node | 122 | 28.9M | if (lowlink[v] == discoveryIndex[v]) | 123 | | // generate an SCC and pop the node stack | 124 | 28.7M | emitSCC(v); | 125 | | | 126 | | // pop v and propagate its lowlink to the parent (post-recursion update) | 127 | 28.9M | workStack.pop_back(); | 128 | 28.9M | if (!workStack.empty()) | 129 | 19.6M | { | 130 | 19.6M | NodeIndex const parent = workStack.back().node; | 131 | 19.6M | lowlink[parent] = std::min(lowlink[parent], lowlink[v]); | 132 | 19.6M | } | 133 | 28.9M | } | 134 | 76.3M | } | 135 | 9.31M | } | 136 | 3.68M | return std::move(sccs); | 137 | 3.68M | } |
|
138 | | }; |
139 | | |
140 | | } |
141 | | |
142 | | /// Tarjan's strongly-connected-components algorithm. |
143 | | /// Takes an adjacency list where `_adjacency[v]` is the list of successors of node `v`. Node IDs must lie in `[0, _adjacency.size())`. |
144 | | /// Implementation based on the Wikipedia pseudocode. |
145 | | /// |
146 | | /// Wikipedia contributors, "Tarjan's strongly connected components algorithm," Wikipedia, The Free Encyclopedia, |
147 | | /// https://en.wikipedia.org/w/index.php?title=Tarjan%27s_strongly_connected_components_algorithm&oldid=1341352351 |
148 | | /// Tarjan, R.E., "Depth-First Search and Linear Graph Algorithms", https://doi.org/10.1137/0201010 |
149 | | template<std::unsigned_integral NodeID> |
150 | | std::vector<std::vector<NodeID>> computeStronglyConnectedComponents(std::vector<std::vector<NodeID>> const& _adjacency) |
151 | 3.68M | { |
152 | 3.68M | return detail::TarjanSCC<NodeID>(_adjacency).run(); |
153 | 3.68M | } Unexecuted instantiation: _ZN8solidity4util34computeStronglyConnectedComponentsITkNSt3__117unsigned_integralEjEENS2_6vectorINS3_IT_NS2_9allocatorIS4_EEEENS5_IS7_EEEERKS9_ _ZN8solidity4util34computeStronglyConnectedComponentsITkNSt3__117unsigned_integralEmEENS2_6vectorINS3_IT_NS2_9allocatorIS4_EEEENS5_IS7_EEEERKS9_ Line | Count | Source | 151 | 3.68M | { | 152 | 3.68M | return detail::TarjanSCC<NodeID>(_adjacency).run(); | 153 | 3.68M | } |
|
154 | | |
155 | | } |