Antkeeper  0.0.1
hyperoctree.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef ANTKEEPER_GEOM_HYPEROCTREE_HPP
21 #define ANTKEEPER_GEOM_HYPEROCTREE_HPP
22 
23 #include <engine/math/compile.hpp>
24 #include <algorithm>
25 #include <array>
26 #include <bit>
27 #include <concepts>
28 #include <cstddef>
29 #include <set>
30 #include <type_traits>
31 #include <unordered_set>
32 
33 namespace geom {
34 
37 {
39  unordered,
40 
42  dfs_pre,
43 
45  bfs
46 };
47 
49 template <std::unsigned_integral T>
50 using hyperoctree_dfs_pre_compare = std::less<T>;
51 
53 template <std::unsigned_integral T, std::size_t DepthBits>
54 struct hyperoctree_bfs_compare
55 {
56  constexpr bool operator()(const T& lhs, const T& rhs) const noexcept
57  {
58  return std::rotr(lhs, DepthBits) < std::rotr(rhs, DepthBits);
59  }
60 };
61 
63 template <hyperoctree_order Order, std::unsigned_integral T, std::size_t DepthBits>
64 struct hyperoctree_container {};
65 
67 template <std::unsigned_integral T, std::size_t DepthBits>
68 struct hyperoctree_container<hyperoctree_order::unordered, T, DepthBits>
69 {
70  typedef std::unordered_set<T> type;
71 };
72 
74 template <std::unsigned_integral T, std::size_t DepthBits>
75 struct hyperoctree_container<hyperoctree_order::dfs_pre, T, DepthBits>
76 {
77  typedef std::set<T, hyperoctree_dfs_pre_compare<T>> type;
78 };
79 
81 template <std::unsigned_integral T, std::size_t DepthBits>
82 struct hyperoctree_container<hyperoctree_order::bfs, T, DepthBits>
83 {
84  typedef std::set<T, hyperoctree_bfs_compare<T, DepthBits>> type;
85 };
86 
97 template <std::unsigned_integral T, std::size_t N, hyperoctree_order Order = hyperoctree_order::dfs_pre>
99 {
100 private:
128  static consteval std::size_t find_max_depth() noexcept
129  {
130  std::size_t max_depth = 0;
131  for (std::size_t i = 1; i <= sizeof(T) * 8; ++i)
132  {
133  const std::size_t location_bits = sizeof(T) * 8 - i;
134  max_depth = location_bits / N - 1;
135  const std::size_t depth_bits = static_cast<std::size_t>(std::bit_width(max_depth));
136 
137  if (depth_bits + location_bits < sizeof(T) * 8)
138  break;
139  }
140  return static_cast<T>(max_depth);
141  }
142 
143 public:
145  typedef T node_type;
146 
148  static constexpr std::size_t dimensions = N;
149 
151  static constexpr hyperoctree_order order = Order;
152 
154  static constexpr node_type max_depth = find_max_depth();
155 
157  static constexpr node_type node_bits = sizeof(node_type) * 8;
158 
160  static constexpr node_type depth_bits = std::bit_width(max_depth);
161 
163  static constexpr node_type location_bits = (max_depth + 1) * N;
164 
167 
169  static constexpr node_type children_per_node = math::compile::exp2<node_type>(N);
170 
173 
175  static constexpr node_type resolution = math::compile::exp2<node_type>(max_depth);
176 
178  static constexpr std::size_t max_node_count = (math::compile::pow<std::size_t>(resolution * 2, N) - 1) / siblings_per_node;
179 
181  static constexpr node_type root = 0;
182 
184  typedef typename hyperoctree_container<order, node_type, depth_bits>::type container_type;
185 
187  typedef typename container_type::iterator iterator;
188 
190  typedef typename container_type::const_iterator const_iterator;
191 
193  typedef std::conditional<order != hyperoctree_order::unordered, std::reverse_iterator<iterator>, iterator>::type reverse_iterator;
194 
196  typedef std::conditional<order != hyperoctree_order::unordered, std::reverse_iterator<const_iterator>, const_iterator>::type const_reverse_iterator;
197 
200 
207  static inline constexpr node_type depth(node_type node) noexcept
208  {
209  constexpr node_type mask = math::compile::exp2<node_type>(depth_bits) - 1;
210  return node & mask;
211  }
212 
220  static inline constexpr node_type location(node_type node) noexcept
221  {
222  return node >> ((node_bits - 1) - depth(node) * N);
223  }
224 
232  static constexpr std::array<node_type, 2> split(node_type node) noexcept
233  {
235  const node_type location = node >> ((node_bits - 1) - depth * N);
236  return {depth, location};
237  }
238 
249  static inline constexpr node_type node(node_type depth, node_type location) noexcept
250  {
251  return (location << ((node_bits - 1) - depth * N)) | depth;
252  }
253 
264  static inline constexpr node_type ancestor(node_type node, node_type depth) noexcept
265  {
266  const node_type mask = (~node_type(0)) << ((node_bits - 1) - depth * N);
267  return (node & mask) | depth;
268  }
269 
277  static inline constexpr node_type parent(node_type node) noexcept
278  {
279  return ancestor(node, depth(node) - 1);
280  }
281 
290  static constexpr node_type sibling(node_type node, node_type n) noexcept
291  {
292  constexpr node_type mask = (1 << N) - 1;
293  const auto [depth, location] = split(node);
294  const node_type sibling_location = (location & (~mask)) | ((location + n) & mask);
295  return hyperoctree::node(depth, sibling_location);
296  }
297 
306  static inline constexpr node_type child(node_type node, T n) noexcept
307  {
308  return sibling(node + 1, n);
309  }
310 
319  static constexpr node_type common_ancestor(node_type a, node_type b) noexcept
320  {
321  const node_type bits = std::min<node_type>(depth(a), depth(b)) * N;
322  const node_type marker = (node_type(1) << (node_bits - 1)) >> bits;
323  const node_type depth = node_type(std::countl_zero((a ^ b) | marker) / N);
324  return ancestor(a, depth);
325  }
327 
330  nodes({root})
331  {}
332 
335 
341  inline iterator begin() noexcept
342  {
343  return nodes.begin();
344  }
345  inline const_iterator begin() const noexcept
346  {
347  return nodes.begin();
348  }
349  inline const_iterator cbegin() const noexcept
350  {
351  return nodes.cbegin();
352  }
354 
361  inline iterator end() noexcept
362  {
363  return nodes.end();
364  }
365  inline const_iterator end() const noexcept
366  {
367  return nodes.end();
368  }
369  inline const_iterator cend() const noexcept
370  {
371  return nodes.cend();
372  }
374 
382  inline reverse_iterator rbegin() noexcept
383  {
384  if constexpr (order != hyperoctree_order::unordered)
385  return nodes.rbegin();
386  else
387  return nodes.begin();
388  }
389  inline const_reverse_iterator rbegin() const noexcept
390  {
391  if constexpr (order != hyperoctree_order::unordered)
392  return nodes.rbegin();
393  else
394  return nodes.begin();
395  }
396  inline const_reverse_iterator crbegin() const noexcept
397  {
398  if constexpr (order != hyperoctree_order::unordered)
399  return nodes.crbegin();
400  else
401  return nodes.cbegin();
402  }
404 
412  inline reverse_iterator rend() noexcept
413  {
414  if constexpr (order != hyperoctree_order::unordered)
415  return nodes.rend();
416  else
417  return nodes.end();
418  }
419  inline const_reverse_iterator rend() const noexcept
420  {
421  if constexpr (order != hyperoctree_order::unordered)
422  return nodes.rend();
423  else
424  return nodes.end();
425  }
426  inline const_reverse_iterator crend() const noexcept
427  {
428  if constexpr (order != hyperoctree_order::unordered)
429  return nodes.crend();
430  else
431  return nodes.cend();
432  }
435 
438 
445  inline bool empty() const noexcept
446  {
447  return nodes.empty();
448  }
449 
455  inline bool full() const noexcept
456  {
457  return size() == max_size();
458  }
459 
467  inline std::size_t size() const noexcept
468  {
469  return nodes.size();
470  }
471 
473  constexpr std::size_t max_size() const noexcept
474  {
475  return max_node_count;
476  }
478 
481 
484  inline void clear()
485  {
486  nodes = {root};
487  }
488 
497  {
498  if (contains(node))
499  return;
500 
501  // Insert node
502  nodes.emplace(node);
503 
504  // Insert node siblings
505  for (node_type i = 1; i < children_per_node; ++i)
506  nodes.emplace(sibling(node, i));
507 
508  // Insert node ancestors
509  insert(parent(node));
510  }
511 
520  {
521  if (node == root || !contains(node))
522  return;
523 
524  // Erase node and its descendants
525  nodes.erase(node);
526  erase(child(node, 0));
527 
528  // Erase node siblings
529  for (node_type i = 0; i < siblings_per_node; ++i)
530  {
531  node = sibling(node, 1);
532 
533  // Erase sibling and its descendants
534  nodes.erase(node);
535  erase(child(node, 0));
536  }
537  }
539 
542 
549  inline bool contains(node_type node) const
550  {
551  return nodes.contains(node);
552  }
553 
561  inline bool is_leaf(node_type node) const
562  {
563  return !contains(child(node, 0));
564  }
566 
567 private:
568  container_type nodes;
569 };
570 
572 template <std::unsigned_integral T, std::size_t N>
574 
575 } // namespace geom
576 
577 #endif // ANTKEEPER_GEOM_HYPEROCTREE_HPP
Hashed linear hyperoctree.
Definition: hyperoctree.hpp:99
hyperoctree()
Constructs a hyperoctree with a single root node.
reverse_iterator rbegin() noexcept
Returns a reverse iterator to the first node of the revered hyperoctree, in the traversal order speci...
bool contains(node_type node) const
Checks if a node is contained within the hyperoctree.
static constexpr node_type resolution
Resolution in each dimension.
const_iterator begin() const noexcept
Returns an iterator to the first node, in the traversal order specified by hyperoctree::order.
const_reverse_iterator crend() const noexcept
Returns a reverse iterator to the node following the last node of the reverse hyperoctree,...
static constexpr std::size_t dimensions
Number of dimensions.
bool is_leaf(node_type node) const
Checks if a node has no children.
static constexpr node_type depth(node_type node) noexcept
Extracts the depth of a node from its identifier.
static constexpr node_type root
Node identifier of the persistent root node.
static constexpr node_type parent(node_type node) noexcept
Constructs an identifier for the parent of a node.
static constexpr std::size_t max_node_count
Number of nodes in a full hyperoctree.
reverse_iterator rend() noexcept
Returns a reverse iterator to the node following the last node of the reverse hyperoctree,...
static constexpr node_type depth_bits
Number of bits required to encode the depth of a node.
const_iterator cend() const noexcept
Returns an iterator to the node following the last node, in the traversal order specified by hyperoct...
static constexpr node_type siblings_per_node
Number of siblings per node.
bool full() const noexcept
Checks if the hyperoctree is full.
iterator begin() noexcept
Returns an iterator to the first node, in the traversal order specified by hyperoctree::order.
iterator end() noexcept
Returns an iterator to the node following the last node, in the traversal order specified by hyperoct...
const_reverse_iterator rbegin() const noexcept
Returns a reverse iterator to the first node of the revered hyperoctree, in the traversal order speci...
T node_type
Node identifier type.
static constexpr node_type node_bits
Number of bits in the node type.
constexpr std::size_t max_size() const noexcept
Returns the total number of nodes the hyperoctree is capable of containing.
static constexpr node_type child(node_type node, T n) noexcept
Constructs an identifier for the nth child of a node.
static constexpr std::array< node_type, 2 > split(node_type node) noexcept
Extracts the depth and Morton location code of a node from its identifier.
static constexpr node_type ancestor(node_type node, node_type depth) noexcept
Constructs an identifier for the ancestor of a node at a given depth.
static constexpr node_type max_depth
Maximum node depth level.
std::size_t size() const noexcept
Returns the number of nodes in the hyperoctree.
const_iterator end() const noexcept
Returns an iterator to the node following the last node, in the traversal order specified by hyperoct...
static constexpr node_type common_ancestor(node_type a, node_type b) noexcept
Constructs an identifier for the first common ancestor of two nodes.
const_reverse_iterator crbegin() const noexcept
Returns a reverse iterator to the first node of the revered hyperoctree, in the traversal order speci...
static constexpr node_type children_per_node
Number of children per node.
std::conditional< order !=hyperoctree_order::unordered, std::reverse_iterator< const_iterator >, const_iterator >::type const_reverse_iterator
Constant reverse iterator type.
static constexpr node_type sibling(node_type node, node_type n) noexcept
Constructs an identifier for the nth sibling of a node.
static constexpr node_type node(node_type depth, node_type location) noexcept
Constructs an identifier for a node at the given depth and location.
hyperoctree_container< order, node_type, depth_bits >::type container_type
Node container type.
void erase(node_type node)
Erases a node, along with its descendants, siblings, and descendants of siblings.
const_reverse_iterator rend() const noexcept
Returns a reverse iterator to the node following the last node of the reverse hyperoctree,...
bool empty() const noexcept
Checks if the hyperoctree has no nodes.
container_type::iterator iterator
Iterator type.
void insert(node_type node)
Inserts a node and its siblings into the hyperoctree, inserting ancestors as necessary.
const_iterator cbegin() const noexcept
Returns an iterator to the first node, in the traversal order specified by hyperoctree::order.
container_type::const_iterator const_iterator
Constant iterator type.
std::conditional< order !=hyperoctree_order::unordered, std::reverse_iterator< iterator >, iterator >::type reverse_iterator
Reverse iterator type.
static constexpr hyperoctree_order order
Node storage and traversal order.
static constexpr node_type divider_bits
Number of bits separating the depth and Morton location code in a node identifier.
static constexpr node_type location(node_type node) noexcept
Extracts the Morton location code of a node from its identifier.
void clear()
Erases all nodes except the root node, which is persistent.
static constexpr node_type location_bits
Number of bits required to encode the Morton location code of a node.
Geometric algorithms.
@ a
Vertex A region.
@ b
Vertex B region.
hyperoctree_order
Orders in which hyperoctree nodes can be stored and traversed.
Definition: hyperoctree.hpp:37
@ unordered
Hyperoctree nodes are unordered, potentially resulting in faster insertions through the internal use ...
@ bfs
Hyperoctree nodes are stored and traversed in breadth-first order.
@ dfs_pre
Hyperoctree nodes are stored and traversed in depth-first preorder.
Text and typography.
Definition: bitmap-font.cpp:24