From 9845d2c87d6bc8aabb5f147c181f5513bb19e096 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Fri, 17 Jun 2022 10:36:04 +0200 Subject: [PATCH] adjacency_matrix: * iterator review * in_edges iterable --- TODO | 2 +- src/entt/graph/adjacency_matrix.hpp | 150 +++++++++++++++++++++------ test/entt/graph/adjacency_matrix.cpp | 20 +++- 3 files changed, 138 insertions(+), 34 deletions(-) diff --git a/TODO b/TODO index 63f8c5874..644f527be 100644 --- a/TODO +++ b/TODO @@ -13,7 +13,7 @@ DOC: * update entity doc when the storage based model is in place WIP: -* add directed/undirected support to adjacency matrix (see brief) +* add directed/undirected support to adjacency matrix (see brief) and refine iterators a little * handle: replace visit with an iterable object * add storage getter for filters to views and groups * exploit the tombstone mechanism to allow enabling/disabling entities (see bump, compact and clear for further details) diff --git a/src/entt/graph/adjacency_matrix.hpp b/src/entt/graph/adjacency_matrix.hpp index 283208c2b..ea4a7b43c 100644 --- a/src/entt/graph/adjacency_matrix.hpp +++ b/src/entt/graph/adjacency_matrix.hpp @@ -20,37 +20,38 @@ namespace entt { namespace internal { -struct edge_iterator { - using value_type = std::pair; +template +class row_edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - constexpr edge_iterator() noexcept - : matrix{}, + constexpr row_edge_iterator() noexcept + : it{}, vert{}, - it{}, + pos{}, last{} {} - constexpr edge_iterator(const std::size_t *ref, const std::size_t vertices, const std::size_t from) noexcept - : edge_iterator{ref, vertices, from, vertices * vertices} {} - - constexpr edge_iterator(const std::size_t *ref, const std::size_t vertices, const std::size_t from, const std::size_t to) noexcept - : matrix{ref}, + constexpr row_edge_iterator(It base, const size_type vertices, const size_type from, const size_type to) noexcept + : it{std::move(base)}, vert{vertices}, - it{from}, + pos{from}, last{to} { - for(; it != last && !matrix[it]; ++it) {} + for(; pos != last && !it[pos]; ++pos) {} } - constexpr edge_iterator &operator++() noexcept { - for(++it; it != last && !matrix[it]; ++it) {} + constexpr row_edge_iterator &operator++() noexcept { + for(++pos; pos != last && !it[pos]; ++pos) {} return *this; } - constexpr edge_iterator operator++(int) noexcept { - edge_iterator orig = *this; + constexpr row_edge_iterator operator++(int) noexcept { + row_edge_iterator orig = *this; return ++(*this), orig; } @@ -59,23 +60,89 @@ struct edge_iterator { } [[nodiscard]] constexpr pointer operator->() const noexcept { - return std::make_pair(it / vert, it % vert); + return std::make_pair(pos / vert, pos % vert); } - friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + template + friend constexpr bool operator==(const row_edge_iterator &, const row_edge_iterator &) noexcept; private: - const std::size_t *matrix; - std::size_t vert; - std::size_t it; - std::size_t last; + It it; + size_type vert; + size_type pos; + size_type last; }; -[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { - return lhs.it == rhs.it; +template +[[nodiscard]] inline constexpr bool operator==(const row_edge_iterator &lhs, const row_edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; } -[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { +template +[[nodiscard]] inline constexpr bool operator!=(const row_edge_iterator &lhs, const row_edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +class col_edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + constexpr col_edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{} {} + + constexpr col_edge_iterator(It base, const size_type vertices, const size_type from, const size_type to) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to} { + for(; pos != last && !it[pos]; pos += vert) {} + } + + constexpr col_edge_iterator &operator++() noexcept { + for(pos += vert; pos != last && !it[pos]; pos += vert) {} + return *this; + } + + constexpr col_edge_iterator operator++(int) noexcept { + col_edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const col_edge_iterator &, const col_edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const col_edge_iterator &lhs, const col_edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const col_edge_iterator &lhs, const col_edge_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -108,7 +175,11 @@ public: /*! @brief Vertex iterator type. */ using vertex_iterator = iota_iterator; /*! @brief Edge iterator type. */ - using edge_iterator = internal::edge_iterator; + using edge_iterator = internal::row_edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = internal::row_edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = internal::col_edge_iterator; /*! @brief Default constructor. */ basic_adjacency_matrix() noexcept(noexcept(allocator_type{})) @@ -230,7 +301,9 @@ public: * @return An iterable object to visit all edges of a matrix. */ [[nodiscard]] iterable_adaptor edges() const noexcept { - return {{matrix.data(), vert, 0u}, {matrix.data(), vert, matrix.size()}}; + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz}, {it, vert, sz, sz}}; } /** @@ -238,10 +311,23 @@ public: * @param vertex The vertex of which to return all out edges. * @return An iterable object to visit all out edges of a vertex. */ - [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { - const auto first = vertex * vert; - const auto last = vertex * vert + vert; - return {{matrix.data(), vert, first, last}, {matrix.data(), vert, last, last}}; + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = vertex * vert + vert; + return {{it, vert, from, to}, {it, vert, to, to}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vertex * vert + vertex; + return {{it, vert, from, to}, {it, vert, to, to}}; } /** @@ -264,7 +350,7 @@ public: std::pair insert(const vertex_type lhs, const vertex_type rhs) { const auto pos = lhs * vert + rhs; const auto inserted = !std::exchange(matrix[pos], 1u); - return {edge_iterator{matrix.data(), vert, pos}, inserted}; + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size()}, inserted}; } /** diff --git a/test/entt/graph/adjacency_matrix.cpp b/test/entt/graph/adjacency_matrix.cpp index fff0ace5f..2b0ef5f95 100644 --- a/test/entt/graph/adjacency_matrix.cpp +++ b/test/entt/graph/adjacency_matrix.cpp @@ -254,7 +254,7 @@ TEST(AdjacencyMatrix, Edges) { TEST(AdjacencyMatrix, OutEdges) { entt::adjacency_matrix adjacency_matrix{3u}; - auto iterable = adjacency_matrix.edges(); + auto iterable = adjacency_matrix.out_edges(0u); ASSERT_EQ(iterable.begin(), iterable.end()); @@ -270,6 +270,24 @@ TEST(AdjacencyMatrix, OutEdges) { ASSERT_EQ(it, iterable.end()); } +TEST(AdjacencyMatrix, InEdges) { + entt::adjacency_matrix adjacency_matrix{3u}; + auto iterable = adjacency_matrix.in_edges(1u); + + ASSERT_EQ(iterable.begin(), iterable.end()); + + adjacency_matrix.insert(0u, 1u); + adjacency_matrix.insert(1u, 2u); + iterable = adjacency_matrix.in_edges(1u); + + ASSERT_NE(iterable.begin(), iterable.end()); + + auto it = iterable.begin(); + + ASSERT_EQ(*it++, std::make_pair(std::size_t{0u}, std::size_t{1u})); + ASSERT_EQ(it, iterable.end()); +} + TEST(AdjacencyMatrix, ThrowingAllocator) { using allocator = test::throwing_allocator; using exception = typename allocator::exception_type;