fennec
Loading...
Searching...
No Matches
bintree.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_BINTREE_H
32#define FENNEC_CONTAINERS_BINTREE_H
33
39
40namespace fennec
41{
42
47template<typename TypeT, class AllocT = allocator<TypeT>>
48struct bintree {
49
50// Definitions =========================================================================================================
51protected:
52 struct node;
53
54public:
55 using value_t = TypeT;
56 using alloc_t = allocator_traits<AllocT>::template rebind<node>;
57 static constexpr size_t npos = -1;
58
59 friend class iterator;
60 friend class const_iterator;
61
62protected:
63 struct node {
64 value_t value;
65 size_t parent;
66 size_t child[2];
67
68 constexpr node()
69 : value()
70 , parent(npos), child{ npos, npos } {
71 }
72
73 template<typename...ArgsT>
74 constexpr node(size_t p, size_t l, size_t r, ArgsT&&...args)
75 : value(fennec::forward<ArgsT>(args)...)
76 , parent(p), child{ l, r } {
77 }
78
79 constexpr ~node() {
80 parent = npos;
81 child[0] = npos;
82 child[1] = npos;
83 }
84
85 size_t& operator[](bool d) {
86 return child[d];
87 }
88 };
89
92
93// Constructors & Destructor ===========================================================================================
94public:
95
98
101 constexpr bintree()
102 : _table()
103 , _freed()
104 , _root(npos)
105 , _size(0) {
106 }
107
111 constexpr bintree(bintree&& tree) noexcept
112 : _table(fennec::move(tree._table))
113 , _freed(fennec::move(tree._freed))
114 , _root(tree._root)
115 , _size(tree._size) {
116 }
117
121 constexpr bintree(const bintree& tree)
122 : _table(tree._table)
123 , _freed(tree._freed)
124 , _root(tree._root)
125 , _size(tree._size) {
126 }
127
130 constexpr ~bintree() {
131 clear();
132 }
133
135
136
137// Properties ==========================================================================================================
138public:
139
142 constexpr size_t size() const {
143 return _size;
144 }
145
148 constexpr bool empty() const {
149 return _size == 0;
150 }
151
154 constexpr size_t capacity() const {
155 return _table.capacity();
156 }
157
160 constexpr size_t next_id() const {
161 size_t i = _size;
162 if (not _freed.empty()) {
163 i = _freed.front();
164 }
165 return i;
166 }
167
170 constexpr size_t root() const {
171 return _root;
172 }
173
174
175
176// Navigation ==========================================================================================================
177public:
178
181
186 constexpr size_t parent(size_t i) const {
187 return i == npos ? npos : _table[i].parent;
188 }
189
194 constexpr size_t grandparent(size_t i) const {
195 return parent(parent(i));
196 }
197
202 constexpr size_t left(size_t i) const {
203 return i == npos ? npos : _table[i].child[false];
204 }
205
210 constexpr size_t right(size_t i) const {
211 return i == npos ? npos : _table[i].child[true];
212 }
213
219 constexpr size_t child(size_t i, bool dir) const {
220 return i == npos ? npos : _table[i].child[dir];
221 }
222
227 constexpr bool direction(size_t i) const {
228 return i == npos ? false : i == right(_parent(i));
229 }
230
235 constexpr size_t sibling(size_t i) const {
236 if (i == npos) {
237 return npos;
238 }
239 if (i == _root) {
240 return npos;
241 }
242 size_t p = _parent(i);
243 bool d = i == _right(p);
244 return _child(p, !d);
245 }
246
251 constexpr size_t depth(size_t i) const {
252 size_t d = 0;
253 while (i != npos) {
254 i = _parent(i);
255 ++d;
256 }
257 return d;
258 }
259
264 constexpr size_t left_most(size_t i) const {
265 if (i >= _table.size()) {
266 return npos;
267 }
268 while (_table[i].child[false] != npos) {
269 i = _table[i].child[false];
270 }
271 return i;
272 }
273
278 constexpr size_t right_most(size_t i) const {
279 if (i >= _table.size()) {
280 return npos;
281 }
282 while (_table[i].right[false] != npos) {
283 i = _table[i].right[false];
284 }
285 return i;
286 }
287
289
290
291// Access ==============================================================================================================
292public:
293
296
301 constexpr value_t& operator[](size_t i) {
302 assertd(i < _table.size(), "Index out of bounds.");
303 return _table[i].value;
304 }
305
310 constexpr const value_t& operator[](size_t i) const {
311 assertd(i < _table.size(), "Index out of bounds.");
312 return _table[i].value;
313 }
314
316
317// Modifiers ===========================================================================================================
318
321
328 constexpr size_t insert_left(size_t p, value_t&& val) {
329 return this->_insert_left(p, fennec::forward<value_t>(val));
330 }
331
338 constexpr size_t insert_left(size_t p, const value_t& val) {
339 return this->_insert_left(p, val);
340 }
341
348 template<typename...ArgsT>
349 constexpr size_t emplace_left(size_t p, ArgsT&&...args) {
350 return this->_insert_left(p, fennec::forward<ArgsT>(args)...);
351 }
352
359 constexpr size_t insert_right(size_t p, value_t&& val) {
360 return this->_insert_right(p, fennec::forward<value_t>(val));
361 }
362
369 constexpr size_t insert_right(size_t p, const value_t& val) {
370 return this->_insert_right(p, val);
371 }
372
379 template<typename...ArgsT>
380 constexpr size_t emplace_right(size_t p, ArgsT&&...args) {
381 return this->_insert_right(p, fennec::forward<ArgsT>(args)...);
382 }
383
384
385
390 constexpr size_t rotate(size_t sub, bool dir) {
391 if (sub == npos) {
392 return npos;
393 }
394 size_t sub_parent = _parent(sub);
395 size_t new_root = _child(sub, not dir);
396 size_t new_child = _child(new_root, dir);
397
398 _child(sub, not dir) = new_child;
399 if (new_child != npos) {
400 _parent(new_child) = sub;
401 }
402 _child(new_root, dir) = sub;
403
404 _parent(new_root) = sub_parent;
405 _parent(sub) = new_root;
406 if (sub_parent != npos) {
407 _child(sub_parent, sub == _right(sub_parent)) = new_root;
408 } else {
409 _root = new_root;
410 }
411 return new_root;
412 }
413
421 constexpr size_t insert(size_t p, bool d, value_t&& val) {
422 return this->_insert(p, d, fennec::forward<value_t>(val));
423 }
424
432 constexpr size_t insert(size_t p, bool d, const value_t& val) {
433 return this->_insert(p, d, val);
434 }
435
443 template<typename...ArgsT>
444 constexpr size_t emplace(size_t p, bool d, ArgsT&&...args) {
445 return this->_insert(p, d, fennec::forward<ArgsT>(args)...);
446 }
447
450 constexpr void clear() {
451 list<size_t> queue;
452
453 if (_root != npos) {
454 queue.push_back(_root);
455 }
456
457 while (not queue.empty()) {
458 size_t i = queue.front();
459 queue.pop_front();
460
461 if (_right(i) != npos) {
462 queue.push_front(_right(i));
463 }
464 if (_left(i) != npos) {
465 queue.push_front(_left(i));
466 }
467
468 fennec::destruct(&_table[i]);
469 }
470
471 _size = 0;
472 _root = npos;
473 }
474
476
477
478// Traversal ===========================================================================================================
479
491 template<typename OrderT, typename VisitorT>
492 constexpr void traverse(VisitorT&& visit, size_t i) {
493 OrderT order;
494 i = order(*this, i);
495 while (i != npos) {
496 uint8_t mode = traversal_control_continue;
497 if (_table[i].value) {
498 mode = visit(_table[i].value, i);
499 }
500 if (mode == traversal_control_break) {
501 break;
502 }
503 i = order[*this, i, mode];
504 }
505 }
506
507 struct breadth_first {
508 list<size_t> visit;
509 size_t head;
510
511 size_t operator()(const bintree&, size_t start) {
512 return head = start;
513 }
514
515 size_t operator[](const bintree& tree, size_t node, uint8_t mode) {
516 if (node == npos) {
517 return npos;
518 }
519
520 size_t lft = tree.left(tree.parent(node));
521 size_t nxt = lft == node ? tree.right(tree.parent(node)) : npos;
522 size_t chd = tree.left(node);
523
524 if (nxt != npos && node != head) {
525 visit.push_front(nxt);
526 }
527
528 if (chd != npos && mode != traversal_control_jump_over) {
529 visit.push_back(chd);
530 }
531
532 if (not visit.empty()) {
533 node = visit.front();
534 visit.pop_front();
535 } else {
536 node = npos;
537 }
538
539 return node;
540 }
541 };
542
543 struct pre_order {
544 list<size_t> visit;
545 size_t head;
546
547 constexpr size_t operator()(const bintree&, size_t start) {
548 head = start;
549 return start;
550 }
551
552 constexpr size_t operator[](const bintree& tree, size_t node, uint8_t mode) {
553 if (node == npos) {
554 return npos;
555 }
556
557 size_t nxt = tree.right(tree.parent(node));
558 size_t chd = tree.left(node);
559 nxt = node == nxt ? npos : nxt;
560
561 if (nxt != npos && node != head) {
562 visit.push_front(nxt);
563 }
564
565 if (chd != npos && mode != traversal_control_jump_over) {
566 visit.push_front(chd);
567 }
568
569 if (not visit.empty()) {
570 node = visit.front();
571 visit.pop_front();
572 } else {
573 node = npos;
574 }
575
576 return node;
577 }
578 };
579
580 struct in_order {
581 list<size_t> visit;
582 size_t head;
583
584 constexpr size_t operator()(const bintree& tree, size_t start) {
585 head = start;
586 return tree.left_most(start);
587 }
588
589 constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
590 if (node == npos) {
591 return npos;
592 }
593
594 size_t parent = tree.parent(node);
595 size_t pright = tree.right(parent);
596 size_t next = tree.left_most(tree.right(node));
597
598 if (node != pright && parent != npos) {
599 visit.push_front(parent);
600 }
601
602 if (next != npos) {
603 visit.push_front(next);
604 }
605
606 if (not visit.empty()) {
607 node = visit.front();
608 visit.pop_front();
609 } else {
610 node = npos;
611 }
612
613 return node;
614 }
615 };
616
617 struct post_order {
618 list<size_t> visit;
619 size_t head;
620
621 constexpr size_t successor(const bintree& tree, size_t n) {
622 size_t s = tree.left_most(n);
623 while (n == s) {
624 size_t r = tree.right(n);
625 if (r != npos) {
626 n = r;
627 s = tree.left_most(n);
628 } else {
629 break;
630 }
631 }
632 return s == npos ? n : s;
633 }
634
635 constexpr size_t operator()(const bintree& tree, size_t start) {
636 head = start;
637 return this->successor(tree, start);
638 }
639
640 constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
641 if (node == npos) {
642 return npos;
643 }
644
645 size_t parent = tree.parent(node);
646 size_t pright = tree.right(parent);
647
648 if (node == pright) {
649 if (parent != npos) {
650 visit.push_front(parent);
651 }
652 } else if (pright != npos) {
653 visit.push_front(this->successor(tree, pright));
654 }
655
656 if (not visit.empty()) {
657 node = visit.front();
658 visit.pop_front();
659 } else {
660 node = npos;
661 }
662
663
664 return node;
665 }
666 };
667
668// Iterator ============================================================================================================
669
670 class iterator {
671 protected:
672 bintree* _tree;
673 in_order _order;
674 size_t _n;
675
676 public:
677 constexpr iterator(bintree* tree, size_t root)
678 : _tree(tree)
679 , _order()
680 , _n(_order(*tree, root)) {
681 }
682
683 constexpr iterator(bintree* tree, size_t root, size_t node)
684 : _tree(tree)
685 , _order()
686 , _n(node) {
687 _order.head = root;
688 }
689
690 size_t index() const {
691 return _n;
692 }
693
694 iterator& operator++() {
695 return _n = _order[*_tree, _n, traversal_control_continue], *this;
696 }
697
698 value_t& operator*() {
699 return (*_tree)[_n];
700 }
701
702 value_t* operator->() {
703 return &(*_tree)[_n];
704 }
705
706 const value_t& operator*() const {
707 return (*_tree)[_n];
708 }
709
710 const value_t* operator->() const {
711 return &(*_tree)[_n];
712 }
713
714 constexpr bool operator==(const iterator& it) {
715 return _tree == it._tree and _n == it._n;
716 }
717
718 constexpr bool operator!=(const iterator& it) {
719 return _tree != it._tree or _n != it._n;
720 }
721
722 };
723
724// Fields ==============================================================================================================
725protected:
726 table_t _table;
727 freed_t _freed;
728 size_t _root, _size;
729
730// Helpers =============================================================================================================
731
732 constexpr size_t _next_free() {
733 size_t i = _size;
734 if (not _freed.empty()) {
735 i = _freed.front();
736 _freed.pop_front();
737 }
738 if (i >= _table.capacity()) {
739 _table.creallocate(2 * fennec::max(_table.capacity(), size_t(4)));
740 }
741 ++_size;
742 return i;
743 }
744
745 template<typename...ArgsT>
746 constexpr size_t _insert_left(size_t p, ArgsT&&...args) {
747 size_t i = p >= capacity() ? npos : p;
748 i = i == npos ? _root : _left(i);
749 if (i != npos) {
750 _table[i].value = value_t(fennec::forward<ArgsT>(args)...);
751 } else {
752 i = _next_free();
753 if (p != npos) {
754 _left(p) = i;
755 }
756 if (_root == npos) {
757 _root = i;
758 }
759 fennec::construct(&_table[i], p, npos, npos, fennec::forward<ArgsT>(args)...);
760 }
761 return i;
762 }
763
764 template<typename...ArgsT>
765 constexpr size_t _insert_right(size_t p, ArgsT&&...args) {
766 size_t i = p == npos ? _root : _right(p);
767 if (i != npos) {
768 _table[i].value = value_t(fennec::forward<ArgsT>(args)...);
769 } else {
770 i = _next_free();
771 if (p != npos) {
772 _right(p) = i;
773 }
774 if (_root == npos) {
775 _root = i;
776 }
777 fennec::construct(&_table[i], p, npos, npos, fennec::forward<ArgsT>(args)...);
778 }
779 return i;
780 }
781
782 template<typename...ArgsT>
783 constexpr size_t _insert(size_t p, bool d, ArgsT&&...args) {
784 size_t i = p == npos ? _root : _child(p, d);
785 if (i != npos) {
786 _table[i].value = value_t(fennec::forward<ArgsT>(args)...);
787 return i;
788 }
789
790 i = _next_free();
791 if (p != npos) {
792 _child(p, d) = i;
793 }
794 if (_root == npos) {
795 _root = i;
796 }
797 fennec::construct(&_table[i], p, npos, npos, fennec::forward<ArgsT>(args)...);
798 return i;
799 }
800
801
802 constexpr size_t& _parent(size_t i) {
803 return _table[i].parent;
804 }
805
806 constexpr size_t& _grandparent(size_t i) {
807 return _parent(_parent(i));
808 }
809
810 constexpr size_t& _left(size_t i) {
811 return _table[i].child[false];
812 }
813
814 constexpr size_t& _right(size_t i) {
815 return _table[i].child[true];
816 }
817
818 constexpr size_t& _child(size_t i, bool dir) {
819 return _table[i].child[dir];
820 }
821
822 constexpr size_t& _sibling(size_t i) {
823 size_t p = _parent(i);
824 bool d = i == _right(p);
825 return _child(p, !d);
826 }
827};
828
829}
830
831#endif // FENNEC_CONTAINERS_BINTREE_H
This header contains structures and classes related to allocating blocks of memory.
A header containing the definition for a dynamically allocated array.
A header containing the definition for a linked list of values.
A header containing the definition for a container with an optionally present variable.
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 size_t size() const
Getter for the byte size of the allocation.
Definition allocator.h:610
Helper structure for obtaining traits of an allocator class.
Definition allocator.h:61
Structure defining a binary tree.
Definition bintree.h:48
constexpr size_t parent(size_t i) const
Definition bintree.h:186
constexpr size_t left(size_t i) const
Definition bintree.h:202
constexpr size_t insert_left(size_t p, const value_t &val)
Copy Left Insertion, constructs a new node as the left child of p
Definition bintree.h:338
constexpr bintree(const bintree &tree)
Copy Constructor, copies a tree.
Definition bintree.h:121
constexpr const value_t & operator[](size_t i) const
Definition bintree.h:310
constexpr size_t sibling(size_t i) const
Definition bintree.h:235
constexpr bintree(bintree &&tree) noexcept
Move Constructor, takes ownership of a tree.
Definition bintree.h:111
constexpr size_t size() const
Definition bintree.h:142
constexpr size_t capacity() const
Definition bintree.h:154
constexpr value_t & operator[](size_t i)
Definition bintree.h:301
constexpr size_t left_most(size_t i) const
Definition bintree.h:264
constexpr size_t insert_right(size_t p, value_t &&val)
Move Right Insertion, constructs a new node as the right child of p
Definition bintree.h:359
constexpr void clear()
Clears the tree, destroying all elements.
Definition bintree.h:450
constexpr void traverse(VisitorT &&visit, size_t i)
Traverse the tree using a specified order and visiting functor.
Definition bintree.h:492
constexpr size_t rotate(size_t sub, bool dir)
Perform a Tree Rotation at i in the specified direction.
Definition bintree.h:390
constexpr size_t right(size_t i) const
Definition bintree.h:210
constexpr size_t right_most(size_t i) const
Definition bintree.h:278
constexpr size_t root() const
Definition bintree.h:170
constexpr size_t grandparent(size_t i) const
Definition bintree.h:194
constexpr bool direction(size_t i) const
Definition bintree.h:227
constexpr size_t emplace_left(size_t p, ArgsT &&...args)
Emplace Left Insertion, constructs a new node as the left child of p
Definition bintree.h:349
constexpr size_t emplace(size_t p, bool d, ArgsT &&...args)
Emplace Insertion, constructs a new node as the child of p
Definition bintree.h:444
constexpr size_t insert(size_t p, bool d, value_t &&val)
Move Insertion, bool d, constructs a new node as the child of p
Definition bintree.h:421
constexpr ~bintree()
Destructor, clears the tree.
Definition bintree.h:130
constexpr bool empty() const
Definition bintree.h:148
constexpr size_t next_id() const
Definition bintree.h:160
constexpr size_t insert_left(size_t p, value_t &&val)
Move Left Insertion, constructs a new node as the left child of p
Definition bintree.h:328
constexpr bintree()
Default Constructor, initializes an empty tree.
Definition bintree.h:101
constexpr size_t child(size_t i, bool dir) const
Definition bintree.h:219
constexpr size_t insert(size_t p, bool d, const value_t &val)
Copy Insertion, bool d, constructs a new node as the child of p
Definition bintree.h:432
constexpr size_t emplace_right(size_t p, ArgsT &&...args)
Emplace Right Insertion, constructs a new node as the right child of p
Definition bintree.h:380
constexpr size_t insert_right(size_t p, const value_t &val)
Copy Right Insertion, constructs a new node as the right child of p
Definition bintree.h:369
constexpr size_t depth(size_t i) const
Definition bintree.h:251
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
a header containing constants and utilities related to traversal
::uint8_t uint8_t
Unsigned 8-bit integer.
Definition types.h:272