- Documentation of containers, core, and format

This commit is contained in:
2025-12-18 00:00:36 -05:00
parent e7503ed92f
commit 9e6f00eb60
57 changed files with 2007 additions and 805 deletions

View File

@@ -48,18 +48,18 @@ template<typename TypeT, class AllocT = allocator<TypeT>>
struct bintree {
// Definitions =========================================================================================================
protected:
private:
struct node;
public:
using value_t = TypeT;
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
static constexpr size_t npos = -1;
using value_t = TypeT; //!< The element type
using alloc_t = allocator_traits<AllocT>::template rebind<node>; //!< The allocator type
static constexpr size_t npos = -1; //!< null position constant
friend class iterator;
friend class const_iterator;
protected:
private:
struct node {
value_t value;
size_t parent;
@@ -144,7 +144,7 @@ public:
}
///
/// \returns `true` when there are no elements in the tree, `false` otherwise.
/// \returns \f$true\f$ when there are no elements in the tree, \f$false\f$ otherwise.
constexpr bool empty() const {
return _size == 0;
}
@@ -156,7 +156,7 @@ public:
}
///
/// \returns The next id to be returned by `insert` or `emplace`.
/// \returns The next id to be returned by \f$insert\f$ or \f$emplace\f$.
constexpr size_t next_id() const {
size_t i = _size;
if (not _freed.empty()) {
@@ -166,7 +166,7 @@ public:
}
///
/// \returns The next id to be returned by `insert` or `emplace`.
/// \returns The next id to be returned by \f$insert\f$ or \f$emplace\f$.
constexpr size_t root() const {
return _root;
}
@@ -182,7 +182,7 @@ public:
///
/// \details \f$O(1)\f$
/// \param i The node id
/// \returns The parent of node `i`
/// \returns The parent of node \f$i\f$
constexpr size_t parent(size_t i) const {
return i == npos ? npos : _table[i].parent;
}
@@ -190,7 +190,7 @@ public:
///
/// \details \f$O(1)\f$
/// \param i The node id
/// \returns The grandparent of node `i`
/// \returns The grandparent of node \f$i\f$
constexpr size_t grandparent(size_t i) const {
return parent(parent(i));
}
@@ -198,7 +198,7 @@ public:
///
/// \details \f$O(1)\f$
/// \param i The node id
/// \returns The left child of node `i`
/// \returns The left child of node \f$i\f$
constexpr size_t left(size_t i) const {
return i == npos ? npos : _table[i].child[false];
}
@@ -206,7 +206,7 @@ public:
///
/// \details \f$O(1)\f$
/// \param i The node id
/// \returns The right child of node `i`
/// \returns The right child of node \f$i\f$
constexpr size_t right(size_t i) const {
return i == npos ? npos : _table[i].child[true];
}
@@ -214,8 +214,8 @@ public:
///
/// \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`
/// \param dir The direction to go \f$true\f$ for right, \f$false\f$ for left
/// \returns The child in the direction specified by \f$dir\f$
constexpr size_t child(size_t i, bool dir) const {
return i == npos ? npos : _table[i].child[dir];
}
@@ -223,7 +223,7 @@ public:
///
/// \details \f$O(1)\f$
/// \param i The node id
/// \returns `true` if `i` is the right node of `parent(i)`, `false` otherwise
/// \returns \f$true\f$ if \f$i\f$ is the right node of `parent(i)`, \f$false\f$ otherwise
constexpr bool direction(size_t i) const {
return i == npos ? false : i == right(_parent(i));
}
@@ -231,7 +231,7 @@ public:
///
/// \brief \f$O(1)\f$
/// \param i The id of the node
/// \returns The id of the sibling of `i`
/// \returns The id of the sibling of \f$i\f$
constexpr size_t sibling(size_t i) const {
if (i == npos) {
return npos;
@@ -247,7 +247,7 @@ public:
///
/// \details \f$O(\log n)\f$
/// \param i The node id
/// \returns The depth of node `i`
/// \returns The depth of node \f$i\f$
constexpr size_t depth(size_t i) const {
size_t d = 0;
while (i != npos) {
@@ -260,7 +260,7 @@ public:
///
/// \brief \f$O(\log n)\f$
/// \param i The node id
/// \returns The id of the left-most node of `i`
/// \returns The id of the left-most node of \f$i\f$
constexpr size_t left_most(size_t i) const {
if (i >= _table.size()) {
return npos;
@@ -274,7 +274,7 @@ public:
///
/// \brief \f$O(\log n)\f$
/// \param i The node id
/// \returns The id of the right-most node of `i`
/// \returns The id of the right-most node of \f$i\f$
constexpr size_t right_most(size_t i) const {
if (i >= _table.size()) {
return npos;
@@ -297,7 +297,7 @@ public:
///
/// \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`
/// \returns \f$nullptr\f$ if node \f$i\f$ does not exist, otherwise, a pointer to the value of node \f$i\f$
constexpr value_t& operator[](size_t i) {
assertd(i < _table.size(), "Index out of bounds.");
return _table[i].value;
@@ -306,7 +306,7 @@ public:
///
/// \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`
/// \returns \f$nullptr\f$ if node \f$i\f$ does not exist, otherwise, a pointer to the value of node \f$i\f$
constexpr const value_t& operator[](size_t i) const {
assertd(i < _table.size(), "Index out of bounds.");
return _table[i].value;
@@ -320,8 +320,8 @@ public:
/// @{
///
/// \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
/// \brief Move Left Insertion, constructs a new node as the left child of \f$p\f$
/// \details If the left node of \f$p\f$ 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
@@ -330,8 +330,8 @@ public:
}
///
/// \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
/// \brief Copy Left Insertion, constructs a new node as the left child of \f$p\f$
/// \details If the left node of \f$p\f$ 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
@@ -340,8 +340,8 @@ public:
}
///
/// \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
/// \brief Emplace Left Insertion, constructs a new node as the left child of \f$p\f$
/// \details If the left node of \f$p\f$ 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
@@ -351,8 +351,8 @@ public:
}
///
/// \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
/// \brief Move Right Insertion, constructs a new node as the right child of \f$p\f$
/// \details If the right node of \f$p\f$ 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
@@ -361,8 +361,8 @@ public:
}
///
/// \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
/// \brief Copy Right Insertion, constructs a new node as the right child of \f$p\f$
/// \details If the right node of \f$p\f$ 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
@@ -371,8 +371,8 @@ public:
}
///
/// \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
/// \brief Emplace Right Insertion, constructs a new node as the right child of \f$p\f$
/// \details If the right node of \f$p\f$ 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
@@ -384,9 +384,9 @@ public:
///
/// \brief Perform a Tree Rotation at `i` in the specified direction
/// \brief Perform a Tree Rotation at \f$i\f$ in the specified direction
/// \param sub The root node for the rotation
/// \param dir The direction to rotate, `true` for right, `false` for left
/// \param dir The direction to rotate, \f$true\f$ for right, \f$false\f$ for left
/// \returns the new root
constexpr size_t rotate(size_t sub, bool dir) {
if (sub == npos) {
@@ -413,8 +413,8 @@ public:
}
///
/// \brief Move Insertion, bool d, constructs a new node as the child of `p`
/// \details If the child of `p` already exists, the move assignment operator is used instead
/// \brief Move Insertion, bool d, constructs a new node as the child of \f$p\f$
/// \details If the child of \f$p\f$ already exists, the move assignment operator is used instead
/// \param p The parent node
/// \param d The direction to insert
/// \param val The object to move into the new node
@@ -424,8 +424,8 @@ public:
}
///
/// \brief Copy Insertion, bool d, constructs a new node as the child of `p`
/// \details If the child of `p` already exists, the copy assignment operator is used instead
/// \brief Copy Insertion, bool d, constructs a new node as the child of \f$p\f$
/// \details If the child of \f$p\f$ already exists, the copy assignment operator is used instead
/// \param p The parent node
/// \param d The direction to insert
/// \param val The object to copy to the new node
@@ -435,8 +435,8 @@ public:
}
///
/// \brief Emplace Insertion, constructs a new node as the child of `p`
/// \details If the child of `p` already exists, the move assignment operator is used instead
/// \brief Emplace Insertion, constructs a new node as the child of \f$p\f$
/// \details If the child of \f$p\f$ already exists, the move assignment operator is used instead
/// \param p The parent node
/// \param d The direction to insert
/// \param args The arguments to construct the new node with
@@ -482,7 +482,7 @@ public:
/// \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 accept a reference to a value of type \f$TypeT\f$ 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.
@@ -508,13 +508,21 @@ public:
///
/// \brief Traverser pattern for breadth-first traversal
struct breadth_first {
list<size_t> visit;
size_t head;
///
/// \brief Traversal Init
/// \param start The node in the tree to start at
/// \returns The first index of the specified order
size_t operator()(const bintree&, size_t start) {
return head = start;
}
///
/// \brief Traverser Step
/// \param tree The tree we are operating on
/// \param node The current node
/// \param mode The mode specifying branch conditions
/// \returns The next index according to the traversal order
size_t operator[](const bintree& tree, size_t node, uint8_t mode) {
if (node == npos) {
return npos;
@@ -541,19 +549,31 @@ public:
return node;
}
private:
list<size_t> visit;
size_t head;
};
///
/// \brief Traverser pattern for pre-order traversal
struct pre_order {
list<size_t> visit;
size_t head;
///
/// \brief Traversal Init
/// \param start The node in the tree to start at
/// \returns The first index of the specified order
constexpr size_t operator()(const bintree&, size_t start) {
head = start;
return start;
}
///
/// \brief Traverser Step
/// \param tree The tree we are operating on
/// \param node The current node
/// \param mode The mode specifying branch conditions
/// \returns The next index according to the traversal order
constexpr size_t operator[](const bintree& tree, size_t node, uint8_t mode) {
if (node == npos) {
return npos;
@@ -580,19 +600,31 @@ public:
return node;
}
private:
list<size_t> visit;
size_t head;
};
///
/// \brief Traverser pattern for in-order traversal
struct in_order {
list<size_t> visit;
size_t head;
///
/// \brief Traversal Init
/// \param tree The tree we are operating on
/// \param start The node in the tree to start at
/// \returns The first index of the specified order
constexpr size_t operator()(const bintree& tree, size_t start) {
head = start;
return tree.left_most(start);
}
///
/// \brief Traverser Step
/// \param tree The tree we are operating on
/// \param node The current node
/// \returns The next index according to the traversal order
constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
if (node == npos) {
return npos;
@@ -619,33 +651,31 @@ public:
return node;
}
private:
list<size_t> visit;
size_t head;
};
///
/// \brief Traverser pattern for post-order traversal
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;
}
///
/// \brief Traversal Init
/// \param tree The tree we are operating on
/// \param start The node in the tree to start at
/// \returns The first index of the specified order
constexpr size_t operator()(const bintree& tree, size_t start) {
head = start;
return this->successor(tree, start);
return this->_successor(tree, start);
}
///
/// \brief Traverser Step
/// \param tree The tree we are operating on
/// \param node The current node
/// \returns The next index according to the traversal order
constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
if (node == npos) {
return npos;
@@ -659,7 +689,7 @@ public:
visit.push_front(parent);
}
} else if (pright != npos) {
visit.push_front(this->successor(tree, pright));
visit.push_front(this->_successor(tree, pright));
}
if (not visit.empty()) {
@@ -672,23 +702,50 @@ public:
return node;
}
private:
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;
}
};
// Iterator ============================================================================================================
///
/// \brief C++ Iterator Specification `iterator`
/// \details Performs pre-order traversal
class iterator {
protected:
bintree* _tree;
in_order _order;
size_t _n;
public:
///
/// \brief bintree iterator constructor
/// \param tree A reference to the tree
/// \param root The root node for iteration
constexpr iterator(bintree* tree, size_t root)
: _tree(tree)
, _order()
, _n(_order(*tree, root)) {
}
///
/// \brief bintree iterator constructor
/// \param tree A reference to the tree
/// \param root The root node for iteration
/// \param node The node to jump to
constexpr iterator(bintree* tree, size_t root, size_t node)
: _tree(tree)
, _order()
@@ -696,42 +753,171 @@ public:
_order.head = root;
}
///
/// \returns The index of the current node
size_t index() const {
return _n;
}
///
/// \brief iterator pre-increment operator
/// \returns A reference to self after having stepped to the next node
iterator& operator++() {
return _n = _order[*_tree, _n, traversal_control_continue], *this;
}
///
/// \brief dereference operator
/// \returns the value held in the current node
value_t& operator*() {
return (*_tree)[_n];
}
value_t* operator->() {
return &(*_tree)[_n];
}
///
/// \returns the value held in the current node
const value_t& operator*() const {
return (*_tree)[_n];
}
///
/// \brief pointer access operator
/// \returns a reference to the value held in the current node
value_t* operator->() {
return &(*_tree)[_n];
}
///
/// \returns a reference to the value held in the current node
const value_t* operator->() const {
return &(*_tree)[_n];
}
///
/// \brief iterator equality operator
/// \param it the iterator to compare with
/// \returns \f$true\f$ if iterators are identical, \f$false\f$ otherwise
constexpr bool operator==(const iterator& it) {
return _tree == it._tree and _n == it._n;
}
///
/// \brief iterator inequality operator
/// \param it the iterator to compare with
/// \returns \f$true\f$ if iterators are different, \f$false\f$ otherwise
constexpr bool operator!=(const iterator& it) {
return _tree != it._tree or _n != it._n;
}
private:
bintree* _tree;
in_order _order;
size_t _n;
};
///
/// \brief C++ Iterator Specification `iterator`
/// \details Performs pre-order traversal
class const_iterator {
public:
///
/// \brief bintree iterator constructor
/// \param tree A reference to the tree
/// \param root The root node for iteration
constexpr const_iterator(bintree* tree, size_t root)
: _tree(tree)
, _order()
, _n(_order(*tree, root)) {
}
///
/// \brief bintree iterator constructor
/// \param tree A reference to the tree
/// \param root The root node for iteration
/// \param node The node to jump to
constexpr const_iterator(bintree* tree, size_t root, size_t node)
: _tree(tree)
, _order()
, _n(node) {
_order.head = root;
}
///
/// \returns The index of the current node
size_t index() const {
return _n;
}
///
/// \brief iterator pre-increment operator
/// \returns A reference to self after having stepped to the next node
const_iterator& operator++() {
return _n = _order[*_tree, _n, traversal_control_continue], *this;
}
///
/// \returns the value held in the current node
const value_t& operator*() const {
return (*_tree)[_n];
}
///
/// \returns a reference to the value held in the current node
const value_t* operator->() const {
return &(*_tree)[_n];
}
///
/// \brief iterator equality operator
/// \param it the iterator to compare with
/// \returns \f$true\f$ if iterators are identical, \f$false\f$ otherwise
constexpr bool operator==(const iterator& it) {
return _tree == it._tree and _n == it._n;
}
///
/// \brief iterator inequality operator
/// \param it the iterator to compare with
/// \returns \f$true\f$ if iterators are different, \f$false\f$ otherwise
constexpr bool operator!=(const iterator& it) {
return _tree != it._tree or _n != it._n;
}
private:
bintree* _tree;
in_order _order;
size_t _n;
};
///
/// \returns an iterator at the first element in pre-order traversal
iterator begin() {
return iterator(this, _root);
}
///
/// \brief C++ Iterator Specification `begin()`
/// \returns an iterator at the first element in pre-order traversal
const_iterator begin() const {
return iterator(this, _root);
}
///
/// \returns an iterator at the first element in pre-order traversal
iterator end() {
return iterator(this, _root, nullid);
}
///
/// \brief C++ Iterator Specification `begin()`
/// \returns an iterator at the first element in pre-order traversal
const_iterator end() const {
return iterator(this, _root, nullid);
}
// Fields ==============================================================================================================
protected:
private:
table_t _table;
freed_t _freed;
size_t _root, _size;