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