Explorar el Código

graph: (currently directed only) adjacency_matrix

Michele Caini hace 3 años
padre
commit
8029777c4e

+ 2 - 0
CMakeLists.txt

@@ -147,6 +147,8 @@ if(ENTT_INCLUDE_HEADERS)
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sparse_set.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/view.hpp>
+            $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/adjacency_matrix.hpp>
+            $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/fwd.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/locator/locator.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/adl_pointer.hpp>
             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/container.hpp>

+ 1 - 0
TODO

@@ -12,6 +12,7 @@ DOC:
 * document iterator.hpp (see core.md)
 
 WIP:
+* add directed/undirected support to adjacency matrix (see brief)
 * 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)

+ 1 - 0
src/entt/entt.hpp

@@ -32,6 +32,7 @@
 #include "entity/sparse_set.hpp"
 #include "entity/storage.hpp"
 #include "entity/view.hpp"
+#include "graph/adjacency_matrix.hpp"
 #include "locator/locator.hpp"
 #include "meta/adl_pointer.hpp"
 #include "meta/container.hpp"

+ 1 - 0
src/entt/fwd.hpp

@@ -1,6 +1,7 @@
 #include "container/fwd.hpp"
 #include "core/fwd.hpp"
 #include "entity/fwd.hpp"
+#include "graph/fwd.hpp"
 #include "meta/fwd.hpp"
 #include "poly/fwd.hpp"
 #include "process/fwd.hpp"

+ 298 - 0
src/entt/graph/adjacency_matrix.hpp

