瀏覽代碼

adjacency_matrix:
* iterator review
* in_edges iterable

Michele Caini 3 年之前
父節點
當前提交
9845d2c87d
共有 3 個文件被更改,包括 137 次插入33 次删除
  1. 1 1
      TODO
  2. 117 31
      src/entt/graph/adjacency_matrix.hpp
  3. 19 1
      test/entt/graph/adjacency_matrix.cpp

+ 1 - 1
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)

+ 117 - 31
src/entt/graph/adjacency_matrix.hpp

@@ -20,37 +20,101 @@ namespace entt {
 
 namespace internal {
 
-struct edge_iterator {
-    using value_type = std::pair<std::size_t, std::size_t>;
+template<typename It>
+class row_edge_iterator {
+    using size_type = std::size_t;
+
+public:
+    using value_type = std::pair<size_type, size_type>;
     using pointer = input_iterator_pointer<value_type>;
     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 row_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) {}
+    }
+
+    constexpr row_edge_iterator &operator++() noexcept {
+        for(++pos; pos != last && !it[pos]; ++pos) {}
+        return *this;
+    }
+
+    constexpr row_edge_iterator operator++(int) noexcept {
+        row_edge_iterator orig = *this;
+        return ++(*this), orig;
+    }
 
-    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},
+    [[nodiscard]] constexpr reference operator*() const noexcept {
+        return *operator->();
+    }
+
+    [[nodiscard]] constexpr pointer operator->() const noexcept {
+        return std::make_pair<size_type>(pos / vert, pos % vert);
+    }
+
+    template<typename Type>
+    friend constexpr bool operator==(const row_edge_iterator<Type> &, const row_edge_iterator<Type> &) noexcept;
+
+private:
+    It it;
+    size_type vert;
+    size_type pos;
+    size_type last;
+};
+
+template<typename Container>
+[[nodiscard]] inline constexpr bool operator==(const row_edge_iterator<Container> &lhs, const row_edge_iterator<Container> &rhs) noexcept {
+    return lhs.pos == rhs.pos;
+}
+
+template<typename Container>
+[[nodiscard]] inline constexpr bool operator!=(const row_edge_iterator<Container> &lhs, const row_edge_iterator<Container> &rhs) noexcept {
+    return !(lhs == rhs);
+}
+
+template<typename It>
+class col_edge_iterator {
+    using size_type = std::size_t;
+
+public:
+    using value_type = std::pair<size_type, size_type>;
+    using pointer = input_iterator_pointer<value_type>;
+    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},
-          it{from},
+          pos{from},
           last{to} {
-        for(; it != last && !matrix[it]; ++it) {}
+        for(; pos != last && !it[pos]; pos += vert) {}
     }
 
-    constexpr edge_iterator &operator++() noexcept {
-        for(++it; it != last && !matrix[it]; ++it) {}
+    constexpr col_edge_iterator &operator++() noexcept {
+        for(pos += vert; pos != last && !it[pos]; pos += vert) {}
         return *this;
     }
 
-    constexpr edge_iterator operator++(int) noexcept {
-        edge_iterator orig = *this;
+    constexpr col_edge_iterator operator++(int) noexcept {
+        col_edge_iterator orig = *this;
         return ++(*this), orig;
     }
 
@@ -59,23 +123,26 @@ struct edge_iterator {
     }
 
     [[nodiscard]] constexpr pointer operator->() const noexcept {
-        return std::make_pair<std::size_t>(it / vert, it % vert);
+        return std::make_pair<size_type>(pos / vert, pos % vert);
     }
 
-    friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept;
+    template<typename Type>
+    friend constexpr bool operator==(const col_edge_iterator<Type> &, const col_edge_iterator<Type> &) 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<typename Container>
+[[nodiscard]] inline constexpr bool operator==(const col_edge_iterator<Container> &lhs, const col_edge_iterator<Container> &rhs) noexcept {
+    return lhs.pos == rhs.pos;
 }
 
-[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept {
+template<typename Container>
+[[nodiscard]] inline constexpr bool operator!=(const col_edge_iterator<Container> &lhs, const col_edge_iterator<Container> &rhs) noexcept {
     return !(lhs == rhs);
 }
 
@@ -108,7 +175,11 @@ public:
     /*! @brief Vertex iterator type. */
     using vertex_iterator = iota_iterator<vertex_type>;
     /*! @brief Edge iterator type. */
-    using edge_iterator = internal::edge_iterator;
+    using edge_iterator = internal::row_edge_iterator<typename container_type::const_iterator>;
+    /*! @brief Out edge iterator type. */
+    using out_edge_iterator = internal::row_edge_iterator<typename container_type::const_iterator>;
+    /*! @brief In edge iterator type. */
+    using in_edge_iterator = internal::col_edge_iterator<typename container_type::const_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<edge_iterator> 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<edge_iterator> 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_edge_iterator> 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_edge_iterator> 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<edge_iterator, bool> 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};
     }
 
     /**

+ 19 - 1
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<std::size_t>;
     using exception = typename allocator::exception_type;