/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ #pragma once #include #include namespace dev { /** * Detector for cycles in directed graphs. It returns the first * vertex on the path towards a cycle or a nullptr if there is * no reachable cycle starting from a given vertex. */ template class CycleDetector { public: using Visitor = std::function; /// Initializes the cycle detector /// @param _visit function that is given the current vertex /// and is supposed to call @a run on all /// adjacent vertices. explicit CycleDetector(Visitor _visit): m_visit(std::move(_visit)) { } /// Recursively perform cycle detection starting /// (or continuing) with @param _vertex /// @returns the first vertex on the path towards a cycle from @a _vertex /// or nullptr if no cycle is reachable from @a _vertex. V const* run(V const& _vertex) { if (m_firstCycleVertex) return m_firstCycleVertex; if (m_processed.count(&_vertex)) return nullptr; else if (m_processing.count(&_vertex)) return m_firstCycleVertex = &_vertex; m_processing.insert(&_vertex); m_depth++; m_visit(_vertex, *this, m_depth); m_depth--; if (m_firstCycleVertex && m_depth == 1) m_firstCycleVertex = &_vertex; m_processing.erase(&_vertex); m_processed.insert(&_vertex); return m_firstCycleVertex; } private: Visitor m_visit; std::set m_processing; std::set m_processed; size_t m_depth = 0; V const* m_firstCycleVertex = nullptr; }; /** * Generic breadth first search. * * Example: Gather all (recursive) children in a graph starting at (and including) ``root``: * * Node const* root = ...; * std::set allNodes = BreadthFirstSearch{{root}}.run([](Node const& _node, auto&& _addChild) { * // Potentially process ``_node``. * for (Node const& _child: _node.children()) * // Potentially filter the children to be visited. * _addChild(_child); * }).visited; * * Note that the order of the traversal is *non-deterministic* (the children are stored in a std::set of pointers). */ template struct BreadthFirstSearch { /// Runs the breadth first search. The verticesToTraverse member of the struct needs to be initialized. /// @param _forEachChild is a callable of the form [...](V const& _node, auto&& _addChild) { ... } /// that is called for each visited node and is supposed to call _addChild(childNode) for every child /// node of _node. template BreadthFirstSearch& run(ForEachChild&& _forEachChild) { while (!verticesToTraverse.empty()) { V const* v = *verticesToTraverse.begin(); verticesToTraverse.erase(verticesToTraverse.begin()); visited.insert(v); _forEachChild(*v, [this](V const& _vertex) { if (!visited.count(&_vertex)) verticesToTraverse.insert(&_vertex); }); } return *this; } std::set verticesToTraverse; std::set visited{}; }; }