@@ -0,0 +1,298 @@
+#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP
+#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#include "../config/config.h"
+#include "../core/iterator.hpp"
+#include "fwd.hpp"
+
+namespace entt {
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+struct edge_iterator {
+    using value_type = std::pair<std::size_t, std::size_t>;
+    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{},
+          vert{},
+          it{},
+          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},
+          vert{vertices},
+          it{from},
+          last{to} {
+        for(; it != last && !matrix[it]; ++it) {}
+    }
+
+    constexpr edge_iterator &operator++() noexcept {
+        for(++it; it != last && !matrix[it]; ++it) {}
+        return *this;
+    }
+
+    constexpr edge_iterator operator++(int) noexcept {
+        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<std::size_t>(it / vert, it % vert);
+    }
+
+    friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept;
+
+private:
+    const std::size_t *matrix;
+    std::size_t vert;
+    std::size_t it;
+    std::size_t last;
+};
+
+[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept {
+    return lhs.it == rhs.it;
+}
+
+[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept {
+    return !(lhs == rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+/**
+ * @brief Basic implementation of a directed adjacency matrix.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Allocator>
+class basic_adjacency_matrix {
+    using alloc_traits = std::allocator_traits<Allocator>;
+    static_assert(std::is_same_v<typename alloc_traits::value_type, std::size_t>, "Invalid value type");
+    using container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
+
+public:
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
+    /*! @brief Unsigned integer type. */
+    using size_type = std::size_t;
+    /*! @brief Vertex type. */
+    using vertex_type = size_type;
+    /*! @brief Edge type. */
+    using edge_type = std::pair<vertex_type, vertex_type>;
+    /*! @brief Vertex iterator type. */
+    using vertex_iterator = iota_iterator<vertex_type>;
+    /*! @brief Edge iterator type. */
+    using edge_iterator = internal::edge_iterator;
+
+    /*! @brief Default constructor. */
+    basic_adjacency_matrix() noexcept(noexcept(allocator_type{}))
+        : basic_adjacency_matrix{0u} {}
+
+    /**
+     * @brief Constructs an empty container with a given allocator.
+     * @param allocator The allocator to use.
+     */
+    explicit basic_adjacency_matrix(const allocator_type &allocator) noexcept
+        : basic_adjacency_matrix{0u, allocator} {}
+
+    /**
+     * @brief Constructs an empty container with a given allocator and user
+     * supplied number of vertices.
+     * @param vertices Number of vertices.
+     * @param allocator The allocator to use.
+     */
+    basic_adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{})
+        : matrix(vertices * vertices),
+          vert{vertices} {}
+
+    /**
+     * @brief Copy constructor.
+     * @param other The instance to copy from.
+     */
+    basic_adjacency_matrix(const basic_adjacency_matrix &other)
+        : basic_adjacency_matrix{other, other.get_allocator()} {}
+
+    /**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+    basic_adjacency_matrix(const basic_adjacency_matrix &other, const allocator_type &allocator)
+        : matrix{other.matrix, allocator},
+          vert{other.vert} {}
+
+    /**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+    basic_adjacency_matrix(basic_adjacency_matrix &&other) noexcept
+        : basic_adjacency_matrix{std::move(other), other.get_allocator()} {}
+
+    /**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    basic_adjacency_matrix(basic_adjacency_matrix &&other, const allocator_type &allocator)
+        : matrix{std::move(other.matrix), allocator},
+          vert{std::exchange(other.vert, 0u)} {}
+
+    /**
+     * @brief Default copy assignment operator.
+     * @param other The instance to copy from.
+     * @return This container.
+     */
+    basic_adjacency_matrix &operator=(const basic_adjacency_matrix &other) {
+        matrix = other.matrix;
+        vert = other.vert;
+        return *this;
+    }
+
+    /**
+     * @brief Default move assignment operator.
+     * @param other The instance to move from.
+     * @return This container.
+     */
+    basic_adjacency_matrix &operator=(basic_adjacency_matrix &&other) noexcept {
+        matrix = std::move(other.matrix);
+        vert = std::exchange(other.vert, 0u);
+        return *this;
+    }
+
+    /**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+        return matrix.get_allocator();
+    }
+
+    /*! @brief Clears the adjacency matrix. */
+    void clear() noexcept {
+        matrix.clear();
+        vert = {};
+    }
+
+    /**
+     * @brief Exchanges the contents with those of a given adjacency matrix.
+     * @param other Adjacency matrix to exchange the content with.
+     */
+    void swap(adjacency_matrix &other) {
+        using std::swap;
+        swap(matrix, other.matrix);
+        swap(vert, other.vert);
+    }
+
+    /**
+     * @brief Returns the number of vertices.
+     * @return The number of vertices.
+     */
+    [[nodiscard]] size_type size() const noexcept {
+        return vert;
+    }
+
+    /**
+     * @brief Returns an iterable object to visit all vertices of a matrix.
+     * @return An iterable object to visit all vertices of a matrix.
+     */
+    [[nodiscard]] iterable_adaptor<vertex_iterator> vertices() const noexcept {
+        return {0u, vert};
+    }
+
+    /**
+     * @brief Returns an iterable object to visit all edges of a matrix.
+     * @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()}};
+    }
+
+    /**
+     * @brief Returns an iterable object to visit all edges of a vertex.
+     * @param vertex The vertex of which to return all edges.
+     * @return An iterable object to visit all edges of a vertex.
+     */
+    [[nodiscard]] iterable_adaptor<edge_iterator> 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}};
+    }
+
+    /**
+     * @brief Resizes an adjacency matrix.
+     * @param vertices The new number of vertices.
+     */
+    void resize(const size_type vertices) {
+        matrix.resize(vertices * vertices);
+        vert = vertices;
+    }
+
+    /**
+     * @brief Inserts an edge into the adjacency matrix, if it does not exist.
+     * @param lhs The left hand vertex of the edge.
+     * @param rhs The right hand vertex of the edge.
+     * @return A pair consisting of an iterator to the inserted element (or to
+     * the element that prevented the insertion) and a bool denoting whether the
+     * insertion took place.
+     */
+    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};
+    }
+
+    /**
+     * @brief Removes the edge associated with a pair of given vertices.
+     * @param lhs The left hand vertex of the edge.
+     * @param rhs The right hand vertex of the edge.
+     * @return Number of elements removed (either 0 or 1).
+     */
+    size_type erase(const vertex_type lhs, const vertex_type rhs) {
+        return std::exchange(matrix[lhs * vert + rhs], 0u);
+    }
+
+    /**
+     * @brief Checks if an adjacency matrix contains a given edge.
+     * @param lhs The left hand vertex of the edge.
+     * @param rhs The right hand vertex of the edge.
+     * @return True if there is such an edge, false otherwise.
+     */
+    [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const {
+        const auto pos = lhs * vert + rhs;
+        return pos < matrix.size() && matrix[pos];
+    }
+
+private:
+    container_type matrix;
+    size_type vert;
+};
+
+} // namespace entt
+
+#endif

