- Lots of bug-fixing for containers - Performance optimization for containers
832 lines
20 KiB
C++
832 lines
20 KiB
C++
// =====================================================================================================================
|
|
// fennec, a free and open source game engine
|
|
// Copyright © 2025 Medusa Slockbower
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
// =====================================================================================================================
|
|
|
|
///
|
|
/// \file bintree.h
|
|
/// \brief
|
|
///
|
|
///
|
|
/// \details
|
|
/// \author Medusa Slockbower
|
|
///
|
|
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
|
///
|
|
///
|
|
|
|
#ifndef FENNEC_CONTAINERS_BINTREE_H
|
|
#define FENNEC_CONTAINERS_BINTREE_H
|
|
|
|
#include <fennec/containers/dynarray.h>
|
|
#include <fennec/containers/list.h>
|
|
#include <fennec/containers/optional.h>
|
|
#include <fennec/containers/traversal.h>
|
|
#include <fennec/math/exponential.h>
|
|
#include <fennec/memory/allocator.h>
|
|
|
|
namespace fennec
|
|
{
|
|
|
|
///
|
|
/// \brief Structure defining a binary tree
|
|
/// \tparam TypeT The data type
|
|
/// \tparam AllocT An allocator class
|
|
template<typename TypeT, class AllocT = allocator<TypeT>>
|
|
struct bintree {
|
|
|
|
// Definitions =========================================================================================================
|
|
protected:
|
|
struct node;
|
|
|
|
public:
|
|
using value_t = TypeT;
|
|
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
|
|
static constexpr size_t npos = -1;
|
|
|
|
friend class iterator;
|
|
friend class const_iterator;
|
|
|
|
protected:
|
|
struct node {
|
|
value_t value;
|
|
size_t parent, left, right;
|
|
size_t depth;
|
|
|
|
constexpr node()
|
|
: value(nullopt)
|
|
, parent(npos), left(npos), right(npos)
|
|
, depth(0) {
|
|
}
|
|
|
|
template<typename...ArgsT>
|
|
constexpr node(size_t p, size_t l, size_t r, size_t d, ArgsT&&...args)
|
|
: value(fennec::forward<ArgsT>(args)...)
|
|
, parent(p), left(l), right(r)
|
|
, depth(d) {
|
|
}
|
|
|
|
constexpr ~node() {
|
|
parent = npos;
|
|
left = npos;
|
|
right = npos;
|
|
depth = npos;
|
|
}
|
|
|
|
size_t& operator[](bool d) {
|
|
return d ? right : left;
|
|
}
|
|
};
|
|
|
|
using table_t = allocation<node, alloc_t>;
|
|
using freed_t = list<size_t, alloc_t>;
|
|
|
|
// Constructors & Destructor ===========================================================================================
|
|
public:
|
|
|
|
/// \name Constructors & Destructor
|
|
/// @{
|
|
|
|
///
|
|
/// \brief Default Constructor, initializes an empty tree
|
|
constexpr bintree()
|
|
: _table()
|
|
, _freed()
|
|
, _root(npos)
|
|
, _size(0) {
|
|
}
|
|
|
|
///
|
|
/// \brief Move Constructor, takes ownership of a tree
|
|
/// \param tree The tree to take ownership of
|
|
constexpr bintree(bintree&& tree) noexcept
|
|
: _table(fennec::move(tree._table))
|
|
, _freed(fennec::move(tree._freed))
|
|
, _root(tree._root)
|
|
, _size(tree._size) {
|
|
}
|
|
|
|
///
|
|
/// \brief Copy Constructor, copies a tree
|
|
/// \param tree The tree to copy
|
|
constexpr bintree(const bintree& tree)
|
|
: _table(tree._table)
|
|
, _freed(tree._freed)
|
|
, _root(tree._root)
|
|
, _size(tree._size) {
|
|
}
|
|
|
|
///
|
|
/// \brief Destructor, clears the tree
|
|
constexpr ~bintree() {
|
|
clear();
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Properties ==========================================================================================================
|
|
public:
|
|
|
|
///
|
|
/// \returns The number of elements in the tree
|
|
constexpr size_t size() const {
|
|
return _size;
|
|
}
|
|
|
|
///
|
|
/// \returns `true` when there are no elements in the tree, `false` otherwise.
|
|
constexpr bool empty() const {
|
|
return _size == 0;
|
|
}
|
|
|
|
///
|
|
/// \returns The capacity of the underlying allocation
|
|
constexpr size_t capacity() const {
|
|
return _table.capacity();
|
|
}
|
|
|
|
///
|
|
/// \returns The next id to be returned by `insert` or `emplace`.
|
|
constexpr size_t next_id() const {
|
|
size_t i = _size;
|
|
if (not _freed.empty()) {
|
|
i = _freed.front();
|
|
}
|
|
return i;
|
|
}
|
|
|
|
///
|
|
/// \returns The next id to be returned by `insert` or `emplace`.
|
|
constexpr size_t root() const {
|
|
return _root;
|
|
}
|
|
|
|
|
|
|
|
// Navigation ==========================================================================================================
|
|
public:
|
|
|
|
/// \name Navigation
|
|
/// @{
|
|
|
|
///
|
|
/// \details \f$O(1)\f$
|
|
/// \param i The node id
|
|
/// \returns The parent of node `i`
|
|
constexpr size_t parent(size_t i) const {
|
|
return i == npos ? npos : _table[i].parent;
|
|
}
|
|
|
|
///
|
|
/// \details \f$O(1)\f$
|
|
/// \param i The node id
|
|
/// \returns The grandparent of node `i`
|
|
constexpr size_t grandparent(size_t i) const {
|
|
return parent(parent(i));
|
|
}
|
|
|
|
///
|
|
/// \details \f$O(1)\f$
|
|
/// \param i The node id
|
|
/// \returns The left child of node `i`
|
|
constexpr size_t left(size_t i) const {
|
|
return i == npos ? npos : _table[i].left;
|
|
}
|
|
|
|
///
|
|
/// \details \f$O(1)\f$
|
|
/// \param i The node id
|
|
/// \returns The right child of node `i`
|
|
constexpr size_t right(size_t i) const {
|
|
return i == npos ? npos : _table[i].right;
|
|
}
|
|
|
|
///
|
|
/// \details \f$O(1)\f$
|
|
/// \param i The node id
|
|
/// \param dir The direction to go `true` for right, `false` for left
|
|
/// \returns The child in the direction specified by `dir`
|
|
constexpr size_t child(size_t i, bool dir) const {
|
|
return dir ? right(i) : left(i);
|
|
}
|
|
|
|
///
|
|
/// \details \f$O(1)\f$
|
|
/// \param i The node id
|
|
/// \returns `true` if `i` is the right node of `parent(i)`, `false` otherwise
|
|
constexpr bool direction(size_t i) const {
|
|
return i == npos ? false : i == right(parent(i));
|
|
}
|
|
|
|
///
|
|
/// \brief \f$O(1)\f$
|
|
/// \param i The id of the node
|
|
/// \returns The id of the sibling of `i`
|
|
constexpr size_t sibling(size_t i) const {
|
|
size_t p = parent(i);
|
|
size_t l = left(p);
|
|
size_t r = right(p);
|
|
return i == l ? r : l;
|
|
}
|
|
|
|
///
|
|
/// \details \f$O(\log n)\f$
|
|
/// \param i The node id
|
|
/// \returns The depth of node `i`
|
|
constexpr size_t depth(size_t i) const {
|
|
return i >= _table.size() ? npos : _table[i].depth;
|
|
}
|
|
|
|
///
|
|
/// \brief \f$O(\log n)\f$
|
|
/// \param i The node id
|
|
/// \returns The id of the left-most node of `i`
|
|
constexpr size_t left_most(size_t i) const {
|
|
if (i >= _table.size()) {
|
|
return npos;
|
|
}
|
|
while (_table[i].left != npos) {
|
|
i = _table[i].left;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
///
|
|
/// \brief \f$O(\log n)\f$
|
|
/// \param i The node id
|
|
/// \returns The id of the right-most node of `i`
|
|
constexpr size_t right_most(size_t i) const {
|
|
if (i >= _table.size()) {
|
|
return npos;
|
|
}
|
|
while (_table[i].right != npos) {
|
|
i = _table[i].right;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Access ==============================================================================================================
|
|
public:
|
|
|
|
/// \name Access
|
|
/// @{
|
|
|
|
///
|
|
/// \details \f$O(1)\f$
|
|
/// \param i The node id
|
|
/// \returns `nullptr` if node `i` does not exist, otherwise, a pointer to the value of node `i`
|
|
constexpr value_t& operator[](size_t i) {
|
|
assertd(i < _table.size(), "Index out of bounds.");
|
|
return _table[i].value;
|
|
}
|
|
|
|
///
|
|
/// \details Const Access, \f$O(1)\f$
|
|
/// \param i The node id
|
|
/// \returns `nullptr` if node `i` does not exist, otherwise, a pointer to the value of node `i`
|
|
constexpr const value_t& operator[](size_t i) const {
|
|
assertd(i < _table.size(), "Index out of bounds.");
|
|
return _table[i].value;
|
|
}
|
|
|
|
/// @}
|
|
|
|
// Modifiers ===========================================================================================================
|
|
|
|
/// \name Modifiers
|
|
/// @{
|
|
|
|
///
|
|
/// \brief Move Left Insertion, constructs a new node as the left child of `p`
|
|
/// \details If the left node of `p` already exists, the move assignment operator is used instead
|
|
/// \param p The parent node
|
|
/// \param val The object to move into the new node
|
|
/// \returns The id of the new node
|
|
constexpr size_t insert_left(size_t p, value_t&& val) {
|
|
return this->_insert_left(p, fennec::forward<value_t>(val));
|
|
}
|
|
|
|
///
|
|
/// \brief Copy Left Insertion, constructs a new node as the left child of `p`
|
|
/// \details If the left node of `p` already exists, the copy assignment operator is used instead
|
|
/// \param p The parent node
|
|
/// \param val The object to copy to the new node
|
|
/// \returns The id of the new node
|
|
constexpr size_t insert_left(size_t p, const value_t& val) {
|
|
return this->_insert_left(p, val);
|
|
}
|
|
|
|
///
|
|
/// \brief Emplace Left Insertion, constructs a new node as the left child of `p`
|
|
/// \details If the left node of `p` already exists, the move assignment operator is used instead
|
|
/// \param p The parent node
|
|
/// \param args The arguments to construct the new node with
|
|
/// \returns The id of the new node
|
|
template<typename...ArgsT>
|
|
constexpr size_t emplace_left(size_t p, ArgsT&&...args) {
|
|
return this->_insert_left(p, fennec::forward<ArgsT>(args)...);
|
|
}
|
|
|
|
///
|
|
/// \brief Move Right Insertion, constructs a new node as the right child of `p`
|
|
/// \details If the right node of `p` already exists, the move assignment operator is used instead
|
|
/// \param p The parent node
|
|
/// \param val The object to move into the new node
|
|
/// \returns The id of the new node
|
|
constexpr size_t insert_right(size_t p, value_t&& val) {
|
|
return this->_insert_right(p, fennec::forward<value_t>(val));
|
|
}
|
|
|
|
///
|
|
/// \brief Copy Right Insertion, constructs a new node as the right child of `p`
|
|
/// \details If the right node of `p` already exists, the copy assignment operator is used instead
|
|
/// \param p The parent node
|
|
/// \param val The object to copy to the new node
|
|
/// \returns The id of the new node
|
|
constexpr size_t insert_right(size_t p, const value_t& val) {
|
|
return this->_insert_right(p, val);
|
|
}
|
|
|
|
///
|
|
/// \brief Emplace Right Insertion, constructs a new node as the right child of `p`
|
|
/// \details If the right node of `p` already exists, the move assignment operator is used instead
|
|
/// \param p The parent node
|
|
/// \param args The arguments to construct the new node with
|
|
/// \returns The id of the new node
|
|
template<typename...ArgsT>
|
|
constexpr size_t emplace_right(size_t p, ArgsT&&...args) {
|
|
return this->_insert_right(p, fennec::forward<ArgsT>(args)...);
|
|
}
|
|
|
|
///
|
|
/// \brief Perform a Left Tree Rotation at `i`
|
|
/// \param i The root node for the rotation
|
|
constexpr void rotate_left(size_t i) {
|
|
if (i >=_table.size()) {
|
|
return;
|
|
}
|
|
|
|
size_t l = i;
|
|
size_t p = parent(l);
|
|
size_t r = right(l);
|
|
|
|
if (p == npos) {
|
|
_root = r;
|
|
} else if (l == _table[p].left) {
|
|
_table[p].left = r;
|
|
} else {
|
|
_table[p].right = r;
|
|
}
|
|
|
|
_table[l].parent = r;
|
|
_table[l].right = _table[r].left;
|
|
|
|
_table[r].parent = p;
|
|
_table[r].left = l;
|
|
}
|
|
|
|
///
|
|
/// \brief Perform a Right Tree Rotation at `i`
|
|
/// \param i The root node for the rotation
|
|
constexpr void rotate_right(size_t i) {
|
|
if (i >=_table.size()) {
|
|
return;
|
|
}
|
|
|
|
size_t r = i;
|
|
size_t p = parent(r);
|
|
size_t l = left(r);
|
|
|
|
if (p == npos) {
|
|
_root = l;
|
|
} else if (r == _table[p].left) {
|
|
_table[p].left = l;
|
|
} else {
|
|
_table[p].right = l;
|
|
}
|
|
|
|
_table[r].parent = l;
|
|
_table[r].left = _table[l].right;
|
|
|
|
_table[l].parent = p;
|
|
_table[l].right = r;
|
|
}
|
|
|
|
///
|
|
/// \brief Perform a Tree Rotation at `i` in the specified direction
|
|
/// \param i The root node for the rotation
|
|
/// \param dir The direction to rotate, `true` for right, `false` for left
|
|
constexpr size_t rotate(size_t sub, bool dir) {
|
|
if (sub >=_table.size()) {
|
|
return npos;
|
|
}
|
|
size_t sub_parent = parent(sub);
|
|
size_t new_root = child(sub, not dir);
|
|
size_t new_child = child(new_root, dir);
|
|
|
|
_child(sub, not dir) = new_child;
|
|
if (new_child != npos) {
|
|
_parent(new_child) = sub;
|
|
}
|
|
_child(new_root, dir) = sub;
|
|
|
|
_parent(new_root) = sub_parent;
|
|
_parent(sub) = new_root;
|
|
if (sub_parent != npos) {
|
|
_child(sub_parent, sub == right(sub_parent)) = new_root;
|
|
} else {
|
|
_root = new_root;
|
|
}
|
|
return new_root;
|
|
}
|
|
|
|
///
|
|
/// \brief Clears the tree, destroying all elements
|
|
constexpr void clear() {
|
|
list<size_t> queue;
|
|
|
|
if (_root != npos) {
|
|
queue.push_back(_root);
|
|
}
|
|
|
|
while (not queue.empty()) {
|
|
size_t i = queue.front();
|
|
queue.pop_front();
|
|
|
|
if (_table[i].left != npos) {
|
|
queue.push_front(_table[i].left);
|
|
}
|
|
if (_table[i].right != npos) {
|
|
queue.push_front(_table[i].right);
|
|
}
|
|
|
|
fennec::destruct(&_table[i]);
|
|
}
|
|
|
|
_size = 0;
|
|
_root = npos;
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Traversal ===========================================================================================================
|
|
|
|
///
|
|
/// \brief Traverse the tree using a specified order and visiting functor
|
|
///
|
|
/// \details
|
|
/// The visitor should accept a reference to a value of type `TypeT` and a `size_t` which contains the node's id.
|
|
/// The visitor should return one of the following values in the `fennec::traversal_control_` enum
|
|
///
|
|
/// \tparam OrderT The order with which to traverse the tree.
|
|
/// \tparam VisitorT The visitor, should fulfill the signature `uint8_t visit(TypeT&, size_t)`
|
|
/// \param visit The visiting object
|
|
/// \param i The node to start at
|
|
template<typename OrderT, typename VisitorT>
|
|
constexpr void traverse(VisitorT&& visit, size_t i) {
|
|
OrderT order;
|
|
i = order(*this, i);
|
|
while (i != npos) {
|
|
uint8_t mode = traversal_control_continue;
|
|
if (_table[i].value) {
|
|
mode = visit(_table[i].value, i);
|
|
}
|
|
if (mode == traversal_control_break) {
|
|
break;
|
|
}
|
|
i = order[*this, i, mode];
|
|
}
|
|
}
|
|
|
|
struct breadth_first {
|
|
list<size_t> visit;
|
|
size_t head;
|
|
|
|
size_t operator()(const bintree&, size_t start) {
|
|
return head = start;
|
|
}
|
|
|
|
size_t operator[](const bintree& tree, size_t node, uint8_t mode) {
|
|
if (node == npos) {
|
|
return npos;
|
|
}
|
|
|
|
size_t lft = tree.left(tree.parent(node));
|
|
size_t nxt = lft == node ? tree.right(tree.parent(node)) : npos;
|
|
size_t chd = tree.left(node);
|
|
|
|
if (nxt != npos && node != head) {
|
|
visit.push_front(nxt);
|
|
}
|
|
|
|
if (chd != npos && mode != traversal_control_jump_over) {
|
|
visit.push_back(chd);
|
|
}
|
|
|
|
if (not visit.empty()) {
|
|
node = visit.front();
|
|
visit.pop_front();
|
|
} else {
|
|
node = npos;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
};
|
|
|
|
struct pre_order {
|
|
list<size_t> visit;
|
|
size_t head;
|
|
|
|
constexpr size_t operator()(const bintree&, size_t start) {
|
|
head = start;
|
|
return start;
|
|
}
|
|
|
|
constexpr size_t operator[](const bintree& tree, size_t node, uint8_t mode) {
|
|
if (node == npos) {
|
|
return npos;
|
|
}
|
|
|
|
size_t nxt = tree.right(tree.parent(node));
|
|
size_t chd = tree.left(node);
|
|
nxt = node == nxt ? npos : nxt;
|
|
|
|
if (nxt != npos && node != head) {
|
|
visit.push_front(nxt);
|
|
}
|
|
|
|
if (chd != npos && mode != traversal_control_jump_over) {
|
|
visit.push_front(chd);
|
|
}
|
|
|
|
if (not visit.empty()) {
|
|
node = visit.front();
|
|
visit.pop_front();
|
|
} else {
|
|
node = npos;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
};
|
|
|
|
struct in_order {
|
|
list<size_t> visit;
|
|
size_t head;
|
|
|
|
constexpr size_t operator()(const bintree& tree, size_t start) {
|
|
head = start;
|
|
return tree.left_most(start);
|
|
}
|
|
|
|
constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
|
|
if (node == npos) {
|
|
return npos;
|
|
}
|
|
|
|
size_t parent = tree.parent(node);
|
|
size_t pright = tree.right(parent);
|
|
size_t next = tree.left_most(tree.right(node));
|
|
|
|
if (node != pright && parent != npos) {
|
|
visit.push_front(parent);
|
|
}
|
|
|
|
if (next != npos) {
|
|
visit.push_front(next);
|
|
}
|
|
|
|
if (not visit.empty()) {
|
|
node = visit.front();
|
|
visit.pop_front();
|
|
} else {
|
|
node = npos;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
};
|
|
|
|
struct post_order {
|
|
list<size_t> visit;
|
|
size_t head;
|
|
|
|
constexpr size_t successor(const bintree& tree, size_t n) {
|
|
size_t s = tree.left_most(n);
|
|
while (n == s) {
|
|
size_t r = tree.right(n);
|
|
if (r != npos) {
|
|
n = r;
|
|
s = tree.left_most(n);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return s == npos ? n : s;
|
|
}
|
|
|
|
constexpr size_t operator()(const bintree& tree, size_t start) {
|
|
head = start;
|
|
return this->successor(tree, start);
|
|
}
|
|
|
|
constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
|
|
if (node == npos) {
|
|
return npos;
|
|
}
|
|
|
|
size_t parent = tree.parent(node);
|
|
size_t pright = tree.right(parent);
|
|
|
|
if (node == pright) {
|
|
if (parent != npos) {
|
|
visit.push_front(parent);
|
|
}
|
|
} else if (pright != npos) {
|
|
visit.push_front(this->successor(tree, pright));
|
|
}
|
|
|
|
if (not visit.empty()) {
|
|
node = visit.front();
|
|
visit.pop_front();
|
|
} else {
|
|
node = npos;
|
|
}
|
|
|
|
|
|
return node;
|
|
}
|
|
};
|
|
|
|
// Iterator ============================================================================================================
|
|
|
|
class iterator {
|
|
protected:
|
|
bintree* _tree;
|
|
in_order _order;
|
|
size_t _n;
|
|
|
|
public:
|
|
constexpr iterator(bintree* tree, size_t root)
|
|
: _tree(tree)
|
|
, _order()
|
|
, _n(_order(*tree, root)) {
|
|
}
|
|
|
|
constexpr iterator(bintree* tree, size_t root, size_t node)
|
|
: _tree(tree)
|
|
, _order()
|
|
, _n(node) {
|
|
_order(*tree, root);
|
|
}
|
|
|
|
size_t index() const {
|
|
return _n;
|
|
}
|
|
|
|
iterator& operator++() {
|
|
return _n = _order[*_tree, _n, traversal_control_continue], *this;
|
|
}
|
|
|
|
value_t& operator*() {
|
|
return (*_tree)[_n];
|
|
}
|
|
|
|
value_t* operator->() {
|
|
return &(*_tree)[_n];
|
|
}
|
|
|
|
const value_t& operator*() const {
|
|
return (*_tree)[_n];
|
|
}
|
|
|
|
const value_t* operator->() const {
|
|
return &(*_tree)[_n];
|
|
}
|
|
|
|
constexpr bool operator==(const iterator& it) {
|
|
return _tree == it._tree and _n == it._n;
|
|
}
|
|
|
|
constexpr bool operator!=(const iterator& it) {
|
|
return _tree != it._tree or _n != it._n;
|
|
}
|
|
|
|
};
|
|
|
|
// Fields ==============================================================================================================
|
|
protected:
|
|
table_t _table;
|
|
freed_t _freed;
|
|
size_t _root, _size;
|
|
|
|
// Helpers =============================================================================================================
|
|
|
|
constexpr size_t _next_free() {
|
|
size_t i = _size;
|
|
if (not _freed.empty()) {
|
|
i = _freed.front();
|
|
_freed.pop_front();
|
|
}
|
|
if (i >= _table.capacity()) {
|
|
_table.creallocate(2 * fennec::max(_table.capacity(), size_t(4)));
|
|
}
|
|
++_size;
|
|
return i;
|
|
}
|
|
|
|
template<typename...ArgsT>
|
|
constexpr size_t _insert_left(size_t p, ArgsT&&...args) {
|
|
size_t i = p >= capacity() ? npos : p;
|
|
i = i == npos ? _root : _left(i);
|
|
if (i != npos) {
|
|
_table[i].value = value_t(fennec::forward<ArgsT>(args)...);
|
|
} else {
|
|
size_t d = 1;
|
|
i = _next_free();
|
|
if (p != npos) {
|
|
d = depth(p) + 1;
|
|
_table[p].left = i;
|
|
}
|
|
if (_root == npos) {
|
|
_root = i;
|
|
}
|
|
fennec::construct(&_table[i], p, npos, npos, d, fennec::forward<ArgsT>(args)...);
|
|
}
|
|
return i;
|
|
}
|
|
|
|
template<typename...ArgsT>
|
|
constexpr size_t _insert_right(size_t p, ArgsT&&...args) {
|
|
size_t i = p == npos ? _root : _right(p);
|
|
if (i != npos) {
|
|
_table[i].value = value_t(fennec::forward<ArgsT>(args)...);
|
|
if (p == npos || _root == npos) {
|
|
_root = i;
|
|
}
|
|
} else {
|
|
size_t d = 1;
|
|
i = _next_free();
|
|
if (p != npos) {
|
|
d = depth(p) + 1;
|
|
_table[p].right = i;
|
|
}
|
|
if (_root == npos) {
|
|
_root = i;
|
|
}
|
|
fennec::construct(&_table[i], p, npos, npos, d, fennec::forward<ArgsT>(args)...);
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
constexpr size_t& _parent(size_t i) {
|
|
return _table[i].parent;
|
|
}
|
|
|
|
constexpr size_t& _grandparent(size_t i) {
|
|
return _parent(parent(i));
|
|
}
|
|
|
|
constexpr size_t& _left(size_t i) {
|
|
return _table[i].left;
|
|
}
|
|
|
|
constexpr size_t& _right(size_t i) {
|
|
return _table[i].right;
|
|
}
|
|
|
|
constexpr size_t& _child(size_t i, bool dir) {
|
|
return dir ? _right(i) : _left(i);
|
|
}
|
|
|
|
constexpr size_t& _sibling(size_t i) {
|
|
size_t p = parent(i);
|
|
size_t& l = _left(p);
|
|
size_t& r = _right(p);
|
|
return i == l ? l : r;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
#endif // FENNEC_CONTAINERS_BINTREE_H
|