xref: /aosp_15_r20/external/stg/scc.h (revision 9e3b08ae94a55201065475453d799e8b1378bea6)
1*9e3b08aeSAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2*9e3b08aeSAndroid Build Coastguard Worker // -*- mode: C++ -*-
3*9e3b08aeSAndroid Build Coastguard Worker //
4*9e3b08aeSAndroid Build Coastguard Worker // Copyright 2020-2022 Google LLC
5*9e3b08aeSAndroid Build Coastguard Worker //
6*9e3b08aeSAndroid Build Coastguard Worker // Licensed under the Apache License v2.0 with LLVM Exceptions (the
7*9e3b08aeSAndroid Build Coastguard Worker // "License"); you may not use this file except in compliance with the
8*9e3b08aeSAndroid Build Coastguard Worker // License.  You may obtain a copy of the License at
9*9e3b08aeSAndroid Build Coastguard Worker //
10*9e3b08aeSAndroid Build Coastguard Worker //     https://llvm.org/LICENSE.txt
11*9e3b08aeSAndroid Build Coastguard Worker //
12*9e3b08aeSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
13*9e3b08aeSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
14*9e3b08aeSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15*9e3b08aeSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
16*9e3b08aeSAndroid Build Coastguard Worker // limitations under the License.
17*9e3b08aeSAndroid Build Coastguard Worker //
18*9e3b08aeSAndroid Build Coastguard Worker // Author: Giuliano Procida
19*9e3b08aeSAndroid Build Coastguard Worker 
20*9e3b08aeSAndroid Build Coastguard Worker #ifndef STG_SCC_H_
21*9e3b08aeSAndroid Build Coastguard Worker #define STG_SCC_H_
22*9e3b08aeSAndroid Build Coastguard Worker 
23*9e3b08aeSAndroid Build Coastguard Worker #include <cstddef>
24*9e3b08aeSAndroid Build Coastguard Worker #include <exception>
25*9e3b08aeSAndroid Build Coastguard Worker #include <functional>
26*9e3b08aeSAndroid Build Coastguard Worker #include <iterator>
27*9e3b08aeSAndroid Build Coastguard Worker #include <optional>
28*9e3b08aeSAndroid Build Coastguard Worker #include <unordered_map>
29*9e3b08aeSAndroid Build Coastguard Worker #include <utility>
30*9e3b08aeSAndroid Build Coastguard Worker #include <vector>
31*9e3b08aeSAndroid Build Coastguard Worker 
32*9e3b08aeSAndroid Build Coastguard Worker #include "error.h"
33*9e3b08aeSAndroid Build Coastguard Worker 
34*9e3b08aeSAndroid Build Coastguard Worker namespace stg {
35*9e3b08aeSAndroid Build Coastguard Worker 
36*9e3b08aeSAndroid Build Coastguard Worker /*
37*9e3b08aeSAndroid Build Coastguard Worker  * This is a streamlined Strongly-Connected Component finder for use with
38*9e3b08aeSAndroid Build Coastguard Worker  * procedurally generated or explored graphs, where the nodes and edges are not
39*9e3b08aeSAndroid Build Coastguard Worker  * known a priori.
40*9e3b08aeSAndroid Build Coastguard Worker  *
41*9e3b08aeSAndroid Build Coastguard Worker  * REQUIREMENTS
42*9e3b08aeSAndroid Build Coastguard Worker  *
43*9e3b08aeSAndroid Build Coastguard Worker  * The Node type must be copyable and have a suitable hash function.
44*9e3b08aeSAndroid Build Coastguard Worker  *
45*9e3b08aeSAndroid Build Coastguard Worker  * The user code must take the form of a Depth First Search which can be
46*9e3b08aeSAndroid Build Coastguard Worker  * repeatedly invoked on unvisited nodes until the whole graph has been
47*9e3b08aeSAndroid Build Coastguard Worker  * traversed.
48*9e3b08aeSAndroid Build Coastguard Worker  *
49*9e3b08aeSAndroid Build Coastguard Worker  * The user code must always follow edges to child nodes, even if it knows the
50*9e3b08aeSAndroid Build Coastguard Worker  * node has already been visited. The SCC finder needs to know about all edges.
51*9e3b08aeSAndroid Build Coastguard Worker  *
52*9e3b08aeSAndroid Build Coastguard Worker  * The user code must ensure that nodes are not re-examined once they have been
53*9e3b08aeSAndroid Build Coastguard Worker  * assigned to an SCC. The finder does not maintain any state for such nodes.
54*9e3b08aeSAndroid Build Coastguard Worker  *
55*9e3b08aeSAndroid Build Coastguard Worker  * GUARANTEES
56*9e3b08aeSAndroid Build Coastguard Worker  *
57*9e3b08aeSAndroid Build Coastguard Worker  * The SCC finder will ensure each node is examined exactly once.
58*9e3b08aeSAndroid Build Coastguard Worker  *
59*9e3b08aeSAndroid Build Coastguard Worker  * The SCCs will be presented in a topological order, leaves first.
60*9e3b08aeSAndroid Build Coastguard Worker  *
61*9e3b08aeSAndroid Build Coastguard Worker  * Note that within each SCC, nodes will be presented in DFS traversal order,
62*9e3b08aeSAndroid Build Coastguard Worker  * roots first. However, this is just an implementation detail, not a guarantee.
63*9e3b08aeSAndroid Build Coastguard Worker  *
64*9e3b08aeSAndroid Build Coastguard Worker  * USAGE
65*9e3b08aeSAndroid Build Coastguard Worker  *
66*9e3b08aeSAndroid Build Coastguard Worker  * Create an SCC finder with a lifetime bracketing a top-level DFS invocation.
67*9e3b08aeSAndroid Build Coastguard Worker  *
68*9e3b08aeSAndroid Build Coastguard Worker  * Before examining a node, check it's not been assigned to an SCC already and
69*9e3b08aeSAndroid Build Coastguard Worker  * then call Open. If the node is already "open" (i.e., is already waiting to be
70*9e3b08aeSAndroid Build Coastguard Worker  * assigned to an SCC), this will return an empty optional value and the node
71*9e3b08aeSAndroid Build Coastguard Worker  * should not be examined. If Open succeeds, a numerical node handle will be
72*9e3b08aeSAndroid Build Coastguard Worker  * returned and the node will be recorded as waiting to be assigned to an SCC.
73*9e3b08aeSAndroid Build Coastguard Worker  *
74*9e3b08aeSAndroid Build Coastguard Worker  * Now examine the node, making recursive calls to follow edges to other nodes.
75*9e3b08aeSAndroid Build Coastguard Worker  * Information about the node can be stored provisionally, but must NOT be used
76*9e3b08aeSAndroid Build Coastguard Worker  * to make decisions about whether to revisit it - that is Open's job.
77*9e3b08aeSAndroid Build Coastguard Worker  *
78*9e3b08aeSAndroid Build Coastguard Worker  * Once the examination is done, call Close, passing in the handle. If the node
79*9e3b08aeSAndroid Build Coastguard Worker  * has been identified as the "root" of an SCC, the whole SCC will be returned
80*9e3b08aeSAndroid Build Coastguard Worker  * as a vector of nodes. If any processing needs to be done (such as recording
81*9e3b08aeSAndroid Build Coastguard Worker  * the nodes as visited), this should be done now. Otherwise, an empty vector
82*9e3b08aeSAndroid Build Coastguard Worker  * will be returned.
83*9e3b08aeSAndroid Build Coastguard Worker  *
84*9e3b08aeSAndroid Build Coastguard Worker  * On destruction, after a top-level DFS invocation has completed, the SCC
85*9e3b08aeSAndroid Build Coastguard Worker  * finder will check that it is carrying no state.
86*9e3b08aeSAndroid Build Coastguard Worker  */
87*9e3b08aeSAndroid Build Coastguard Worker template <typename Node, typename Hash = std::hash<Node>>
88*9e3b08aeSAndroid Build Coastguard Worker class SCC {
89*9e3b08aeSAndroid Build Coastguard Worker  public:
~SCC()90*9e3b08aeSAndroid Build Coastguard Worker   ~SCC() noexcept(false) {
91*9e3b08aeSAndroid Build Coastguard Worker     if (std::uncaught_exceptions() == 0) {
92*9e3b08aeSAndroid Build Coastguard Worker       Check(open_.empty() && is_open_.empty() && root_index_.empty())
93*9e3b08aeSAndroid Build Coastguard Worker           << "internal error: SCC state broken";
94*9e3b08aeSAndroid Build Coastguard Worker     }
95*9e3b08aeSAndroid Build Coastguard Worker   }
96*9e3b08aeSAndroid Build Coastguard Worker 
Open(const Node & node)97*9e3b08aeSAndroid Build Coastguard Worker   std::optional<size_t> Open(const Node& node) {
98*9e3b08aeSAndroid Build Coastguard Worker     // Insertion will fail if the node is already open.
99*9e3b08aeSAndroid Build Coastguard Worker     auto [it, inserted] = is_open_.insert({node, is_open_.size()});
100*9e3b08aeSAndroid Build Coastguard Worker     auto ix = it->second;
101*9e3b08aeSAndroid Build Coastguard Worker     if (!inserted) {
102*9e3b08aeSAndroid Build Coastguard Worker       // Pop indices to nodes which cannot be the root of their SCC.
103*9e3b08aeSAndroid Build Coastguard Worker       while (root_index_.back() > ix) {
104*9e3b08aeSAndroid Build Coastguard Worker         root_index_.pop_back();
105*9e3b08aeSAndroid Build Coastguard Worker       }
106*9e3b08aeSAndroid Build Coastguard Worker       return {};
107*9e3b08aeSAndroid Build Coastguard Worker     }
108*9e3b08aeSAndroid Build Coastguard Worker     // Unvisited, record open node and record root index.
109*9e3b08aeSAndroid Build Coastguard Worker     open_.push_back(node);
110*9e3b08aeSAndroid Build Coastguard Worker     root_index_.push_back(ix);
111*9e3b08aeSAndroid Build Coastguard Worker     return {ix};
112*9e3b08aeSAndroid Build Coastguard Worker   }
113*9e3b08aeSAndroid Build Coastguard Worker 
Close(size_t ix)114*9e3b08aeSAndroid Build Coastguard Worker   std::vector<Node> Close(size_t ix) {
115*9e3b08aeSAndroid Build Coastguard Worker     std::vector<Node> scc;
116*9e3b08aeSAndroid Build Coastguard Worker     Check(ix < open_.size()) << "internal error: illegal SCC node index";
117*9e3b08aeSAndroid Build Coastguard Worker     if (ix == root_index_.back()) {
118*9e3b08aeSAndroid Build Coastguard Worker       // Close SCC.
119*9e3b08aeSAndroid Build Coastguard Worker       root_index_.pop_back();
120*9e3b08aeSAndroid Build Coastguard Worker       const auto begin = open_.begin() + ix;
121*9e3b08aeSAndroid Build Coastguard Worker       const auto end = open_.end();
122*9e3b08aeSAndroid Build Coastguard Worker       for (auto it = begin; it != end; ++it) {
123*9e3b08aeSAndroid Build Coastguard Worker         is_open_.erase(*it);
124*9e3b08aeSAndroid Build Coastguard Worker       }
125*9e3b08aeSAndroid Build Coastguard Worker       scc.reserve(end - begin);
126*9e3b08aeSAndroid Build Coastguard Worker       std::move(begin, end, std::back_inserter(scc));
127*9e3b08aeSAndroid Build Coastguard Worker       open_.erase(begin, end);
128*9e3b08aeSAndroid Build Coastguard Worker     }
129*9e3b08aeSAndroid Build Coastguard Worker     return scc;
130*9e3b08aeSAndroid Build Coastguard Worker   }
131*9e3b08aeSAndroid Build Coastguard Worker 
132*9e3b08aeSAndroid Build Coastguard Worker  private:
133*9e3b08aeSAndroid Build Coastguard Worker   std::vector<Node> open_;  // index to node
134*9e3b08aeSAndroid Build Coastguard Worker   std::unordered_map<Node, size_t, Hash> is_open_;  // node to index
135*9e3b08aeSAndroid Build Coastguard Worker   std::vector<size_t> root_index_;
136*9e3b08aeSAndroid Build Coastguard Worker };
137*9e3b08aeSAndroid Build Coastguard Worker 
138*9e3b08aeSAndroid Build Coastguard Worker }  // namespace stg
139*9e3b08aeSAndroid Build Coastguard Worker 
140*9e3b08aeSAndroid Build Coastguard Worker #endif  // STG_SCC_H_
141