adjacency_matrix.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. #ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP
  2. #define ENTT_GRAPH_ADJACENCY_MATRIX_HPP
  3. #include <cstddef>
  4. #include <iterator>
  5. #include <memory>
  6. #include <type_traits>
  7. #include <utility>
  8. #include <vector>
  9. #include "../config/config.h"
  10. #include "../core/iterator.hpp"
  11. #include "fwd.hpp"
  12. namespace entt {
  13. /**
  14. * @cond TURN_OFF_DOXYGEN
  15. * Internal details not to be documented.
  16. */
  17. namespace internal {
  18. template<typename It>
  19. class edge_iterator {
  20. using size_type = std::size_t;
  21. public:
  22. using value_type = std::pair<size_type, size_type>;
  23. using pointer = input_iterator_pointer<value_type>;
  24. using reference = value_type;
  25. using difference_type = std::ptrdiff_t;
  26. using iterator_category = std::input_iterator_tag;
  27. using iterator_concept = std::forward_iterator_tag;
  28. constexpr edge_iterator() noexcept
  29. : it{},
  30. vert{},
  31. pos{},
  32. last{},
  33. offset{} {}
  34. constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept
  35. : it{std::move(base)},
  36. vert{vertices},
  37. pos{from},
  38. last{to},
  39. offset{step} {
  40. for(; pos != last && !it[pos]; pos += offset) {}
  41. }
  42. constexpr edge_iterator &operator++() noexcept {
  43. for(pos += offset; pos != last && !it[pos]; pos += offset) {}
  44. return *this;
  45. }
  46. constexpr edge_iterator operator++(int) noexcept {
  47. edge_iterator orig = *this;
  48. return ++(*this), orig;
  49. }
  50. [[nodiscard]] constexpr reference operator*() const noexcept {
  51. return *operator->();
  52. }
  53. [[nodiscard]] constexpr pointer operator->() const noexcept {
  54. return std::make_pair<size_type>(pos / vert, pos % vert);
  55. }
  56. template<typename Type>
  57. friend constexpr bool operator==(const edge_iterator<Type> &, const edge_iterator<Type> &) noexcept;
  58. private:
  59. It it;
  60. size_type vert;
  61. size_type pos;
  62. size_type last;
  63. size_type offset{};
  64. };
  65. template<typename Container>
  66. [[nodiscard]] inline constexpr bool operator==(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
  67. return lhs.pos == rhs.pos;
  68. }
  69. template<typename Container>
  70. [[nodiscard]] inline constexpr bool operator!=(const edge_iterator<Container> &lhs, const edge_iterator<Container> &rhs) noexcept {
  71. return !(lhs == rhs);
  72. }
  73. } // namespace internal
  74. /**
  75. * Internal details not to be documented.
  76. * @endcond
  77. */
  78. /**
  79. * @brief Basic implementation of a directed adjacency matrix.
  80. * @tparam Category Either a directed or undirected category tag.
  81. * @tparam Allocator Type of allocator used to manage memory and elements.
  82. */
  83. template<typename Category, typename Allocator>
  84. class adjacency_matrix {
  85. using alloc_traits = std::allocator_traits<Allocator>;
  86. static_assert(std::is_base_of_v<directed_tag, Category>, "Invalid graph category");
  87. static_assert(std::is_same_v<typename alloc_traits::value_type, std::size_t>, "Invalid value type");
  88. using container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
  89. public:
  90. /*! @brief Allocator type. */
  91. using allocator_type = Allocator;
  92. /*! @brief Unsigned integer type. */
  93. using size_type = std::size_t;
  94. /*! @brief Vertex type. */
  95. using vertex_type = size_type;
  96. /*! @brief Edge type. */
  97. using edge_type = std::pair<vertex_type, vertex_type>;
  98. /*! @brief Vertex iterator type. */
  99. using vertex_iterator = iota_iterator<vertex_type>;
  100. /*! @brief Edge iterator type. */
  101. using edge_iterator = internal::edge_iterator<typename container_type::const_iterator>;
  102. /*! @brief Out edge iterator type. */
  103. using out_edge_iterator = edge_iterator;
  104. /*! @brief In edge iterator type. */
  105. using in_edge_iterator = edge_iterator;
  106. /*! @brief Graph category tag. */
  107. using graph_category = Category;
  108. /*! @brief Default constructor. */
  109. adjacency_matrix() noexcept(noexcept(allocator_type{}))
  110. : adjacency_matrix{0u} {}
  111. /**
  112. * @brief Constructs an empty container with a given allocator.
  113. * @param allocator The allocator to use.
  114. */
  115. explicit adjacency_matrix(const allocator_type &allocator) noexcept
  116. : adjacency_matrix{0u, allocator} {}
  117. /**
  118. * @brief Constructs an empty container with a given allocator and user
  119. * supplied number of vertices.
  120. * @param vertices Number of vertices.
  121. * @param allocator The allocator to use.
  122. */
  123. adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{})
  124. : matrix{vertices * vertices, allocator},
  125. vert{vertices} {}
  126. /**
  127. * @brief Copy constructor.
  128. * @param other The instance to copy from.
  129. */
  130. adjacency_matrix(const adjacency_matrix &other)
  131. : adjacency_matrix{other, other.get_allocator()} {}
  132. /**
  133. * @brief Allocator-extended copy constructor.
  134. * @param other The instance to copy from.
  135. * @param allocator The allocator to use.
  136. */
  137. adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator)
  138. : matrix{other.matrix, allocator},
  139. vert{other.vert} {}
  140. /**
  141. * @brief Move constructor.
  142. * @param other The instance to move from.
  143. */
  144. adjacency_matrix(adjacency_matrix &&other) noexcept
  145. : adjacency_matrix{std::move(other), other.get_allocator()} {}
  146. /**
  147. * @brief Allocator-extended move constructor.
  148. * @param other The instance to move from.
  149. * @param allocator The allocator to use.
  150. */
  151. adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator)
  152. : matrix{std::move(other.matrix), allocator},
  153. vert{std::exchange(other.vert, 0u)} {}
  154. /**
  155. * @brief Default copy assignment operator.
  156. * @param other The instance to copy from.
  157. * @return This container.
  158. */
  159. adjacency_matrix &operator=(const adjacency_matrix &other) {
  160. matrix = other.matrix;
  161. vert = other.vert;
  162. return *this;
  163. }
  164. /**
  165. * @brief Default move assignment operator.
  166. * @param other The instance to move from.
  167. * @return This container.
  168. */
  169. adjacency_matrix &operator=(adjacency_matrix &&other) noexcept {
  170. matrix = std::move(other.matrix);
  171. vert = std::exchange(other.vert, 0u);
  172. return *this;
  173. }
  174. /**
  175. * @brief Returns the associated allocator.
  176. * @return The associated allocator.
  177. */
  178. [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
  179. return matrix.get_allocator();
  180. }
  181. /*! @brief Clears the adjacency matrix. */
  182. void clear() noexcept {
  183. matrix.clear();
  184. vert = {};
  185. }
  186. /**
  187. * @brief Exchanges the contents with those of a given adjacency matrix.
  188. * @param other Adjacency matrix to exchange the content with.
  189. */
  190. void swap(adjacency_matrix &other) {
  191. using std::swap;
  192. swap(matrix, other.matrix);
  193. swap(vert, other.vert);
  194. }
  195. /**
  196. * @brief Returns the number of vertices.
  197. * @return The number of vertices.
  198. */
  199. [[nodiscard]] size_type size() const noexcept {
  200. return vert;
  201. }
  202. /**
  203. * @brief Returns an iterable object to visit all vertices of a matrix.
  204. * @return An iterable object to visit all vertices of a matrix.
  205. */
  206. [[nodiscard]] iterable_adaptor<vertex_iterator> vertices() const noexcept {
  207. return {0u, vert};
  208. }
  209. /**
  210. * @brief Returns an iterable object to visit all edges of a matrix.
  211. * @return An iterable object to visit all edges of a matrix.
  212. */
  213. [[nodiscard]] iterable_adaptor<edge_iterator> edges() const noexcept {
  214. const auto it = matrix.cbegin();
  215. const auto sz = matrix.size();
  216. return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}};
  217. }
  218. /**
  219. * @brief Returns an iterable object to visit all out edges of a vertex.
  220. * @param vertex The vertex of which to return all out edges.
  221. * @return An iterable object to visit all out edges of a vertex.
  222. */
  223. [[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept {
  224. const auto it = matrix.cbegin();
  225. const auto from = vertex * vert;
  226. const auto to = from + vert;
  227. return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}};
  228. }
  229. /**
  230. * @brief Returns an iterable object to visit all in edges of a vertex.
  231. * @param vertex The vertex of which to return all in edges.
  232. * @return An iterable object to visit all in edges of a vertex.
  233. */
  234. [[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept {
  235. const auto it = matrix.cbegin();
  236. const auto from = vertex;
  237. const auto to = vert * vert + from;
  238. return {{it, vert, from, to, vert}, {it, vert, to, to, vert}};
  239. }
  240. /**
  241. * @brief Resizes an adjacency matrix.
  242. * @param vertices The new number of vertices.
  243. */
  244. void resize(const size_type vertices) {
  245. adjacency_matrix other{vertices, get_allocator()};
  246. for(auto [lhs, rhs]: edges()) {
  247. other.insert(lhs, rhs);
  248. }
  249. other.swap(*this);
  250. }
  251. /**
  252. * @brief Inserts an edge into the adjacency matrix, if it does not exist.
  253. * @param lhs The left hand vertex of the edge.
  254. * @param rhs The right hand vertex of the edge.
  255. * @return A pair consisting of an iterator to the inserted element (or to
  256. * the element that prevented the insertion) and a bool denoting whether the
  257. * insertion took place.
  258. */
  259. std::pair<edge_iterator, bool> insert(const vertex_type lhs, const vertex_type rhs) {
  260. const auto pos = lhs * vert + rhs;
  261. if constexpr(std::is_same_v<graph_category, undirected_tag>) {
  262. const auto rev = rhs * vert + lhs;
  263. ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong");
  264. matrix[rev] = 1u;
  265. }
  266. const auto inserted = !std::exchange(matrix[pos], 1u);
  267. return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted};
  268. }
  269. /**
  270. * @brief Removes the edge associated with a pair of given vertices.
  271. * @param lhs The left hand vertex of the edge.
  272. * @param rhs The right hand vertex of the edge.
  273. * @return Number of elements removed (either 0 or 1).
  274. */
  275. size_type erase(const vertex_type lhs, const vertex_type rhs) {
  276. const auto pos = lhs * vert + rhs;
  277. if constexpr(std::is_same_v<graph_category, undirected_tag>) {
  278. const auto rev = rhs * vert + lhs;
  279. ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong");
  280. matrix[rev] = 0u;
  281. }
  282. return std::exchange(matrix[pos], 0u);
  283. }
  284. /**
  285. * @brief Checks if an adjacency matrix contains a given edge.
  286. * @param lhs The left hand vertex of the edge.
  287. * @param rhs The right hand vertex of the edge.
  288. * @return True if there is such an edge, false otherwise.
  289. */
  290. [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const {
  291. const auto pos = lhs * vert + rhs;
  292. return pos < matrix.size() && matrix[pos];
  293. }
  294. private:
  295. container_type matrix;
  296. size_type vert;
  297. };
  298. } // namespace entt
  299. #endif