1 /* 2 Copyright (c) 2008-2020, Benoit AUTHEMAN All rights reserved. 3 4 Redistribution and use in source and binary forms, with or without 5 modification, are permitted provided that the following conditions are met: 6 * Redistributions of source code must retain the above copyright 7 notice, this list of conditions and the following disclaimer. 8 * Redistributions in binary form must reproduce the above copyright 9 notice, this list of conditions and the following disclaimer in the 10 documentation and/or other materials provided with the distribution. 11 * Neither the name of the author or Destrat.io nor the 12 names of its contributors may be used to endorse or promote products 13 derived from this software without specific prior written permission. 14 15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 19 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 //----------------------------------------------------------------------------- 28 // This file is a part of the GTpo software library. 29 // 30 // \file algorithm.h 31 // \author benoit@destrat.io 32 // \date 2018 07 05 33 //----------------------------------------------------------------------------- 34 35 #ifndef gtpo_algorithm_h 36 #define gtpo_algorithm_h 37 38 // STD headers 39 #include <list> 40 #include <unordered_set> 41 #include <memory> // std::shared_ptr std::weak_ptr and std::make_shared 42 #include <iterator> // std::forward_iterator_tag 43 #include <unordered_map> 44 #include <stack> 45 46 // GTpo headers 47 #include "./utils.h" 48 #include "./config.h" 49 #include "./edge.h" 50 #include "./node.h" 51 52 namespace gtpo { // ::gtpo 53 54 template <class config_t> 55 class graph; 56 57 58 /* Adjacent Edges Collection *///---------------------------------------------- 59 /*! \brief Return an unordered set of all adjacents to a given node, taking group into account. 60 * 61 * For ungrouped nodes, adjacent edges of a simple is just the set containing node in_nodes and out_nodes. 62 * 63 * When a node is a group (is_group() return true), adjacent_edges of grouped nodes are 64 * also returned recursively. 65 * 66 */ 67 template <class graph_t> 68 auto collect_adjacent_edges(const typename graph_t::weak_node_t& node) noexcept -> std::unordered_set<typename graph_t::weak_node_t>; 69 //----------------------------------------------------------------------------- 70 71 72 /* Iterative Graph Traversal Algorithms *///----------------------------------- 73 /*! \brief Return a linearized DFS ordered version of a given tree. 74 * 75 */ 76 template <class graph_t> 77 auto linearize_dfs(const graph_t& graph) noexcept -> std::vector<typename graph_t::weak_node_t>; 78 //----------------------------------------------------------------------------- 79 80 81 /* Recursive Graph Traversal Algorithms *///----------------------------------- 82 83 /*! \brief Return true if a graph is an acyclic graph (DAG), ie it does not contains circuits. 84 * 85 * \note is_dag_rec() will return true for an empty graph. 86 * \note is_dag_rec() will return true for a graph with only one vertice. 87 * \note complexity is at most O(n) with n beeing number of vertices in input \c graph. 88 * \note Mandatory static behaviours: none 89 * \note Recursive algorithm with no overflow protection. 90 */ 91 template <class graph_t> 92 auto is_dag_rec(const graph_t& graph) noexcept -> bool; 93 94 /*! \brief Return true if a graph is actually a tree (ie it does not not contains circuits and nodes maximum indegree <= 1). 95 * 96 * Test if a given graph \c graph is a tree, ie if it is an acyclic directed graph where node in degree is at 97 * most one. There is no complete connectivity test, so a forest is considered a tree (ie any sub graph of 98 * graph "root nodes" is a tree). 99 * 100 * \note is_tree_rec() will return true for an empty graph. 101 * \note is_tree_rec() will return true for a graph with only one vertice. 102 * \note complexity is at most O(2n) with n beeing number of vertices in input \c graph. 103 * \note Mandatory static behaviours: none 104 * \note Recursive algorithm with no overflow protection. 105 */ 106 template <class graph_t> 107 auto is_tree_rec(const graph_t& graph) noexcept -> bool; 108 109 /*! \brief Compute and return graph depth. 110 * 111 * FIXME: Warning, depend on graph root nodes (graph should bot contains circuits...) 112 * 113 * \note recursive algorithm with no overflow protection. 114 */ 115 template <class graph_t> 116 auto tree_depth_rec(const graph_t& graph) noexcept -> int; 117 118 /*! \brief Return a linearized DFS ordered version of a given tree. 119 * 120 * FIXME: Warning, depend on graph root nodes (graph should bot contains circuits...) 121 * 122 * \note recursive algorithm with no overflow protection. 123 */ 124 template <class graph_t> 125 auto linearize_tree_dfs_rec(const graph_t& graph) noexcept -> std::vector<typename graph_t::weak_node_t>; 126 127 /*! \brief Return nodes ordered by their level in DFS order. 128 * 129 * FIXME: Warning, depend on graph root nodes (graph should bot contains circuits...) 130 * 131 * \note recursive algorithm with no overflow protection. 132 */ 133 template <class graph_t> 134 auto levelize_tree_dfs_rec(const graph_t& graph) noexcept -> std::vector<std::vector<typename graph_t::weak_node_t > >; 135 //----------------------------------------------------------------------------- 136 137 138 139 /* BFS Graph Iterator *///----------------------------------------------------- 140 141 /* 142 foraward_iterator_tag, standard: http://www.cplusplus.com/reference/iterator/ForwardIterator/ 143 Is default-constructible, copy-constructible, copy-assignable and destructible X a; X b(a); b = a; 144 Can be compared for equivalence using the equality/inequality operators 145 (meaningful when both iterator values iterate over the same underlying sequence). a == b a != b 146 Can be dereferenced as an rvalue (if in a dereferenceable state). *a a->m 147 For mutable iterators (non-constant iterators): 148 Can be dereferenced as an lvalue (if in a dereferenceable state). *a = t 149 Can be incremented (if in a dereferenceable state). 150 The result is either also dereferenceable or a past-the-end iterator. 151 Two iterators that compare equal, keep comparing equal when both are increased. ++a a++ *a++ 152 Lvalues are swappable. swap(a,b) 153 */ 154 155 template <class graph_t> 156 class bfs_iterator 157 { 158 public: 159 using weak_node_t = typename graph_t::weak_node_t; 160 161 using iterator_category = ::std::forward_iterator_tag; 162 using value_type = weak_node_t; 163 using difference_type = int; 164 using pointer = weak_node_t*; 165 using reference = int&; 166 167 //! Create an invalid bfs_iterator. bfs_iterator()168 explicit bfs_iterator() { } 169 170 enum class pos_tag_t { BEGIN = 0, END = 1 }; 171 172 explicit bfs_iterator(graph_t& graph, pos_tag_t pos_tag = pos_tag_t::BEGIN) : 173 _nodes{&graph.get_nodes()} 174 { 175 if ( pos_tag == pos_tag_t::END ) { 176 // Do nothing, _nodes is initialized, let all internal iterators invalid 177 } else { 178 _node_iter = graph.get_nodes().begin(); 179 // Note: reserve() must be called on _candidate_unmarked, otherwise, _candidate_unmarked_iter 180 // is invalid at startup even after the call to begin()... 181 _candidate_unmarked.reserve(std::max(1, static_cast<int>(graph.get_nodes().size() / 3))); 182 _candidate_unmarked_iter = _candidate_unmarked.begin(); 183 operator++(); 184 } 185 } 186 187 bfs_iterator& operator++() 188 { 189 if ( _nodes == nullptr ) 190 return *this; 191 192 if ( _stack.empty() ) 193 step(); 194 195 // stack is empty, _nodes and _candidate_unmarked have all been 196 // consummed, set this iterator to end. 197 if ( _stack.empty() && 198 ( _node_iter >= _nodes->end() && 199 _candidate_unmarked_iter >= _candidate_unmarked.end() ) ) { 200 _node = weak_node_t{}; 201 return *this; 202 } 203 204 // FIXME FIXME FIXME !!!!!! 205 // N'enregistrer que lorsque on a mark.insert, il peut y avoir plusieurs 206 // node identiques dans la stack.... 207 bool node_found = false; 208 while ( !node_found && 209 !_stack.empty() ) { 210 auto node = _stack.top(); 211 _stack.pop(); 212 if ( _marks.find(node) == _marks.end() ) { 213 _node = node; 214 node_found = true; 215 _marks.insert(node); 216 push_sub_nodes_to_stack(node); // Push all current node out nodes on stack 217 } 218 } 219 return *this; 220 } 221 push_sub_nodes_to_stack(weak_node_t & weak_node)222 void push_sub_nodes_to_stack(weak_node_t& weak_node) 223 { 224 auto node = weak_node.lock(); 225 if ( !node ) 226 return; 227 for ( auto& out_node : node->get_out_nodes() ) { 228 if ( _marks.find(out_node) == _marks.end() ) 229 _stack.push(out_node); 230 } 231 } 232 233 // Advance from step node element our iterative DFS: multiple 234 // nodes may be enstacked during one step. step()235 void step() { 236 if ( _nodes == nullptr ) 237 return; 238 239 const auto push_to_stack = [this](const weak_node_t& weak_node) -> void { 240 auto node = weak_node.lock(); 241 if ( !node ) 242 return; 243 _stack.push(weak_node); 244 }; 245 246 if ( _node_iter < _nodes->end() ) { 247 while ( _node_iter < _nodes->end() ) { // Eat all marked nodes until we find an unmarked potential root/mother node 248 if ( !*_node_iter ) 249 continue; 250 if ( _marks.find(*_node_iter) == _marks.end() ) { // Skip already marked nodes 251 if ( (*_node_iter)->get_in_degree() == 0 ) { // Feed root/mother nodes first 252 push_to_stack(*_node_iter); // Push current node and it's out nodes to stack 253 _node_iter++; // Switch to next node for next call to feed_stack() 254 break; 255 } else 256 _candidate_unmarked.push_back(*_node_iter); 257 } 258 _node_iter++; // Switch to next node for next call to feed_stack() 259 } 260 } else if ( _candidate_unmarked_iter < _candidate_unmarked.end() ) { // all graph nodes have been consumed in a 261 // first pass, consume unmarked nodes with non 262 // zero indegree 263 while ( _candidate_unmarked_iter < _candidate_unmarked.end() ) { 264 if ( *_candidate_unmarked_iter && 265 _marks.count(*_candidate_unmarked_iter) == 0 ) { 266 push_to_stack(*_candidate_unmarked_iter); // Push current node and it's out nodes to stack 267 _candidate_unmarked_iter++; // Switch to next node for next call to feed_stack() 268 break; 269 } 270 _candidate_unmarked_iter++; 271 } 272 } 273 } 274 275 /*! Dereference DFS iterator at current position 276 * 277 * \warning There is no out of range protection, but dereferencing an out of bound iterator does NOT lead 278 * to undefined behaviour: an expired weak_ptr is returned. 279 * \throw noexcept 280 */ 281 value_type operator* () const 282 { 283 return _node; 284 } 285 286 bool operator==(const bfs_iterator<graph_t>& rhs) const 287 { 288 if ( _nodes != rhs._nodes ) 289 return false; // Fast exit 290 291 // 2 expired current node are considered equals 292 if ( _node.expired() && 293 rhs._node.expired() ) 294 return true; 295 if ( _node.expired() || // If one ptr is expired, they can't be equal (since they are not both nullptr, see previous check...) 296 rhs._node.expired() ) 297 return false; 298 return _nodes != rhs._nodes && 299 _node.lock().get() == rhs._node.lock().get(); 300 } 301 302 bool operator!=(const bfs_iterator<graph_t>& rhs) const { return !(*this == rhs); } 303 304 private: 305 typename graph_t::weak_node_t _node; 306 typename graph_t::shared_nodes_t::const_iterator _node_iter; 307 308 const typename graph_t::shared_nodes_t* _nodes = nullptr; 309 std::vector<typename graph_t::shared_node_t> _candidate_unmarked; 310 typename std::vector<typename graph_t::shared_node_t>::const_iterator _candidate_unmarked_iter; 311 312 using mark_t = ::std::unordered_set<weak_node_t>; 313 mark_t _marks; 314 315 ::std::stack<weak_node_t> _stack; 316 }; 317 318 template <class graph_t> 319 auto begin_dfs(graph_t& graph) noexcept -> bfs_iterator<graph_t> 320 { 321 return bfs_iterator<graph_t>{graph, bfs_iterator<graph_t>::pos_tag_t::BEGIN}; // RVO 322 } 323 324 template <class graph_t> 325 auto end_dfs(graph_t& graph) noexcept -> bfs_iterator<graph_t> 326 { 327 static_cast<void>(graph); 328 return bfs_iterator<graph_t>{graph, bfs_iterator<graph_t>::pos_tag_t::END}; // RVO 329 } 330 //----------------------------------------------------------------------------- 331 332 } // ::gtpo 333 334 #include "./algorithm.hpp" 335 336 #endif // gtpo_algorithm_h 337 338