1#ifndef ENTT_GRAPH_FLOW_HPP
2#define ENTT_GRAPH_FLOW_HPP
4#include "../config/config.h"
5#include "../container/dense_map.hpp"
6#include "../container/dense_set.hpp"
7#include "../core/compressed_pair.hpp"
8#include "../core/fwd.hpp"
9#include "../core/iterator.hpp"
10#include "../stl/algorithm.hpp"
11#include "../stl/concepts.hpp"
12#include "../stl/cstddef.hpp"
13#include "../stl/functional.hpp"
14#include "../stl/iterator.hpp"
15#include "../stl/memory.hpp"
16#include "../stl/type_traits.hpp"
17#include "../stl/utility.hpp"
18#include "../stl/vector.hpp"
19#include "adjacency_matrix.hpp"
28template<
typename Allocator>
30 using alloc_traits = stl::allocator_traits<Allocator>;
31 static_assert(stl::is_same_v<typename alloc_traits::value_type, id_type>,
"Invalid value type");
33 using ro_rw_container_type = stl::vector<stl::pair<stl::size_t, bool>,
typename alloc_traits::template rebind_alloc<stl::pair<stl::size_t, bool>>>;
37 void emplace(
const id_type res,
const bool is_rw) {
38 ENTT_ASSERT(index.first() < vertices.size(),
"Invalid node");
40 if(!deps.contains(res) && sync_on != vertices.size()) {
41 deps[res].emplace_back(sync_on,
true);
44 deps[res].emplace_back(index.first(), is_rw);
47 void setup_graph(adjacency_matrix_type &matrix)
const {
48 for(
const auto &elem: deps) {
49 const auto last = elem.second.cend();
50 auto it = elem.second.cbegin();
55 if(
auto curr = it++; it != last) {
57 matrix.
insert(curr->first, it->first);
58 }
else if(
const auto next = stl::find_if(it, last, [](
const auto &value) {
return value.second; }); next != last) {
59 for(; it != next; ++it) {
60 matrix.
insert(curr->first, it->first);
61 matrix.
insert(it->first, next->first);
64 for(; it != next; ++it) {
65 matrix.
insert(curr->first, it->first);
71 if(
const auto next = stl::find_if(it, last, [](
const auto &value) {
return value.second; }); next != last) {
72 for(; it != next; ++it) {
73 matrix.
insert(it->first, next->first);
83 void transitive_closure(adjacency_matrix_type &matrix)
const {
84 const auto length = matrix.
size();
86 for(stl::size_t vk{}; vk < length; ++vk) {
87 for(stl::size_t vi{}; vi < length; ++vi) {
88 for(stl::size_t vj{}; vj < length; ++vj) {
97 void transitive_reduction(adjacency_matrix_type &matrix)
const {
98 const auto length = matrix.
size();
100 for(stl::size_t vert{}; vert < length; ++vert) {
101 matrix.
erase(vert, vert);
104 for(stl::size_t vj{}; vj < length; ++vj) {
105 for(stl::size_t vi{}; vi < length; ++vi) {
107 for(stl::size_t vk{}; vk < length; ++vk) {
109 matrix.
erase(vi, vk);
136 : index{0u, allocator},
149 : index{other.index.first(), allocator},
150 vertices{other.vertices, allocator},
151 deps{other.deps, allocator},
152 sync_on{other.sync_on} {}
163 : index{other.index.first(), allocator},
164 vertices{stl::move(other.vertices), allocator},
165 deps{stl::move(other.deps), allocator},
166 sync_on{other.sync_on} {}
189 swap(index, other.index);
190 swap(vertices, other.vertices);
191 swap(deps, other.deps);
192 swap(sync_on, other.sync_on);
224 [[nodiscard]]
bool empty() const noexcept {
225 return vertices.empty();
233 return vertices.size();
242 sync_on += (sync_on == vertices.size());
243 const auto it = vertices.emplace(value).first;
244 index.first() =
size_type(it - vertices.begin());
253 ENTT_ASSERT(index.first() < vertices.size(),
"Invalid node");
254 sync_on = index.first();
256 for(
const auto &elem: deps) {
257 elem.second.emplace_back(sync_on,
true);
290 basic_flow &
ro(stl::input_iterator
auto first, stl::input_iterator
auto last) {
291 for(; first != last; ++first) {
292 emplace(*first,
false);
314 basic_flow &
rw(stl::input_iterator
auto first, stl::input_iterator
auto last) {
315 for(; first != last; ++first) {
316 emplace(*first,
true);
330 transitive_closure(matrix);
331 transitive_reduction(matrix);
338 task_container_type vertices;
339 deps_container_type deps;
Basic implementation of a directed adjacency matrix.
size_type size() const noexcept
Returns the number of vertices.
bool contains(const vertex_type lhs, const vertex_type rhs) const
Checks if an adjacency matrix contains a given edge.
stl::pair< edge_iterator, bool > insert(const vertex_type lhs, const vertex_type rhs)
Inserts an edge into the adjacency matrix, if it does not exist.
size_type erase(const vertex_type lhs, const vertex_type rhs)
Removes the edge associated with a pair of given vertices.
void clear() noexcept
Clears the flow builder.
id_type operator[](const size_type pos) const
Returns the identifier at specified location.
Allocator allocator_type
Allocator type.
size_type size() const noexcept
Returns the number of tasks.
iterable_adaptor< typename task_container_type::const_iterator > iterable
Iterable task list.
basic_flow & sync()
Turns the current task into a sync point.
basic_flow & rw(const id_type res)
Assigns a writable resource to the current task.
constexpr allocator_type get_allocator() const noexcept
Returns the associated allocator.
graph_type graph() const
Generates a task graph for the current content.
basic_flow(const allocator_type &allocator)
Constructs a flow builder with a given allocator.
adjacency_matrix_type graph_type
Adjacency matrix type.
~basic_flow()=default
Default destructor.
basic_flow & ro(stl::input_iterator auto first, stl::input_iterator auto last)
Assigns a range of read-only resources to the current task.
basic_flow & ro(const id_type res)
Assigns a read-only resource to the current task.
basic_flow(basic_flow &&) noexcept=default
Default move constructor.
basic_flow & bind(const id_type value)
Binds a task to a flow builder.
basic_flow & set(const id_type res, bool is_rw=false)
Assigns a resource to the current task with a given access mode.
stl::size_t size_type
Unsigned integer type.
basic_flow & operator=(basic_flow &&) noexcept=default
Default move assignment operator.
basic_flow()
Default constructor.
basic_flow(const basic_flow &other, const allocator_type &allocator)
Allocator-extended copy constructor.
void swap(basic_flow &other) noexcept
bool empty() const noexcept
Returns true if a flow builder contains no tasks, false otherwise.
basic_flow & operator=(const basic_flow &)=default
Default copy assignment operator.
basic_flow & rw(stl::input_iterator auto first, stl::input_iterator auto last)
Assigns a range of writable resources to the current task.
basic_flow(const basic_flow &)=default
Default copy constructor.
Associative container for key-value pairs with unique keys.
Associative container for unique objects of a given type.
stl::ptrdiff_t difference_type
stl::uint32_t id_type
Alias declaration for type identifiers.
Utility class to create an iterable object from a pair of iterators.