fennec
Loading...
Searching...
No Matches
sequence.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_SEQUENCE_H
32#define FENNEC_CONTAINERS_SEQUENCE_H
42#include <fennec/lang/compare.h>
44
45// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
46// https://www.geeksforgeeks.org/dsa/insertion-in-red-black-tree/
47// https://github.com/anandarao/Red-Black-Tree/blob/master/RBTree.cpp
48
49// After rewriting the _fix_insert and _fix_erase functions the performance decreased significantly in the lower end
50// but now in the higher end it remains consistent. Something I was doing was disturbing both the rb-tree and bst tree
51// properties, now that is fixed. I'll see about optimizing more in the future.
52
53// I realized that the way bintree is setup makes some insert calls O(n + log n) = O(n), so I switched to a pointer based model.
54// This increased performance overall maintaining O(log n).
55
56namespace fennec
57{
58
82template<typename TypeT, typename CompareT = less<TypeT>, class AllocT = allocator<pair<TypeT, bool>>>
83struct sequence {
84
85// Definitions =========================================================================================================
86protected:
87 struct _node;
88
89public:
90 using value_t = TypeT;
92 using alloc_t = allocator_traits<AllocT>::template rebind<_node>;
93 using compare_t = CompareT;
94
95 enum color_ : bool {
96 black = false,
97 red = true,
98 };
99
100 enum dir_ : bool {
101 dir_left = false,
102 dir_right = true,
103 };
104
105 class iterator;
106
107protected:
108 using node = _node*;
109
110 struct _node {
111 node parent;
112 node child[2];
113 value_t key;
114 bool color;
115
116 template<typename...ArgsT>
117 constexpr _node(ArgsT&&...args)
118 : parent(nullptr)
119 , child { nullptr, nullptr }
120 , key(fennec::forward<ArgsT>(args)...)
121 , color(red) {
122 }
123
124 template<typename...ArgsT>
125 constexpr _node(node p, node l, node r, ArgsT&&...args)
126 : parent(p)
127 , child { l, r }
128 , key(fennec::forward<ArgsT>(args)...)
129 , color(red) {
130 }
131
132 constexpr ~_node() {
133 parent = nullptr;
134 child[0] = nullptr;
135 child[1] = nullptr;
136 }
137 };
138
139
140// Member Access Helpers
141
142 constexpr value_t& _key(node n) {
143 return n->key;
144 }
145
146 constexpr bool& _color(node n) {
147 return n->color;
148 }
149
150 constexpr node& _parent(node n) {
151 return n->parent;
152 }
153
154 constexpr node& _child(node n, bool dir) {
155 return n->child[dir];
156 }
157
158 constexpr node& _left(node n) {
159 return n->child[dir_left];
160 }
161
162 constexpr node& _right(node n) {
163 return n->child[dir_right];
164 }
165
166
167// Safe Member Access Helpers
168
169
170 constexpr const value_t& key(node n) const {
171 return n->key;
172 }
173
174 constexpr bool color(node n) {
175 return n ? n->color : (bool)black;
176 }
177
178 constexpr node parent(node n) {
179 return n ? n->parent : nullptr;
180 }
181
182 constexpr node child(node n, bool dir) {
183 return n ? n->child[dir] : nullptr;
184 }
185
186 constexpr node left(node n) {
187 return n ? n->child[dir_left] : nullptr;
188 }
189
190 constexpr node right(node n) {
191 return n ? n->child[dir_right] : nullptr;
192 }
193
194 constexpr node leftmost(node n) {
195 if (n == nullptr) {
196 return nullptr;
197 }
198
199 while (this->_left(n)) {
200 n = this->_left(n);
201 }
202 return n;
203 }
204
205 constexpr node rightmost(node n) {
206 if (n == nullptr) {
207 return nullptr;
208 }
209
210 while (this->_right(n)) {
211 n = this->_right(n);
212 }
213 return n;
214 }
215
216// Constructors & Destructors ==========================================================================================
217public:
220
223 constexpr sequence()
224 : _root(nullptr), _size(0) {
225 }
226
229 constexpr sequence(sequence&&) noexcept = default;
230
233 constexpr sequence(const sequence&) = default;
234
237 constexpr ~sequence() {
238 this->clear();
239 }
240
242
243// Search ==============================================================================================================
244public:
247
252 constexpr iterator find(const value_t& val) {
253 node node = _root;
254 while (node) {
255 if (_compare(val, _key(node))) {
256 node = _left(node);
257 } else if (_compare(_key(node), val)) {
258 node = _right(node);
259 } else {
260 return sequence::iterator(this, _root, node);
261 }
262 }
263 return sequence::iterator(this, _root, node);
264 }
265
270 bool contains(const value_t& val) {
271 return find(val) != end();
272 }
273
275
276
277// Properties ==========================================================================================================
278public:
281
284 constexpr size_t size() const {
285 return _size;
286 }
287
290 constexpr bool empty() const {
291 return _size == 0;
292 }
293
295
296// Modifiers ===========================================================================================================
297public:
300
304 constexpr void insert(value_t&& val) {
305 node i = _insert_bst(fennec::forward<value_t>(val));
306 _fix_insert(i);
307 }
308
312 constexpr void insert(const value_t& val) {
313 node i = _insert_bst(val);
314 _fix_insert(i);
315 }
316
321 template<typename...ArgsT>
322 constexpr void emplace(ArgsT&&...args) {
323 node i = _insert_bst(fennec::forward<ArgsT>(args)...);
324 _fix_insert(i);
325 }
326
327 constexpr void erase(const value_t& val) {
328 _erase(find(val)._node);
329 }
330
333 constexpr void clear() {
334 list<node> visit;
335 for (iterator it = begin(); it != end(); ++it) {
336 visit.push_back(it._node);
337 }
338 for (node n : visit) {
339 _free_node(n);
340 }
341 _root = nullptr;
342 _size = 0;
343 }
344
346
347// Iterator ============================================================================================================
348
351 constexpr iterator begin() {
352 return sequence::iterator(this, _root);
353 }
354
357 constexpr iterator end() {
358 return sequence::iterator(this, _root, nullptr);
359 }
360
361 class iterator {
362 protected:
363 sequence* _seq;
364 node _head;
365 node _node;
366 list<node> _visit;
367
368
369 public:
370 constexpr iterator(sequence* seq, node start)
371 : _seq(seq)
372 , _head(start)
373 , _node(seq->leftmost(start)) {
374 }
375
376 constexpr iterator(sequence* seq, node root, node start)
377 : _seq(seq)
378 , _head(root)
379 , _node(start) {
380 }
381
382 iterator& operator++() {
383 if (_node == nullptr) {
384 return *this;
385 }
386
387 node parent = _seq->_parent(_node);
388 node pright = _seq->right(parent);
389 node next = _seq->leftmost(_seq->right(_node));
390
391 if (_node != pright && parent != nullptr) {
392 _visit.push_front(parent);
393 }
394
395 if (next != nullptr) {
396 _visit.push_front(next);
397 }
398
399 if (not _visit.empty()) {
400 _node = _visit.front();
401 _visit.pop_front();
402 } else {
403 _node = nullptr;
404 }
405
406 return *this;
407 }
408
409 iterator operator++(int) {
410 iterator prev = *this;
411 this->operator++();
412 return prev;
413 }
414
415 const value_t& operator*() const {
416 return _node->key;
417 }
418
419 const value_t* operator->() const {
420 return &_node->key;
421 }
422
423 constexpr friend bool operator==(const iterator& lhs, const iterator& rhs) {
424 return lhs._node == rhs._node;
425 }
426
427 friend struct sequence;
428 };
429
430
431// Fields ==============================================================================================================
432protected:
433 alloc_t _alloc;
434 node _root;
435 compare_t _compare;
436 size_t _size;
437
438// Helpers =============================================================================================================
439protected:
440
441 template<typename...ArgsT>
442 constexpr node _make_node(ArgsT&&...args) {
443 node res = _alloc.allocate(1);
444 fennec::construct(res, fennec::forward<ArgsT>(args)...);
445 return res;
446 }
447
448 constexpr void _free_node(node n) {
449 fennec::destruct(n);
450 _alloc.deallocate(n);
451 }
452
453 constexpr node _rotate(node sub, bool dir) {
454 if (sub == nullptr) {
455 return nullptr;
456 }
457 node sub_parent = _parent(sub);
458 node new_root = _child(sub, not dir);
459 node new_child = _child(new_root, dir);
460
461 _child(sub, not dir) = new_child;
462 if (new_child != nullptr) {
463 _parent(new_child) = sub;
464 }
465 _child(new_root, dir) = sub;
466
467 _parent(new_root) = sub_parent;
468 _parent(sub) = new_root;
469 if (sub_parent != nullptr) {
470 _child(sub_parent, sub == _right(sub_parent)) = new_root;
471 } else {
472 _root = new_root;
473 }
474 return new_root;
475 }
476
477 constexpr void _recolor(node n) {
478 bool c = color(n) == black;
479 if (n == _root) { // Only recolor if not the root node
480 _color(n) = c;
481 }
482 _color(_left(n)) = !_color(_left(n)) ;
483 _color(_right(n)) = !_color(_right(n));
484 }
485
486 // run-of-the-mill bst insert
487 template<typename...ArgsT>
488 constexpr node _insert_bst(ArgsT&&...args) {
489 node res = _make_node(fennec::forward<ArgsT>(args)...);
490
491 if (_root == nullptr) {
492 ++_size;
493 _color(res) = black;
494 return _root = res;
495 }
496
497 node i = _root;
498 node p = nullptr;
499 bool d = dir_left;
500 while (i != nullptr) {
501 p = i;
502
503 if (_compare(_key(res), _key(i))) {
504 i = _left(i);
505 d = dir_left;
506 } else if (_compare(_key(i), _key(res))) {
507 i = _right(i);
508 d = dir_right;
509 } else {
510 _free_node(res);
511 return nullptr;
512 }
513 }
514
515 ++_size;
516 _child(p, d) = res;
517 _parent(res) = p;
518 return res;
519 }
520
521 // This makes some cheats given that the structure is modified only by internal functions
522 // If such is the case, ONLY LL, LR, RL, and RR will show up
523 // Then we just need to handle splitting a 4-node
524 constexpr void _fix_insert(node n) {
525 if (n == nullptr) {
526 return;
527 }
528
529 node p = _parent(n);
530 while (n != _root && color(n) == red && color(p) == red) {
531 node g = _parent(p);
532 bool d = n == _right(p);
533 bool r = p == _right(g);
534 node u = _child(g, !r);
535
536 if (color(u) == red) {
537 _recolor(g);
538 n = g;
539 p = _parent(n);
540 continue;
541 }
542
543 if (d != r) {
544 _rotate(p, r);
545 n = p;
546 p = _parent(n);
547 }
548
549 _rotate(g, not r);
550 fennec::swap(_color(p), _color(g));
551 n = p;
552 p = _parent(n);
553 }
554 _color(_root) = black;
555 }
556
557 constexpr void _swap_val(node a, node b) {
558 fennec::swap(_key(a), _key(b));
559 }
560
561 constexpr node _red_child(node x) {
562 node l = _left(x);
563 node r = _right(x);
564
565 if (color(l) == red) {
566 return l;
567 }
568
569 if (color(r) == red) {
570 return r;
571 }
572
573 return nullptr;
574 }
575
576 constexpr void _fix_erase(node n) {
577 if (n == nullptr) {
578 return;
579 }
580
581 if (n == _root) {
582 _root = nullptr;
583 return;
584 }
585
586 node o = n;
587 node p = _parent(n);
588 if (p == nullptr) {
589 _root = nullptr;
590 return;
591 }
592
593 bool d = n == _right(p);
594 node c = _red_child(n);
595 node s = nullptr;
596 if (_color(n) == red || c != nullptr) {
597 _child(p, d) = c;
598 if (c != nullptr) {
599 _parent(c) = p;
600 }
601 _color(c) = black;
602 return;
603 }
604
605 while (n != _root) {
606 p = _parent(n);
607 d = n == _right(p);
608 s = _child(p, !d);
609
610 if (s == nullptr) {
611 break;
612 }
613
614 if (_color(s) == red) {
615 _color(s) = black;
616 _color(p) = red;
617 _rotate(p, d);
618 continue;
619 }
620
621 node nc = _child(s, d);
622 node nf = _child(s, !d);
623
624 if (color(nc) == black && color(nf) == black) {
625 _color(s) = red;
626 if (_color(p) == red) {
627 _color(p) = black;
628 break;
629 }
630 n = p;
631 continue;
632 }
633
634 if (color(nf) == black) {
635 _color(nc) = black;
636 _color(s) = red;
637 _rotate(s, !d);
638 s = nc;
639 nf = s;
640 }
641 _color(s) = _color(p);
642 _color(p) = black;
643 _color(nf) = black;
644 _rotate(p, d);
645 break;
646 }
647
648 p = parent(o);
649 if (p != nullptr) {
650 if (o == _left(p)) {
651 _left(p) = nullptr;
652 } else {
653 _right(p) = nullptr;
654 }
655 _color(_root) = black;
656 } else {
657 _root = nullptr;
658 }
659 }
660
661 constexpr void _erase(node n) {
662 if (n == nullptr) {
663 return;
664 }
665
666 node l = _left(n);
667 node r = _right(n);
668
669 // 2 children
670 if (l != nullptr && r != nullptr) {
671 node s = leftmost(r);
672 _swap_val(n, s);
673 n = s;
674 l = _left(n);
675 r = _right(n);
676 }
677
678 node p = _parent(n);
679 bool d = n == right(p);
680 node c = l != nullptr ? l : r;
681
682 // Single child
683 if (c != nullptr) {
684 _parent(c) = p;
685 }
686
687 // Handles root cases
688 if (p == nullptr) {
689 _root = c;
690 if (c == nullptr) {
691 _free_node(n);
692 --_size;
693 return;
694 } else {
695 _color(c) = black;
696 }
697 }
698
699 // Single Child, Red, and Root cases
700 if (p == nullptr || c != nullptr || _color(n) == red) {
701 if (p != nullptr) {
702 _child(p, d) = c;
703 }
704 _free_node(n);
705 --_size;
706 return;
707 }
708
709 _fix_erase(n);
710 _free_node(n);
711 --_size;
712 }
713};
714
715}
716
717#endif // FENNEC_CONTAINERS_SEQUENCE_H
This header contains structures and classes related to allocating blocks of memory.
A header containing the definition for a container holding a pair of values.
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
Struct for holding a pair of values.
Definition pair.h:48
wrapper for ordered sets of elements, called sequences in mathematics
Definition sequence.h:83
constexpr sequence(sequence &&) noexcept=default
Move Constructor, takes ownership of a sequence.
bool contains(const value_t &val)
Value Contains Function, checks if the sequence contains a value.
Definition sequence.h:270
constexpr void emplace(ArgsT &&...args)
Emplacement, constructs and adds a value into the sequence.
Definition sequence.h:322
constexpr void clear()
Destructs all elements, in-order, contained in the sequence.
Definition sequence.h:333
constexpr iterator find(const value_t &val)
Value Find Function, finds the iterator position for val, otherwise returns end()
Definition sequence.h:252
constexpr size_t size() const
Definition sequence.h:284
constexpr iterator begin()
Definition sequence.h:351
constexpr void insert(value_t &&val)
Move Insertion, moves val into the sequence.
Definition sequence.h:304
constexpr iterator end()
Definition sequence.h:357
constexpr sequence()
Default Constructor, initializes an empty sequence.
Definition sequence.h:223
constexpr bool empty() const
Definition sequence.h:290
constexpr void insert(const value_t &val)
Copy Insertion, inserts a copy of val into the sequence.
Definition sequence.h:312
constexpr void swap(T &x, T &y) noexcept
Swaps x and y.
Definition utility.h:114