Compare commits
2 Commits
992a02db3e
...
375492ef7b
| Author | SHA1 | Date | |
|---|---|---|---|
| 375492ef7b | |||
| dbcb50349d |
@@ -105,6 +105,7 @@ add_library(fennec STATIC
|
||||
include/fennec/containers/containers.h
|
||||
|
||||
include/fennec/containers/array.h
|
||||
include/fennec/containers/bintree.h
|
||||
include/fennec/containers/deque.h
|
||||
include/fennec/containers/dynarray.h
|
||||
include/fennec/containers/graph.h
|
||||
@@ -114,6 +115,7 @@ add_library(fennec STATIC
|
||||
include/fennec/containers/optional.h
|
||||
include/fennec/containers/pair.h
|
||||
include/fennec/containers/rdtree.h
|
||||
include/fennec/containers/sequence.h
|
||||
include/fennec/containers/set.h
|
||||
include/fennec/containers/traversal.h
|
||||
include/fennec/containers/tuple.h
|
||||
@@ -235,7 +237,7 @@ add_library(fennec STATIC
|
||||
# EXTRA SOURCES ========================================================================================================
|
||||
|
||||
${FENNEC_EXTRA_SOURCES}
|
||||
include/fennec/containers/bintree.h
|
||||
include/fennec/renderers/interface/gfxresourcepool.h
|
||||
)
|
||||
|
||||
add_dependencies(fennec metaprogramming fennec-dependencies)
|
||||
|
||||
@@ -371,7 +371,7 @@ class RDTreePrinter:
|
||||
index += '└'
|
||||
|
||||
index += '─'
|
||||
index += '[{}, {}]'.format(node, i)
|
||||
index += '[{}]'.format(node)
|
||||
return index, value
|
||||
|
||||
|
||||
@@ -389,6 +389,66 @@ class RDTreePrinter:
|
||||
return self.Iterator(self.tree, 0, self.capacity)
|
||||
|
||||
|
||||
# BINTREE ==============================================================================================================
|
||||
|
||||
class BinTreePrinter:
|
||||
"""Print a fennec::bintree"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, tree, node, capacity):
|
||||
self.tree = tree
|
||||
self.capacity = capacity
|
||||
self.visit = deque()
|
||||
|
||||
self.visit.append((node, 0, 0))
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if len(self.visit) == 0:
|
||||
raise StopIteration
|
||||
|
||||
node = self.visit[0][0]
|
||||
i = self.visit[0][1]
|
||||
depth = self.visit[0][2]
|
||||
self.visit.popleft()
|
||||
|
||||
value = self.tree[node]['value']
|
||||
left = self.tree[node]['left']
|
||||
right = self.tree[node]['right']
|
||||
|
||||
if right < self.capacity:
|
||||
self.visit.appendleft((right, 1, depth + 1))
|
||||
if left < self.capacity:
|
||||
self.visit.appendleft((left, 0, depth + 1))
|
||||
|
||||
index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
|
||||
if i == 0:
|
||||
index += '├'
|
||||
else:
|
||||
index += '└'
|
||||
|
||||
index += '─'
|
||||
index += '[{}]'.format(node)
|
||||
return index, value
|
||||
|
||||
|
||||
def __init__(self, val):
|
||||
self.tree = val['_table']['_data']
|
||||
self.size = val['_size']
|
||||
self.root = val['_root']
|
||||
self.capacity = val['_table']['_capacity']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
return "{ empty }"
|
||||
return "{ size = " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.tree, self.root, self.capacity)
|
||||
|
||||
|
||||
# Graph ================================================================================================================
|
||||
|
||||
class GraphPrinter:
|
||||
@@ -475,6 +535,8 @@ def register_printers():
|
||||
pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter)
|
||||
pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter)
|
||||
pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter)
|
||||
pp.add_printer('fennec::bintree', '^fennec::bintree<.*>$', BinTreePrinter)
|
||||
pp.add_printer('fennec::sequence', '^fennec::sequence<.*>$', BinTreePrinter)
|
||||
pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
|
||||
return pp
|
||||
|
||||
|
||||
@@ -34,30 +34,38 @@
|
||||
#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
|
||||
{
|
||||
|
||||
template<typename TypeT, class AllocT>
|
||||
///
|
||||
/// \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 =========================================================================================================
|
||||
// Definitions =========================================================================================================
|
||||
protected:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
using value_t = TypeT;
|
||||
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
|
||||
static constexpr size_t root = 0;
|
||||
static constexpr size_t npos = -1;
|
||||
inline static size_t sink = npos;
|
||||
|
||||
friend class iterator;
|
||||
friend class const_iterator;
|
||||
|
||||
protected:
|
||||
struct node {
|
||||
optional<value_t> value;
|
||||
size_t parent, left, right;
|
||||
size_t depth;
|
||||
value_t value;
|
||||
size_t parent, left, right;
|
||||
size_t depth;
|
||||
|
||||
constexpr node()
|
||||
: value(nullopt)
|
||||
@@ -78,23 +86,118 @@ protected:
|
||||
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 {
|
||||
if (i >= _table.size()) {
|
||||
return false;
|
||||
}
|
||||
return _table[i].parent;
|
||||
return i >= _table.size() ? 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));
|
||||
}
|
||||
|
||||
///
|
||||
@@ -102,10 +205,7 @@ public:
|
||||
/// \param i The node id
|
||||
/// \returns The left child of node `i`
|
||||
constexpr size_t left(size_t i) const {
|
||||
if (i >= _table.size()) {
|
||||
return false;
|
||||
}
|
||||
return _table[i].left;
|
||||
return i >= _table.size() ? npos : _table[i].left;
|
||||
}
|
||||
|
||||
///
|
||||
@@ -113,10 +213,47 @@ public:
|
||||
/// \param i The node id
|
||||
/// \returns The right child of node `i`
|
||||
constexpr size_t right(size_t i) const {
|
||||
if (i >= _table.size()) {
|
||||
return i >= _table.size() ? 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 {
|
||||
size_t p = parent(i);
|
||||
if (p >= _table.capacity()) {
|
||||
return false;
|
||||
}
|
||||
return _table[i].right;
|
||||
return i == right(p);
|
||||
}
|
||||
|
||||
///
|
||||
/// \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 ? l : r;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Short for "Parent Sibling," \f$O(1)\f$
|
||||
/// \param i The id of the node
|
||||
/// \returns The id of the parents' sibling of `i`
|
||||
constexpr size_t parsib(size_t i) const {
|
||||
return sibling(parent(i));
|
||||
}
|
||||
|
||||
///
|
||||
@@ -124,14 +261,45 @@ public:
|
||||
/// \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;
|
||||
}
|
||||
return _table[i].depth;
|
||||
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$
|
||||
@@ -155,8 +323,13 @@ public:
|
||||
return _table[i] ? &*_table[i] : nullptr;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// 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
|
||||
@@ -174,11 +347,11 @@ public:
|
||||
/// \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);
|
||||
return this->_insert_left(p, val);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Left Insertion, constructs a new node as the left child of `p`
|
||||
/// \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
|
||||
@@ -188,6 +361,37 @@ public:
|
||||
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
|
||||
@@ -242,55 +446,379 @@ public:
|
||||
_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;
|
||||
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 = root) {
|
||||
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.sibling(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 prnt = tree.parent(node);
|
||||
size_t next = tree.sibling(node);
|
||||
next = node == next ? npos : next;
|
||||
|
||||
if (node != head) {
|
||||
if (tree.left(prnt) == node) {
|
||||
visit.push_back(prnt);
|
||||
if (next != npos) {
|
||||
visit.push_back(tree.left_most(next));
|
||||
}
|
||||
} else if (next != npos) {
|
||||
visit.push_front(tree.left_most(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 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 prnt = tree.parent(node);
|
||||
size_t next = tree.sibling(node);
|
||||
next = node == next ? npos : next;
|
||||
|
||||
if (node != head) {
|
||||
if (next != npos) {
|
||||
visit.push_front(tree.left_most(next));
|
||||
} else {
|
||||
visit.push_front(prnt);
|
||||
}
|
||||
}
|
||||
|
||||
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, 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;
|
||||
|
||||
constexpr void _next_free() {
|
||||
// Helpers =============================================================================================================
|
||||
|
||||
constexpr size_t _next_free() {
|
||||
size_t i = _size;
|
||||
if (not _freed.empty()) {
|
||||
i = _freed.front();
|
||||
_freed.pop_front();
|
||||
}
|
||||
if (i >= _table.size()) {
|
||||
_table.reallocate(2 * fennec::max(_table.size(), 4));
|
||||
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 = left(p);
|
||||
if (i == npos) {
|
||||
i = _next_free();
|
||||
_table[i].value.emplace(fennec::forward<ArgsT>(args)...);
|
||||
size_t i = p == npos ? _root : left(p);
|
||||
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;
|
||||
}
|
||||
fennec::construct(&_table[i], p, npos, npos, d);
|
||||
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 = right(p);
|
||||
if (i == npos) {
|
||||
i = _next_free();
|
||||
_table[i].value.emplace(fennec::forward<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;
|
||||
}
|
||||
fennec::construct(&_table[i], p, npos, npos, d);
|
||||
fennec::construct(&_table[i], p, npos, npos, d, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
constexpr size_t& parent(size_t i) {
|
||||
return i >= _table.size() ? sink : _table[i].parent;
|
||||
}
|
||||
|
||||
constexpr size_t& grandparent(size_t i) {
|
||||
return parent(parent(i));
|
||||
}
|
||||
|
||||
constexpr size_t& left(size_t i) {
|
||||
return i >= _table.size() ? sink : _table[i].left;
|
||||
}
|
||||
|
||||
constexpr size_t& right(size_t i) {
|
||||
return i >= _table.size() ? sink : _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;
|
||||
}
|
||||
|
||||
constexpr size_t& parsib(size_t i) {
|
||||
return sibling(parent(i));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace fennec::detail
|
||||
{
|
||||
|
||||
|
||||
template <std::size_t I, typename T>
|
||||
template <size_t I, typename T>
|
||||
struct _tuple_leaf
|
||||
{
|
||||
template <typename ArgT>
|
||||
|
||||
@@ -140,6 +140,7 @@ public:
|
||||
for (const value_t& it : l) {
|
||||
this->push_back(it);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
@@ -152,6 +153,7 @@ public:
|
||||
_freed = fennec::move(l._freed);
|
||||
_root = l._root; _last = l._last;
|
||||
_size = l._size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
@@ -164,15 +166,21 @@ public:
|
||||
|
||||
///
|
||||
/// \returns The size of the list in elements.
|
||||
constexpr size_t size() const { return _size; }
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The capacity of the list in elements.
|
||||
constexpr size_t capacity() const { return _table.capacity(); }
|
||||
constexpr size_t capacity() const {
|
||||
return _table.capacity();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns `true` when the list is empty, `false` otherwise.
|
||||
constexpr bool empty() const { return _root == npos; }
|
||||
constexpr bool empty() const {
|
||||
return _root == npos;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
@@ -95,19 +95,33 @@ struct pair {
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, copies both elements
|
||||
constexpr pair(const pair&) = default;
|
||||
constexpr pair(const pair& pair)
|
||||
: first(fennec::copy(pair.first))
|
||||
, second(fennec::copy(pair.second)) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, moves both elements
|
||||
constexpr pair(pair&&) noexcept = default;
|
||||
constexpr pair(pair&& pair) noexcept
|
||||
: first(fennec::move(pair.first))
|
||||
, second(fennec::move(pair.second)) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment, copies both elements
|
||||
constexpr pair& operator=(const pair&) = default;
|
||||
constexpr pair& operator=(const pair& pair) {
|
||||
first = fennec::copy(pair.first);
|
||||
second = fennec::copy(pair.second);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment, moves both elements
|
||||
constexpr pair& operator=(pair&&) noexcept = default;
|
||||
constexpr pair& operator=(pair&& pair) {
|
||||
first = fennec::move(pair.first);
|
||||
second = fennec::move(pair.second);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
@@ -414,7 +414,7 @@ public:
|
||||
/// \param visit The visiting object
|
||||
/// \param i The node to start at
|
||||
template<typename OrderT, typename VisitorT>
|
||||
void traverse(VisitorT&& visit, size_t i = root) {
|
||||
constexpr void traverse(VisitorT&& visit, size_t i = root) {
|
||||
OrderT order;
|
||||
i = order(*this, i);
|
||||
while (i != npos) {
|
||||
@@ -429,16 +429,52 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
struct pre_order {
|
||||
struct breadth_first {
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
size_t head;
|
||||
|
||||
size_t operator()(const rdtree&, size_t start) {
|
||||
constexpr size_t operator()(const rdtree&, size_t start) {
|
||||
head = start;
|
||||
return start;
|
||||
}
|
||||
|
||||
size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
|
||||
size_t nxt = tree.next(node);
|
||||
size_t chd = tree.next(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 rdtree&, size_t start) {
|
||||
head = start;
|
||||
return start;
|
||||
}
|
||||
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
@@ -469,12 +505,12 @@ public:
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
|
||||
size_t operator()(const rdtree& tree, size_t start) {
|
||||
constexpr size_t operator()(const rdtree& tree, size_t start) {
|
||||
head = start;
|
||||
return tree.left_most(start);
|
||||
}
|
||||
|
||||
size_t operator[](const rdtree& tree, size_t node, uint8_t) {
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
@@ -507,12 +543,12 @@ public:
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
|
||||
size_t operator()(const rdtree& tree, size_t start) {
|
||||
constexpr size_t operator()(const rdtree& tree, size_t start) {
|
||||
head = start;
|
||||
return tree.left_most(start);
|
||||
}
|
||||
|
||||
size_t operator[](const rdtree& tree, size_t node, uint8_t) {
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
|
||||
455
include/fennec/containers/sequence.h
Normal file
455
include/fennec/containers/sequence.h
Normal file
@@ -0,0 +1,455 @@
|
||||
// =====================================================================================================================
|
||||
// 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 sequence.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_SEQUENCE_H
|
||||
#define FENNEC_CONTAINERS_SEQUENCE_H
|
||||
#include <fennec/containers/bintree.h>
|
||||
#include <fennec/containers/bintree.h>
|
||||
#include <fennec/containers/pair.h>
|
||||
#include <fennec/containers/sequence.h>
|
||||
#include <fennec/containers/sequence.h>
|
||||
#include <fennec/containers/sequence.h>
|
||||
#include <fennec/lang/compare.h>
|
||||
#include <fennec/memory/allocator.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief wrapper for ordered sets of elements, called sequences in mathematics
|
||||
/// \details
|
||||
/// This data-structure behaves like an ordered-set, but does not use pointers, instead storing the table in-array
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:----------:|:---------------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogenous | ✅ |
|
||||
/// | distinct | ✅ |
|
||||
/// | ordered | ✅ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(\log N)\f$ |
|
||||
/// | find | \f$O(\log N)\f$ |
|
||||
/// | insertion | \f$O(\log N)\f$ |
|
||||
/// | deletion | \f$O(\log N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT The type to contain
|
||||
/// \tparam CompareT Function for comparing two values
|
||||
/// \tparam AllocT An allocator class
|
||||
template<typename TypeT, typename CompareT = less<TypeT>, class AllocT = allocator<pair<TypeT, bool>>>
|
||||
struct sequence : protected bintree<pair<TypeT, bool>, AllocT> {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
protected:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
using value_t = TypeT;
|
||||
using node_t = pair<TypeT, bool>;
|
||||
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
|
||||
using base_t = bintree<pair<TypeT, bool>, AllocT>;
|
||||
using compare_t = CompareT;
|
||||
static constexpr size_t npos = -1;
|
||||
|
||||
enum color_ : bool {
|
||||
black = false,
|
||||
red = true,
|
||||
};
|
||||
|
||||
class iterator;
|
||||
|
||||
protected:
|
||||
using typename base_t::in_order;
|
||||
|
||||
using base_t::left;
|
||||
using base_t::right;
|
||||
using base_t::child;
|
||||
using base_t::direction;
|
||||
using base_t::parent;
|
||||
using base_t::grandparent;
|
||||
using base_t::sibling;
|
||||
using base_t::parsib;
|
||||
|
||||
using base_t::left_most;
|
||||
using base_t::right_most;
|
||||
|
||||
using base_t::insert_left;
|
||||
using base_t::insert_right;
|
||||
|
||||
using base_t::rotate;
|
||||
using base_t::rotate_left;
|
||||
using base_t::rotate_right;
|
||||
|
||||
using base_t::_table;
|
||||
using base_t::_freed;
|
||||
using base_t::_root;
|
||||
using base_t::_size;
|
||||
|
||||
// Constructors & Destructors ==========================================================================================
|
||||
public:
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty sequence
|
||||
constexpr sequence() = default;
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, takes ownership of a sequence
|
||||
constexpr sequence(sequence&&) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, copies a sequence
|
||||
constexpr sequence(const sequence&) = default;
|
||||
|
||||
///
|
||||
/// \brief Default Destructor, destructs elements *in-order*
|
||||
constexpr ~sequence() {
|
||||
this->clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Search ==============================================================================================================
|
||||
public:
|
||||
/// \name Search
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Value Find Function, finds the iterator position for `val`, otherwise returns `end()`
|
||||
/// \param val The value to find
|
||||
/// \returns An iterator at the value
|
||||
constexpr iterator find(const value_t& val) {
|
||||
size_t node = _root;
|
||||
while (node != npos) {
|
||||
if (_compare(val, _value(node))) {
|
||||
node = left(node);
|
||||
} else if (_compare(_value(node), val)) {
|
||||
node = right(node);
|
||||
} else {
|
||||
return sequence::iterator(this, _root, node);
|
||||
}
|
||||
}
|
||||
return sequence::iterator(this, _root, node);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Value Contains Function, checks if the sequence contains a value
|
||||
/// \param val The value to find
|
||||
/// \returns `true` if `val` is in the sequence, `false` otherwise
|
||||
bool contains(const value_t& val) {
|
||||
return find(val) != end();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The number of elements in the sequence
|
||||
using base_t::size;
|
||||
|
||||
///
|
||||
/// \returns The capacity of the underlying allocation
|
||||
using base_t::capacity;
|
||||
|
||||
///
|
||||
/// \returns `true` when there are no elements in the sequence, `false` otherwise.
|
||||
using base_t::empty;
|
||||
|
||||
/// @}
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Move Insertion, moves `val` into the sequence
|
||||
/// \param val The value to insert
|
||||
constexpr void insert(value_t&& val) {
|
||||
size_t i = _insert_bst(fennec::forward<value_t>(val));
|
||||
_fix_insert(i);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion, inserts a copy of `val` into the sequence
|
||||
/// \param val The value to insert
|
||||
constexpr void insert(const value_t& val) {
|
||||
size_t i = _insert_bst(val);
|
||||
_fix_insert(i);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplacement, constructs and adds a value into the sequence
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The arguments to construct with
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
size_t i = _insert_bst(fennec::forward<ArgsT>(args)...);
|
||||
_fix_insert(i);
|
||||
}
|
||||
|
||||
constexpr void erase(const value_t& val) {
|
||||
_erase_bst(val);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructs all elements, *in-order*, contained in the sequence
|
||||
constexpr void clear() {
|
||||
in_order order;
|
||||
size_t node = order(*this, _root);
|
||||
while (node != npos) {
|
||||
size_t erase = node;
|
||||
node = order[*this, node, traversal_control_continue];
|
||||
fennec::destruct(&_table[erase]);
|
||||
}
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Iterator ============================================================================================================
|
||||
|
||||
///
|
||||
/// \returns An iterator at the smallest element in the sequence
|
||||
constexpr sequence::iterator begin() {
|
||||
return sequence::iterator(this, _root, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns An iterator after the largest element in the sequence
|
||||
constexpr sequence::iterator end() {
|
||||
return sequence::iterator(this, _root, npos);
|
||||
}
|
||||
|
||||
class iterator : public base_t::iterator {
|
||||
protected:
|
||||
using base_t::iterator::_n;
|
||||
|
||||
|
||||
public:
|
||||
using base_t::iterator::iterator;
|
||||
|
||||
value_t& operator*() {
|
||||
return _table[_n].value.second;
|
||||
}
|
||||
|
||||
const value_t& operator*() const {
|
||||
return _table[_n].value.second;
|
||||
}
|
||||
|
||||
value_t* operator->() {
|
||||
return &_table[_n].value.second;
|
||||
}
|
||||
|
||||
const value_t* operator->() const {
|
||||
return &_table[_n].value.second;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Fields ==============================================================================================================
|
||||
protected:
|
||||
compare_t _compare;
|
||||
inline static bool color_sink = red;
|
||||
inline static value_t value_sink;
|
||||
|
||||
// Helpers =============================================================================================================
|
||||
protected:
|
||||
constexpr value_t& _value(size_t i) {
|
||||
return i >= _table.capacity() ? value_sink : _table[i].value.first;
|
||||
}
|
||||
|
||||
constexpr const value_t& _value(size_t i) const {
|
||||
return i >= _table.capacity() ? value_sink : _table[i].value.first;
|
||||
}
|
||||
|
||||
constexpr bool& _color(size_t i) {
|
||||
return i >= _table.capacity() ? color_sink = false : _table[i].value.second;
|
||||
}
|
||||
|
||||
constexpr bool _color(size_t i) const {
|
||||
return i >= _table.capacity() ? color_sink = false : _table[i].value.second;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t _insert_bst(ArgsT&&...args) {
|
||||
value_t val(fennec::forward<ArgsT>(args)...);
|
||||
|
||||
size_t i = _root;
|
||||
size_t p = npos;
|
||||
while (i != npos) {
|
||||
p = i;
|
||||
|
||||
if (_compare(val, _value(i))) {
|
||||
i = left(i);
|
||||
} else if (_compare(_value(i), val)) {
|
||||
i = right(i);
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (_root == npos) {
|
||||
return _root = insert_left(npos, node_t(fennec::move(val), red));
|
||||
}
|
||||
|
||||
if (_compare(val, _value(p))) {
|
||||
return insert_left(p, node_t(fennec::move(val), red));
|
||||
} else {
|
||||
return insert_right(p, node_t(fennec::move(val), red));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void _fix_insert(size_t x) {
|
||||
while (x != _root && _color(parent(x)) == red) {
|
||||
if (_color(parsib(x)) == red) {
|
||||
_color(parent(x)) = black;
|
||||
_color(parsib(x)) = black;
|
||||
_color(grandparent(x)) = red;
|
||||
x = grandparent(x);
|
||||
} else if (parent(x) == left(grandparent(x))) {
|
||||
if (x == right(parent(x))) {
|
||||
x = parent(x);
|
||||
rotate_left(x);
|
||||
}
|
||||
_color(parent(x)) = black;
|
||||
_color(grandparent(x)) = red;
|
||||
rotate_right(grandparent(x));
|
||||
} else {
|
||||
if (x == left(parent(x))) {
|
||||
x = parent(x);
|
||||
rotate_right(x);
|
||||
}
|
||||
_color(parent(x)) = black;
|
||||
_color(grandparent(x)) = red;
|
||||
rotate_left(grandparent(x));
|
||||
}
|
||||
}
|
||||
_color(_root) = black;
|
||||
}
|
||||
|
||||
constexpr void _shift(size_t u, size_t v) {
|
||||
if (parent(u) == npos) {
|
||||
_root = v;
|
||||
} else {
|
||||
child(parent(u), direction(u)) = v;
|
||||
}
|
||||
if (v != npos) {
|
||||
parent(v) = parent(u);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void _erase_bst(const value_t& val) {
|
||||
size_t z = find(val).index();
|
||||
size_t y = z;
|
||||
size_t x = npos;
|
||||
bool c = _color(y);
|
||||
size_t p = npos;
|
||||
|
||||
if (left(z) == npos) {
|
||||
x = right(z);
|
||||
p = parent(z);
|
||||
_shift(z, x);
|
||||
} else if (right(z) == npos) {
|
||||
x = left(z);
|
||||
p = parent(z);
|
||||
_shift(z, x);
|
||||
} else {
|
||||
y = left_most(right(z));
|
||||
c = _color(y);
|
||||
x = right(y);
|
||||
p = (parent(y) == z) ? y : parent(y);
|
||||
if (parent(y) != z) {
|
||||
_shift(y, right(y));
|
||||
right(y) = right(z);
|
||||
parent(right(y)) = y;
|
||||
}
|
||||
_shift(z, y);
|
||||
left(y) = left(z);
|
||||
if (left(y))
|
||||
parent(left(y)) = y;
|
||||
_color(y) = _color(z);
|
||||
}
|
||||
|
||||
fennec::destruct(&_table[z]);
|
||||
--_size;
|
||||
|
||||
if (c == black) {
|
||||
_fix_erase(x, p);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void _fix_erase(size_t x, size_t p) {
|
||||
while (x != _root && _color(x) == black) {
|
||||
bool dir = direction(x);
|
||||
size_t w = child(p, not dir);
|
||||
|
||||
if (_color(w) == red) {
|
||||
_color(w) = black;
|
||||
_color(p) = red;
|
||||
w = rotate(p, dir);
|
||||
}
|
||||
|
||||
if (w == npos || (_color(left(w)) == black && _color(right(w)) == black)) {
|
||||
_color(w) = red;
|
||||
x = p;
|
||||
p = parent(x);
|
||||
} else {
|
||||
if (_color(child(w, not dir)) == black) {
|
||||
_color(child(w, dir)) = black;
|
||||
_color(w) = red;
|
||||
rotate(w, not dir);
|
||||
w = child(p, not dir);
|
||||
}
|
||||
|
||||
_color(w) = _color(p);
|
||||
_color(p) = black;
|
||||
_color(child(w, not dir)) = black;
|
||||
rotate(p, dir);
|
||||
x = _root;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_color(x) = black;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_SEQUENCE_H
|
||||
@@ -20,6 +20,7 @@
|
||||
#define FENNEC_PLATFORM_INTERFACE_PLATFORM_H
|
||||
|
||||
#include <fennec/containers/list.h>
|
||||
#include <fennec/containers/sequence.h>
|
||||
#include <fennec/langproc/strings/cstring.h>
|
||||
#include <fennec/langproc/strings/string.h>
|
||||
#include <fennec/lang/typed.h>
|
||||
@@ -69,11 +70,48 @@ public:
|
||||
struct driver {
|
||||
int priority;
|
||||
ctor constructor;
|
||||
|
||||
driver()
|
||||
: priority(0)
|
||||
, constructor(nullptr) {
|
||||
}
|
||||
|
||||
driver(int priority, ctor constructor)
|
||||
: priority(priority)
|
||||
, constructor(constructor) {
|
||||
}
|
||||
|
||||
driver(const driver& d)
|
||||
: priority(d.priority)
|
||||
, constructor(d.constructor) {
|
||||
}
|
||||
|
||||
driver(driver&& d) noexcept
|
||||
: priority(d.priority)
|
||||
, constructor(d.constructor) {
|
||||
}
|
||||
|
||||
driver& operator=(const driver& d) {
|
||||
priority = d.priority;
|
||||
constructor = d.constructor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
driver& operator=(driver&& d) noexcept {
|
||||
priority = fennec::move(d.priority);
|
||||
constructor = fennec::move(d.constructor);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator<(const driver& d) const {
|
||||
return priority > d.priority;
|
||||
}
|
||||
};
|
||||
|
||||
const string name;
|
||||
|
||||
virtual ~platform() = default;
|
||||
platform(const platform&) = delete;
|
||||
|
||||
// Dynamically linked objects
|
||||
virtual shared_object* load_object(const cstring& file) = 0;
|
||||
@@ -96,22 +134,27 @@ protected:
|
||||
explicit platform(const cstring& name, PlatformT* type)
|
||||
: typed(type)
|
||||
, name(name) {
|
||||
assertf(singleton == nullptr, "Conflicting platform implementations!");
|
||||
singleton = this;
|
||||
assertf(globals.singleton == nullptr, "Conflicting platform implementations!");
|
||||
globals.singleton = this;
|
||||
}
|
||||
|
||||
private:
|
||||
platform(const platform&) = delete;
|
||||
|
||||
// Static Stuff ========================================================================================================
|
||||
|
||||
public:
|
||||
static platform* instance() {
|
||||
return singleton;
|
||||
return globals.singleton;
|
||||
}
|
||||
|
||||
private:
|
||||
static platform* singleton;
|
||||
inline static struct global {
|
||||
platform* singleton;
|
||||
sequence<window_driver> windows;
|
||||
|
||||
global()
|
||||
: singleton(nullptr)
|
||||
, windows() {
|
||||
}
|
||||
} globals = global();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ namespace fennec
|
||||
class unix_platform : public platform {
|
||||
public:
|
||||
template<typename PlatformT>
|
||||
explicit unix_platform(const cstring& name, PlatformT*)
|
||||
: platform(name, (PlatformT*)(nullptr)) {
|
||||
explicit unix_platform(const cstring& name, PlatformT* type)
|
||||
: platform(name, type) {
|
||||
}
|
||||
|
||||
shared_object* load_object(const cstring& file) override;
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
class renderer; // Overarching rendering scheme
|
||||
class gfxpass; // Overarching rendering scheme
|
||||
class gfxcontext; // Globals for an API's context
|
||||
class gfxresourcepool; // Handles resource creation, allocation, and mapping
|
||||
class gfxsubpass; // A subpass of the renderer, implements, for example, the steps of composing and lighting a deferred renderer
|
||||
|
||||
@@ -30,25 +30,45 @@
|
||||
|
||||
#ifndef FENNEC_RENDERERS_INTERFACE_GFXCONTEXT_H
|
||||
#define FENNEC_RENDERERS_INTERFACE_GFXCONTEXT_H
|
||||
#include <fennec/renderers/interface/renderer.h>
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/langproc/strings/string.h>
|
||||
#include <fennec/renderers/interface/forward.h>
|
||||
#include <fennec/renderers/interface/gfxresourcepool.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
class gfxcontext {
|
||||
public:
|
||||
enum texture_ : uint8_t {
|
||||
texture_1d = 0,
|
||||
texture_1d_array,
|
||||
texture_2d,
|
||||
texture_2d_array,
|
||||
texture_multisample,
|
||||
texture_multisample_array,
|
||||
texture_cubemap,
|
||||
texture_cubemap_array,
|
||||
texture_3d,
|
||||
};
|
||||
|
||||
using handle_t = uint32_t;
|
||||
|
||||
struct version_t {
|
||||
uint8_t major, minor, patch;
|
||||
string str;
|
||||
};
|
||||
|
||||
const version_t version;
|
||||
gfxresourcepool resources;
|
||||
|
||||
gfxcontext& operator=(const gfxcontext&) = delete;
|
||||
gfxcontext& operator=(gfxcontext&&) = delete;
|
||||
|
||||
renderer* get_renderer() {
|
||||
return _renderer;
|
||||
}
|
||||
virtual gfxpass* create_pass() = 0;
|
||||
|
||||
protected:
|
||||
renderer* _renderer;
|
||||
|
||||
gfxcontext(renderer* renderer);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
@@ -43,33 +43,8 @@ namespace fennec
|
||||
/// \details This represents the overarching renderer, and not any platform specific behaviour.
|
||||
/// I.E. This acts as a proxy for creating API objects, but the behaviour of those objects
|
||||
/// is defined elsewhere.
|
||||
class renderer {
|
||||
class gfxpass {
|
||||
public:
|
||||
enum texture_ : uint8_t {
|
||||
texture_1d = 0,
|
||||
texture_1d_array,
|
||||
texture_2d,
|
||||
texture_2d_array,
|
||||
texture_multisample,
|
||||
texture_multisample_array,
|
||||
texture_cubemap,
|
||||
texture_cubemap_array,
|
||||
texture_3d,
|
||||
};
|
||||
|
||||
using handle_t = uint32_t;
|
||||
|
||||
struct version_t {
|
||||
uint8_t major, minor, patch;
|
||||
string str;
|
||||
};
|
||||
|
||||
const version_t version;
|
||||
|
||||
gfxcontext context;
|
||||
gfxresourcepool resources;
|
||||
|
||||
handle_t add_subpass();
|
||||
|
||||
private:
|
||||
|
||||
43
include/fennec/renderers/interface/gfxresourcepool.h
Normal file
43
include/fennec/renderers/interface/gfxresourcepool.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// =====================================================================================================================
|
||||
// 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 gfxresourcepool.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_RENDERERS_INTERFACE_GFXRESOURCEPOOL_H
|
||||
#define FENNEC_RENDERERS_INTERFACE_GFXRESOURCEPOOL_H
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
class gfxresourcepool {
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_RENDERERS_INTERFACE_GFXRESOURCEPOOL_H
|
||||
@@ -41,8 +41,12 @@ void platform::initialize() {
|
||||
void platform::shutdown() {
|
||||
}
|
||||
|
||||
window* platform::create_window(const window::config& config) {
|
||||
|
||||
window* platform::create_window(const window::config&) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
void platform::register_window_driver(window_driver&& driver) {
|
||||
globals.windows.insert(fennec::forward<window_driver>(driver));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ set(CMAKE_C_STANDARD 23)
|
||||
add_executable(fennec-test
|
||||
main.cpp
|
||||
tests/containers/performance/test_iterator_visitor.h
|
||||
tests/containers/test_sequence.h
|
||||
)
|
||||
|
||||
target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}"
|
||||
|
||||
64
test/tests/containers/test_sequence.h
Normal file
64
test/tests/containers/test_sequence.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// =====================================================================================================================
|
||||
// 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 test_sequence.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_TEST_CONTAINERS_SEQUENCE_H
|
||||
#define FENNEC_TEST_CONTAINERS_SEQUENCE_H
|
||||
#include <fennec/containers/sequence.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
namespace test
|
||||
{
|
||||
|
||||
inline void fennec_test_containers_sequence() {
|
||||
dynarray<size_t> ref;
|
||||
sequence<size_t> test;
|
||||
const size_t n = 50;
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
size_t v = rand();
|
||||
ref.push_back(v);
|
||||
test.insert(v);
|
||||
}
|
||||
|
||||
for (size_t v : ref) {
|
||||
assertf(test.contains(v), "Failed Sequence Test!");
|
||||
test.erase(v);
|
||||
}
|
||||
|
||||
fennec_test_run(test.empty(), true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_TEST_CONTAINERS_SEQUENCE_H
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "containers/test_object_pool.h"
|
||||
#include "containers/test_optional.h"
|
||||
#include "containers/test_rdtree.h"
|
||||
#include "containers/test_sequence.h"
|
||||
#include "containers/test_set.h"
|
||||
#include "containers/test_tuple.h"
|
||||
|
||||
@@ -71,6 +72,11 @@ namespace fennec::test
|
||||
fennec_test_containers_set();
|
||||
fennec_test_spacer(3);
|
||||
|
||||
fennec_test_subheader("sequence tests");
|
||||
fennec_test_spacer(2);
|
||||
fennec_test_containers_sequence();
|
||||
fennec_test_spacer(3);
|
||||
|
||||
fennec_test_subheader("map tests");
|
||||
fennec_test_spacer(2);
|
||||
fennec_test_containers_map();
|
||||
|
||||
Reference in New Issue
Block a user