| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- #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 {
- template<typename It>
- class 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;
- using iterator_concept = std::forward_iterator_tag;
- constexpr edge_iterator() noexcept
- : it{},
- vert{},
- pos{},
- last{},
- offset{} {}
- constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept
- : it{std::move(base)},
- vert{vertices},
- pos{from},
- last{to},
- offset{step} {
- for(; pos != last && !it[pos]; pos += offset) {}
- }
- constexpr edge_iterator &operator++() noexcept {
- for(pos += offset; pos != last && !it[pos]; pos += offset) {}
- 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<size_type>(pos / vert, pos % vert);
- }
- template<typename Type>
- friend constexpr bool operator==(const edge_iterator<Type> &, const edge_iterator<Type> &) noexcept;
- private:
- It it;
- size_type vert;
- size_type pos;
- size_type last;
- size_type offset{};
- };
- template<typename Container>
- [[nodiscard]] inline constexpr bool operator==(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
- return lhs.pos == rhs.pos;
- }
- template<typename Container>
- [[nodiscard]] inline constexpr bool operator!=(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
- return !(lhs == rhs);
- }
- } // namespace internal
- /**
- * Internal details not to be documented.
- * @endcond
- */
- /**
- * @brief Basic implementation of a directed adjacency matrix.
- * @tparam Category Either a directed or undirected category tag.
- * @tparam Allocator Type of allocator used to manage memory and elements.
- */
- template<typename Category, typename Allocator>
- class adjacency_matrix {
- using alloc_traits = std::allocator_traits<Allocator>;
- static_assert(std::is_base_of_v<directed_tag, Category>, "Invalid graph category");
- 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<typename container_type::const_iterator>;
- /*! @brief Out edge iterator type. */
- using out_edge_iterator = edge_iterator;
- /*! @brief In edge iterator type. */
- using in_edge_iterator = edge_iterator;
- /*! @brief Graph category tag. */
- using graph_category = Category;
- /*! @brief Default constructor. */
- adjacency_matrix() noexcept(noexcept(allocator_type{}))
- : adjacency_matrix{0u} {}
- /**
- * @brief Constructs an empty container with a given allocator.
- * @param allocator The allocator to use.
- */
- explicit adjacency_matrix(const allocator_type &allocator) noexcept
- : 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.
- */
- adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{})
- : matrix{vertices * vertices, allocator},
- vert{vertices} {}
- /**
- * @brief Copy constructor.
- * @param other The instance to copy from.
- */
- adjacency_matrix(const adjacency_matrix &other)
- : adjacency_matrix{other, other.get_allocator()} {}
- /**
- * @brief Allocator-extended copy constructor.
- * @param other The instance to copy from.
- * @param allocator The allocator to use.
- */
- adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator)
- : matrix{other.matrix, allocator},
- vert{other.vert} {}
- /**
- * @brief Move constructor.
- * @param other The instance to move from.
- */
- adjacency_matrix(adjacency_matrix &&other) noexcept
- : 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.
- */
- adjacency_matrix(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.
- */
- adjacency_matrix &operator=(const 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.
- */
- adjacency_matrix &operator=(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 {
- const auto it = matrix.cbegin();
- const auto sz = matrix.size();
- return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}};
- }
- /**
- * @brief Returns an iterable object to visit all out edges of a vertex.
- * @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_edge_iterator> out_edges(const vertex_type vertex) const noexcept {
- const auto it = matrix.cbegin();
- const auto from = vertex * vert;
- const auto to = from + vert;
- return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}};
- }
- /**
- * @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 = vert * vert + from;
- return {{it, vert, from, to, vert}, {it, vert, to, to, vert}};
- }
- /**
- * @brief Resizes an adjacency matrix.
- * @param vertices The new number of vertices.
- */
- void resize(const size_type vertices) {
- adjacency_matrix other{vertices, get_allocator()};
- for(auto [lhs, rhs]: edges()) {
- other.insert(lhs, rhs);
- }
- other.swap(*this);
- }
- /**
- * @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;
- if constexpr(std::is_same_v<graph_category, undirected_tag>) {
- const auto rev = rhs * vert + lhs;
- ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong");
- matrix[rev] = 1u;
- }
- const auto inserted = !std::exchange(matrix[pos], 1u);
- return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, 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) {
- const auto pos = lhs * vert + rhs;
- if constexpr(std::is_same_v<graph_category, undirected_tag>) {
- const auto rev = rhs * vert + lhs;
- ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong");
- matrix[rev] = 0u;
- }
- return std::exchange(matrix[pos], 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
|