+ 17 - 0
src/entt/graph/fwd.hpp

@@ -0,0 +1,17 @@
+#ifndef ENTT_GRAPH_FWD_HPP
+#define ENTT_GRAPH_FWD_HPP
+
+#include <cstddef>
+#include <memory>
+
+namespace entt {
+
+template<typename = std::allocator<std::size_t>>
+struct basic_adjacency_matrix;
+
+/*! @brief Alias declaration for the most common use case. */
+using adjacency_matrix = basic_adjacency_matrix<>;
+
+} // namespace entt
+
+#endif

+ 4 - 0
test/CMakeLists.txt

@@ -217,6 +217,10 @@ SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
 SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
 SETUP_BASIC_TEST(view entt/entity/view.cpp)
 
+# Test graph
+
+SETUP_BASIC_TEST(adjacency_matrix entt/graph/adjacency_matrix.cpp)
+
 # Test locator
 
 SETUP_BASIC_TEST(locator entt/locator/locator.cpp)

+ 289 - 0
test/entt/graph/adjacency_matrix.cpp

@@ -0,0 +1,289 @@
+#include <memory>
+#include <utility>
+#include <gtest/gtest.h>
+#include <entt/graph/adjacency_matrix.hpp>
+#include "../common/throwing_allocator.hpp"
+
+TEST(AdjacencyMatrix, Resize) {
+    entt::adjacency_matrix adjacency_matrix{2};
+    adjacency_matrix.insert(0u, 1u);
+
+    ASSERT_EQ(adjacency_matrix.size(), 2u);
+    ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
+
+    adjacency_matrix.resize(3u);
+
+    ASSERT_EQ(adjacency_matrix.size(), 3u);
+    ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
+}
+
+TEST(AdjacencyMatrix, Constructors) {
+    entt::adjacency_matrix adjacency_matrix{};
+
+    ASSERT_EQ(adjacency_matrix.size(), 0u);
+
+    adjacency_matrix = entt::adjacency_matrix{std::allocator<bool>{}};
+    adjacency_matrix = entt::adjacency_matrix{3u, std::allocator<bool>{}};
+
+    ASSERT_EQ(adjacency_matrix.size(), 3u);
+
+    adjacency_matrix.insert(0u, 1u);
+
+    entt::adjacency_matrix temp{adjacency_matrix, adjacency_matrix.get_allocator()};
+    entt::adjacency_matrix other{std::move(adjacency_matrix), adjacency_matrix.get_allocator()};
+
+    ASSERT_EQ(adjacency_matrix.size(), 0u);
+    ASSERT_EQ(other.size(), 3u);
+
+    ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
+    ASSERT_TRUE(other.contains(0u, 1u));
+}
+
+TEST(AdjacencyMatrix, Copy) {
+    entt::adjacency_matrix adjacency_matrix{3u};
+    adjacency_matrix.insert(0u, 1u);
+
+    entt::adjacency_matrix other{adjacency_matrix};
+
+    ASSERT_EQ(adjacency_matrix.size(), 3u);
+    ASSERT_EQ(other.size(), 3u);
+
+    ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
+    ASSERT_TRUE(other.contains(0u, 1u));
+
+    adjacency_matrix.resize(4u);
+    adjacency_matrix.insert(0u, 2u);
+    other.insert(1u, 2u);
+
+    other = adjacency_matrix;
+
+    ASSERT_EQ(other.size(), 4u);
+    ASSERT_EQ(adjacency_matrix.size(), 4u);
+
+    ASSERT_TRUE(other.contains(0u, 1u));
+    ASSERT_FALSE(other.contains(1u, 2u));
+    ASSERT_TRUE(other.contains(0u, 2u));
+}
+
+TEST(AdjacencyMatrix, Move) {
+    entt::adjacency_matrix adjacency_matrix{3u};
+    adjacency_matrix.insert(0u, 1u);
+
+    entt::adjacency_matrix other{std::move(adjacency_matrix)};
+
+    ASSERT_EQ(adjacency_matrix.size(), 0u);
+    ASSERT_EQ(other.size(), 3u);
+
+    ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
+    ASSERT_TRUE(other.contains(0u, 1u));
+
+    adjacency_matrix = {};
+    adjacency_matrix.resize(4u);
+    adjacency_matrix.insert(0u, 2u);
+    other.insert(1u, 2u);
+
+    other = std::move(adjacency_matrix);
+
+    ASSERT_EQ(other.size(), 4u);
+    ASSERT_EQ(adjacency_matrix.size(), 0u);
+
+    ASSERT_FALSE(other.contains(0u, 1u));
+    ASSERT_FALSE(other.contains(1u, 2u));
+    ASSERT_TRUE(other.contains(0u, 2u));
+}
+
+TEST(AdjacencyMatrix, Swap) {
+    entt::adjacency_matrix adjacency_matrix{3u};
+    entt::adjacency_matrix other{};
+
+    adjacency_matrix.insert(0u, 1u);
+
+    ASSERT_EQ(other.size(), 0u);
+    ASSERT_EQ(adjacency_matrix.size(), 3u);
+    ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
+    ASSERT_FALSE(other.contains(0u, 1u));
+
+    adjacency_matrix.swap(other);
+
+    ASSERT_EQ(other.size(), 3u);
+    ASSERT_EQ(adjacency_matrix.size(), 0u);
+    ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
+    ASSERT_TRUE(other.contains(0u, 1u));
+}
+
+TEST(AdjacencyMatrix, Insert) {
+    entt::adjacency_matrix adjacency_matrix{3u};
+
+    auto first = adjacency_matrix.insert(0u, 1u);
+    auto second = adjacency_matrix.insert(0u, 2u);
+    auto other = adjacency_matrix.insert(0u, 1u);
+
+    ASSERT_TRUE(first.second);
+    ASSERT_TRUE(second.second);
+    ASSERT_FALSE(other.second);
+
+    ASSERT_NE(first.first, second.first);
+    ASSERT_EQ(first.first, other.first);
+
+    ASSERT_EQ(*first.first, std::make_pair(std::size_t{0u}, std::size_t{1u}));
+    ASSERT_EQ(*second.first, std::make_pair(std::size_t{0u}, std::size_t{2u}));
+}
+
+TEST(AdjacencyMatrix, Erase) {
+    entt::adjacency_matrix adjacency_matrix{3u};
+
+    adjacency_matrix.insert(0u, 1u);
+
+    ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
+    ASSERT_EQ(adjacency_matrix.erase(0u, 1u), 1u);
+    ASSERT_EQ(adjacency_matrix.erase(0u, 1u), 0u);
+    ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
+}
+
+TEST(AdjacencyMatrix, Clear) {
+    entt::adjacency_matrix adjacency_matrix{3u};
+
+    adjacency_matrix.insert(0u, 1u);
+    adjacency_matrix.insert(0u, 2u);
+
+    ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
+    ASSERT_TRUE(adjacency_matrix.contains(0u, 2u));
+    ASSERT_EQ(adjacency_matrix.size(), 3u);
+
+    adjacency_matrix.clear();
+
+    ASSERT_FALSE(adjacency_matrix.contains(0u, 1u));
+    ASSERT_FALSE(adjacency_matrix.contains(0u, 2u));
+    ASSERT_EQ(adjacency_matrix.size(), 0u);
+}
+
+TEST(AdjacencyMatrix, VertexIterator) {
+    using iterator = typename entt::adjacency_matrix::vertex_iterator;
+
+    static_assert(std::is_same_v<iterator::value_type, std::size_t>);
+    static_assert(std::is_same_v<iterator::pointer, void>);
+    static_assert(std::is_same_v<iterator::reference, std::size_t>);
+
+    entt::adjacency_matrix adjacency_matrix{2u};
+    const auto iterable = adjacency_matrix.vertices();
+
+    iterator end{iterable.begin()};
+    iterator begin{};
+
+    begin = iterable.end();
+    std::swap(begin, end);
+
+    ASSERT_EQ(begin, iterable.begin());
+    ASSERT_EQ(end, iterable.end());
+    ASSERT_NE(begin, end);
+
+    ASSERT_EQ(*begin, 0u);
+    ASSERT_EQ(begin++, iterable.begin());
+    ASSERT_EQ(*begin, 1u);
+    ASSERT_EQ(++begin, iterable.end());
+}
+
+TEST(AdjacencyMatrix, EdgeIterator) {
+    using iterator = typename entt::adjacency_matrix::edge_iterator;
+
+    static_assert(std::is_same_v<iterator::value_type, std::pair<std::size_t, std::size_t>>);
+    static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<std::size_t, std::size_t>>>);
+    static_assert(std::is_same_v<iterator::reference, std::pair<std::size_t, std::size_t>>);
+
+    entt::adjacency_matrix adjacency_matrix{3u};
+
+    adjacency_matrix.insert(0u, 1u);
+    adjacency_matrix.insert(0u, 2u);
+
+    const auto iterable = adjacency_matrix.edges();
+
+    iterator end{iterable.begin()};
+    iterator begin{};
+
+    begin = iterable.end();
+    std::swap(begin, end);
+
+    ASSERT_EQ(begin, iterable.begin());
+    ASSERT_EQ(end, iterable.end());
+    ASSERT_NE(begin, end);
+
+    ASSERT_EQ(*begin, std::make_pair(std::size_t{0u}, std::size_t{1u}));
+    ASSERT_EQ(begin++, iterable.begin());
+    ASSERT_EQ(*begin.operator->(), std::make_pair(std::size_t{0u}, std::size_t{2u}));
+    ASSERT_EQ(++begin, iterable.end());
+}
+
+TEST(AdjacencyMatrix, Vertices) {
+    entt::adjacency_matrix adjacency_matrix{};
+    auto iterable = adjacency_matrix.vertices();
+
+    ASSERT_EQ(adjacency_matrix.size(), 0u);
+    ASSERT_EQ(iterable.begin(), iterable.end());
+
+    adjacency_matrix.resize(2u);
+    iterable = adjacency_matrix.vertices();
+
+    ASSERT_EQ(adjacency_matrix.size(), 2u);
+    ASSERT_NE(iterable.begin(), iterable.end());
+
+    auto it = iterable.begin();
+
+    ASSERT_EQ(*it++, 0u);
+    ASSERT_EQ(*it, 1u);
+    ASSERT_EQ(++it, iterable.end());
+}
+
+TEST(AdjacencyMatrix, Edges) {
+    entt::adjacency_matrix adjacency_matrix{3u};
+    auto iterable = adjacency_matrix.edges();
+
+    ASSERT_EQ(iterable.begin(), iterable.end());
+
+    adjacency_matrix.insert(0u, 1u);
+    adjacency_matrix.insert(1u, 2u);
+    iterable = adjacency_matrix.edges();
+
+    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.operator->(), std::make_pair(std::size_t{1u}, std::size_t{2u}));
+    ASSERT_EQ(++it, iterable.end());
+}
+
+TEST(AdjacencyMatrix, EdgesByVertex) {
+    entt::adjacency_matrix adjacency_matrix{3u};
+    auto iterable = adjacency_matrix.edges();
+
+    ASSERT_EQ(iterable.begin(), iterable.end());
+
+    adjacency_matrix.insert(0u, 1u);
+    adjacency_matrix.insert(1u, 2u);
+    iterable = adjacency_matrix.edges(0u);
+
+    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;
+
+    entt::basic_adjacency_matrix<allocator> adjacency_matrix{2u};
+    adjacency_matrix.insert(0u, 1u);
+
+    allocator::trigger_on_allocate = true;
+
+    ASSERT_EQ(adjacency_matrix.size(), 2u);
+    ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
+
+    ASSERT_THROW(adjacency_matrix.resize(4u), exception);
+
+    ASSERT_EQ(adjacency_matrix.size(), 2u);
+    ASSERT_TRUE(adjacency_matrix.contains(0u, 1u));
+}