diff --git a/libs/util/include/psemek/util/dfs.hpp b/libs/util/include/psemek/util/dfs.hpp new file mode 100644 index 00000000..5a1613ed --- /dev/null +++ b/libs/util/include/psemek/util/dfs.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include +#include + +#include +#include + +namespace psemek::util +{ + + template + struct dfs + { + dfs(Index count, EdgesProvider edges_provider) + : visited_(count, visited_flag::not_visited) + , parent_(count, null) + , edges_provider_(std::move(edges_provider)) + {} + + struct cycle_iterator + { + dfs * parent; + Index i; + + Index operator *() const { return i; } + cycle_iterator & operator ++() + { + i = parent->parent_[i]; + return *this; + } + + friend bool operator == (cycle_iterator const & i1, cycle_iterator const & i2) + { + return i1.i == i2.i; + } + }; + + template + void run(CycleCallback && cycle_callback) + { + for (Index i = 0; i < visited_.size(); ++i) + if (visited_[i] == visited_flag::not_visited) + step(i, cycle_callback); + } + + private: + static constexpr Index null = static_cast(-1); + + enum class visited_flag : std::uint8_t + { + not_visited, + visiting, + visited, + }; + + std::vector visited_; + std::vector parent_; + + EdgesProvider edges_provider_; + + template + void step(Index i, CycleCallback & cycle_callback) + { + visited_[i] = visited_flag::visiting; + + edges_provider_(i, [&](Index j) + { + switch (visited_[j]) + { + case visited_flag::not_visited: + parent_[j] = i; + step(j, cycle_callback); + break; + case visited_flag::visiting: + cycle_callback(util::range{cycle_iterator{this, i}, cycle_iterator{this, null}}); + break; + case visited_flag::visited: + break; + } + }); + + visited_[i] = visited_flag::visited; + } + }; + +}