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