fennec
Loading...
Searching...
No Matches
rdtree.h
Go to the documentation of this file.
1// =====================================================================================================================
2// fennec, a free and open source game engine
3// Copyright © 2025 Medusa Slockbower
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17// =====================================================================================================================
18
30
31#ifndef FENNEC_CONTAINERS_RDTREE_H
32#define FENNEC_CONTAINERS_RDTREE_H
33
38
40
41namespace fennec
42{
43
48template<typename TypeT, typename AllocT = allocator<TypeT>>
49struct rdtree {
50
51// Definitions =========================================================================================================
52protected:
53 struct node;
54
55public:
56 using value_t = TypeT;
57 using alloc_t = typename allocator_traits<AllocT>::template rebind<node>;
58 static constexpr size_t root = 0;
59 static constexpr size_t npos = -1;
60
61protected:
62 struct node {
63 value_t value;
64 size_t parent, child, prev, next;
65 size_t depth, num_children;
66
67 constexpr node()
68 : value(nullopt)
69 , parent(npos), child(npos)
70 , prev(npos), next(npos)
71 , depth(0), num_children(0) {
72 }
73
74 template<typename...ArgsT>
75 constexpr node(size_t p, size_t c, size_t v, size_t n, size_t d, ArgsT&&...args)
76 : value(fennec::forward<ArgsT>(args)...)
77 , parent(p), child(c), prev(v), next(n)
78 , depth(d), num_children(0) {
79 }
80
81 constexpr ~node() {
82 parent = npos;
83 child = npos;
84 prev = npos;
85 next = npos;
86 depth = 0;
87 num_children = 0;
88 }
89 };
90
91public:
92
93// Constructors ========================================================================================================
94
97
102 template<typename...ArgsT>
103 explicit constexpr rdtree(ArgsT&&...args)
104 : _table(), _freed(), _size(1) {
105 _table.creallocate(8);
106 fennec::construct(&_table[0], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
107 }
108
112 constexpr rdtree(const rdtree& tree)
113 : _table(tree._table), _freed(tree._freed), _size(tree._size) {
114 }
115
119 constexpr rdtree(rdtree&& tree) noexcept
120 : _table(fennec::move(tree._table)), _freed(fennec::move(tree._freed)), _size(tree._size) {
121 }
122
124
125
126// Assignment ==========================================================================================================
127
130
135 constexpr rdtree& operator=(const rdtree& rhs) {
136 for (value_t* it : this->_table) {
137 fennec::destruct(it);
138 }
139 _table = rhs._table;
140 _freed = rhs._freed;
141 _size = rhs._size;
142 return *this;
143 }
144
149 constexpr rdtree& operator=(rdtree&& rhs) noexcept {
150 for (value_t* it : _table) {
151 fennec::destruct(it);
152 }
153 _table = fennec::move(rhs._table);
154 _freed = fennec::move(rhs._freed);
155 _size = rhs._size;
156 return *this;
157 }
158
160
161// Properties ==========================================================================================================
162
165
168 constexpr size_t size() const {
169 return _size;
170 }
171
174 constexpr size_t capacity() const {
175 return _table.capacity();
176 }
177
180 constexpr bool empty() const {
181 return _size == 0;
182 }
183
184
185// Access ==============================================================================================================
186
190 constexpr size_t parent(size_t i) const {
191 if (i >= _table.capacity()) return npos;
192 return i == npos ? npos : _table[i].parent;
193 }
194
198 constexpr size_t child(size_t i, size_t n = 0) const {
199 if (i >= _table.capacity() && n != npos) return npos;
200 size_t c = i == npos ? npos : _table[i].child;
201 if (n != 0)
202 return next(c, n == npos ? npos : n - 1);
203 return c;
204 }
205
209 constexpr size_t next(size_t i, size_t n = 0) const {
210 if (i >= _table.capacity() && n != npos) return npos;
211 if (i == npos) {
212 return npos;
213 }
214
215 size_t org = i;
216 size_t nxt = _table[i].next;
217 while (nxt != npos) {
218 i = nxt;
219 nxt = _table[i].next;
220 if (n != npos) {
221 if (n-- == 0) {
222 break;
223 }
224 }
225 }
226
227 return i == org && n != npos ? npos : i;
228 }
229
233 constexpr size_t prev(size_t i, size_t n = 0) const {
234 if (i >= _table.capacity()) return npos;
235 if (i == npos) {
236 return npos;
237 }
238
239 size_t org = i;
240 size_t prv = _table[i].prev;
241 while (prv != npos) {
242 i = prv;
243 prv = _table[i].prev;
244 if (n != npos) {
245 if (n-- == 0) {
246 break;
247 }
248 }
249 }
250
251 return i == org && n != npos ? npos : i;
252 }
253
257 constexpr size_t left_most(size_t i) const {
258 if (i >= _table.capacity()) return npos;
259 size_t n = i;
260 if ((n = child(n)) == npos) {
261 return i;
262 }
263 while (true) {
264 size_t p = n;
265 if ((n = child(n)) == npos) {
266 return p;
267 }
268 }
269 }
270
274 constexpr size_t right_most(size_t i) const {
275 if (i >= _table.capacity()) return npos;
276 if ((i = child(i)) == npos) {
277 return npos;
278 }
279 while (true) {
280 size_t n;
281 while ((n = next(i)) != npos) {
282 i = n;
283 }
284 n = i;
285 if ((i = child(i)) == npos) {
286 return n;
287 }
288 }
289 }
290
294 constexpr size_t depth(size_t i) const {
295 if (i >= _table.capacity()) return npos;
296 return i == npos ? npos : _table[i].depth;
297 }
298
302 constexpr size_t num_children(size_t i) const {
303 if (i >= _table.capacity()) return 0;
304 return i == npos ? 0 : _table[i].num_children;
305 }
306
309 constexpr size_t next_id() const {
310 size_t i = _size;
311 if (not _freed.empty()) {
312 i = _freed.front();
313 }
314 return i;
315 }
316
320 constexpr value_t& operator[](size_t i) {
321 return _table[i].value;
322 }
323
327 constexpr const value_t& operator[](size_t i) const {
328 return _table[i].value;
329 }
330
331
332// Insertion & Deletion ================================================================================================
333
340 constexpr size_t insert(size_t parent, size_t next, const value_t& val) {
341 return this->_insert(parent, next, val);
342 }
343
350 constexpr size_t insert(size_t parent, size_t next, value_t&& val) {
351 return this->_insert(parent, next, fennec::forward<value_t>(val));
352 }
353
354 constexpr size_t insert(size_t parent, size_t next, const rdtree& tree) {
356 visit.push_front({ root, parent });
357 size_t res = npos;
358
359 while (not visit.empty()) {
360 auto node = visit.front();
361 visit.pop_front();
362
363 size_t p = this->_insert(node.second, node.second == parent ? next : npos, tree[node.first]);
364
365 res = (res == npos) ? p : res;
366
367 size_t c = tree.child(node.first, npos);
368 while (c != npos) {
369 visit.push_front({ c, p });
370 c = tree._table[c].prev;
371 }
372 }
373
374 return res;
375 }
376
383 template<typename...ArgsT>
384 constexpr size_t emplace(size_t parent, size_t next, ArgsT&&...args) {
385 return this->_insert(parent, next, fennec::forward<ArgsT>(args)...);
386 }
387
392 constexpr void swap(size_t i0, size_t i1) {
393 assertf(i0 != root and i1 != root, "Cannot Swap With Root");
394
395 size_t p0 = parent(i0);
396 size_t p1 = parent(i1);
397
398 fennec::swap(_table[i0].parent, _table[i1].parent);
399 fennec::swap(_table[i0].child, _table[i1].child);
400 fennec::swap(_table[i0].next, _table[i1].next);
401 fennec::swap(_table[i0].prev, _table[i1].prev);
402 fennec::swap(_table[i0].depth, _table[i1].depth);
403 fennec::swap(_table[i0].num_children, _table[i1].num_children);
404
405 if (child(p0) == i0) _table[p0].child = i1;
406 if (child(p1) == i1) _table[p1].child = i0;
407 }
408
409
413 constexpr void erase(size_t i) {
414 _erase(i);
415 }
416
417
418
419
420// Traversal ===========================================================================================================
421
433 template<typename OrderT, typename VisitorT>
434 constexpr void traverse(VisitorT&& visit, size_t i = root) {
435 OrderT order;
436 i = order(*this, i);
437 while (i != npos) {
438 uint8_t mode = traversal_control_continue;
439 if (_table[i].value) {
440 mode = visit(_table[i].value, i);
441 }
442 if (mode == traversal_control_break) {
443 break;
444 }
445 i = order[*this, i, mode];
446 }
447 }
448
449 struct breadth_first {
450 list<size_t> visit;
451 size_t head;
452
453 constexpr size_t operator()(const rdtree&, size_t start) {
454 head = start;
455 return start;
456 }
457
458 constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
459 if (node == npos) {
460 return npos;
461 }
462
463 size_t nxt = tree.next(node);
464 size_t chd = tree.next(node);
465
466 if (nxt != npos && node != head) {
467 visit.push_front(nxt);
468 }
469
470 if (chd != npos && mode != traversal_control_jump_over) {
471 visit.push_back(chd);
472 }
473
474 if (not visit.empty()) {
475 node = visit.front();
476 visit.pop_front();
477 } else {
478 node = npos;
479 }
480
481 return node;
482 }
483 };
484
485 struct pre_order {
486 list<size_t> visit;
487 size_t head;
488
489 constexpr size_t operator()(const rdtree&, size_t start) {
490 head = start;
491 return start;
492 }
493
494 constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
495 if (node == npos) {
496 return npos;
497 }
498
499 size_t nxt = tree.next(node);
500 size_t chd = tree.child(node);
501
502 if (nxt != npos && node != head) {
503 visit.push_front(nxt);
504 }
505
506 if (chd != npos && mode != traversal_control_jump_over) {
507 visit.push_front(chd);
508 }
509
510 if (not visit.empty()) {
511 node = visit.front();
512 visit.pop_front();
513 } else {
514 node = npos;
515 }
516
517 return node;
518 }
519 };
520
521 struct in_order {
522 list<size_t> visit;
523 size_t head;
524
525 constexpr size_t operator()(const rdtree& tree, size_t start) {
526 head = start;
527 return tree.left_most(start);
528 }
529
530 constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) {
531 if (node == npos) {
532 return npos;
533 }
534
535 size_t prnt = tree.parent(node);
536 size_t next = tree.next(node);
537 if (node != head) {
538 if (tree.child(prnt) == node) {
539 visit.push_back(prnt);
540 if (next != npos) {
541 visit.push_back(tree.left_most(next));
542 }
543 } else if (next != npos) {
544 visit.push_front(tree.left_most(next));
545 }
546 }
547
548 if (not visit.empty()) {
549 node = visit.front();
550 visit.pop_front();
551 } else {
552 node = npos;
553 }
554
555 return node;
556 }
557 };
558
559 struct post_order {
560 list<size_t> visit;
561 size_t head;
562
563 constexpr size_t operator()(const rdtree& tree, size_t start) {
564 head = start;
565 return tree.left_most(start);
566 }
567
568 constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) {
569 if (node == npos) {
570 return npos;
571 }
572
573 size_t prnt = tree.parent(node);
574 size_t next = tree.next(node);
575
576 if (node != head) {
577 if (next != npos) {
578 visit.push_front(tree.left_most(next));
579 } else {
580 visit.push_front(prnt);
581 }
582 }
583
584 if (not visit.empty()) {
585 node = visit.front();
586 visit.pop_front();
587 } else {
588 node = npos;
589 }
590
591 return node;
592 }
593 };
594
595
596protected:
597 allocation<node, alloc_t> _table;
598 list<size_t> _freed;
599 size_t _size;
600
601 void _expand() {
602 _table.creallocate(_table.capacity() * 2);
603 }
604
605 size_t _next_free() {
606 size_t next = _size;
607 if (not _freed.empty()) {
608 next = _freed.front();
609 _freed.pop_front();
610 }
611 if (_size >= capacity()) {
612 _expand();
613 }
614 ++_size;
615 return next;
616 }
617
618 template<typename...ArgsT>
619 constexpr size_t _insert(size_t p, size_t n, ArgsT&&...args) {
620 if (_size == 0) {
621 fennec::construct(&_table[root], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
622 _size = 1;
623 return root;
624 }
625
626 if (p == npos) {
627 _table[root].value = value_t(fennec::forward<ArgsT>(args)...);
628 _size = _size == 0 ? 1 : _size;
629 return root;
630 }
631
632 size_t idx = _next_free();
633 size_t nxt = child(p, n);
634 size_t prv = n == npos ? npos : prev(n);
635
636 ++_table[p].num_children;
637 if ((nxt == child(p) && n != npos) || nxt == npos) {
638 _table[p].child = idx;
639 }
640
641 if (n == npos) {
642 if (nxt != npos) {
643 _table[nxt].next = idx;
644 }
645 fennec::construct(&_table[idx], p, npos, nxt, npos, depth(p) + 1, fennec::forward<ArgsT>(args)...);
646 } else {
647 if (nxt != npos) {
648 _table[nxt].prev = idx;
649 }
650 if (prv != npos) {
651 _table[prv].next = idx;
652 }
653 fennec::construct(&_table[idx], p, npos, prv, nxt, depth(p) + 1, fennec::forward<ArgsT>(args)...);
654 }
655 return idx;
656 }
657
658 constexpr void _erase(size_t i) {
659 list<size_t> queue;
660 queue.push_back(child(i));
661 while (not queue.empty()) {
662 size_t n = queue.front(); queue.pop_front();
663 if (n == npos) continue;
664 queue.push_back(next(n));
665 queue.push_back(child(n));
666 fennec::destruct(&_table[n]);
667 _freed.push_back(n);
668 --_size;
669 }
670
671 fennec::destruct(&_table[i]);
672 if (i != root) _freed.push_back(i);
673 --_size;
674 }
675};
676
677}
678
679#endif // FENNEC_CONTAINERS_RDTREE_H
This header contains structures and classes related to allocating blocks of memory.
A header containing the definition for a linked list of values.
A header containing the definition for a container with an optionally present variable.
A header containing the definition for a container holding a pair of values.
constexpr size_t capacity() const
Getter for the number of elements n of type T that the allocation can hold.
Definition allocator.h:617
constexpr void creallocate(size_t n, align_t align=zero< align_t >()) noexcept
Reallocate the block with a new size. Contents are copied to the new allocation.
Definition allocator.h:541
typename _rebind< Alloc, TypeT >::type rebind
Rebinds the allocator type to produce an element type of type TypeT
Definition allocator.h:134
Data Structure defining lists of elements.
Definition list.h:67
constexpr void pop_front()
Pop Front, erases first element.
Definition list.h:380
constexpr bool empty() const
Definition list.h:181
constexpr size_t push_back(const value_t &x)
Push Back Copy.
Definition list.h:342
constexpr size_t push_front(const value_t &x)
Push Front Copy.
Definition list.h:319
constexpr value_t & front()
Access Front Element.
Definition list.h:222
Rooted-Directed Tree.
Definition rdtree.h:49
constexpr const value_t & operator[](size_t i) const
Definition rdtree.h:327
constexpr size_t next(size_t i, size_t n=0) const
Definition rdtree.h:209
constexpr void swap(size_t i0, size_t i1)
Swap two nodes.
Definition rdtree.h:392
constexpr void traverse(VisitorT &&visit, size_t i=root)
Traverse the tree using a specified order and visiting functor.
Definition rdtree.h:434
constexpr bool empty() const
Definition rdtree.h:180
constexpr size_t next_id() const
Definition rdtree.h:309
constexpr size_t capacity() const
Definition rdtree.h:174
constexpr size_t parent(size_t i) const
Definition rdtree.h:190
constexpr rdtree & operator=(rdtree &&rhs) noexcept
Move Assignment Operator.
Definition rdtree.h:149
constexpr void erase(size_t i)
Erase a node in the tree and all of it's children.
Definition rdtree.h:413
constexpr size_t emplace(size_t parent, size_t next, ArgsT &&...args)
Insertion, creates a node in the tree with parent parent
Definition rdtree.h:384
constexpr rdtree(ArgsT &&...args)
Root Constructor, constructs the root node of the tree.
Definition rdtree.h:103
constexpr size_t depth(size_t i) const
Definition rdtree.h:294
constexpr size_t num_children(size_t i) const
Definition rdtree.h:302
constexpr size_t left_most(size_t i) const
Definition rdtree.h:257
constexpr size_t insert(size_t parent, size_t next, value_t &&val)
Insertion, creates a node in the tree with parent parent
Definition rdtree.h:350
constexpr rdtree & operator=(const rdtree &rhs)
Copy Assignment Operator.
Definition rdtree.h:135
constexpr rdtree(rdtree &&tree) noexcept
Move Constructor, takes ownership over the contents of tree
Definition rdtree.h:119
constexpr rdtree(const rdtree &tree)
Copy Constructor, copies the contents of tree
Definition rdtree.h:112
constexpr value_t & operator[](size_t i)
Definition rdtree.h:320
constexpr size_t right_most(size_t i) const
Definition rdtree.h:274
constexpr size_t size() const
Definition rdtree.h:168
constexpr size_t insert(size_t parent, size_t next, const value_t &val)
Insertion, creates a node in the tree with parent parent
Definition rdtree.h:340
constexpr size_t child(size_t i, size_t n=0) const
Definition rdtree.h:198
constexpr size_t prev(size_t i, size_t n=0) const
Definition rdtree.h:233
a header containing constants and utilities related to traversal
::uint8_t uint8_t
Unsigned 8-bit integer.
Definition types.h:272
constexpr void swap(T &x, T &y) noexcept
Swaps x and y.
Definition utility.h:114
constexpr remove_reference_t< T > && move(T &&x) noexcept
produces an x-value type to indicate x may be "moved"
Definition utility.h:92