Reworked RD-Tree to behave more consistently. The construction of the tree did not allow specifying what index to insert a child at under a parent.

Traverser orders were also broken, which is now fixed.
This commit is contained in:
2025-08-14 17:07:48 -04:00
parent f173c3e7cd
commit 2535e1ac4b
3 changed files with 171 additions and 51 deletions

View File

@@ -171,6 +171,7 @@ class RDTreePrinter:
nprev = self.tree[node]['prev'] nprev = self.tree[node]['prev']
nprevc = self.tree[nprev]['child'] if nprev != 18446744073709551615 else 18446744073709551615 nprevc = self.tree[nprev]['child'] if nprev != 18446744073709551615 else 18446744073709551615
child = self.tree[node]['child'] child = self.tree[node]['child']
n_chld = self.tree[node]['num_children']
index = '' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers index = '' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
@@ -188,7 +189,7 @@ class RDTreePrinter:
index += '' index += ''
index += '' index += ''
index += '[{}, {}]'.format(i, node) index += '[{}, {}, {}, {}]'.format(i, node, depth, n_chld)
print(index) print(index)
if value is None: if value is None:
return index, '{ empty }' return index, '{ empty }'

View File

@@ -44,21 +44,17 @@ public:
static constexpr size_t root = 0; static constexpr size_t root = 0;
static constexpr size_t npos = -1; static constexpr size_t npos = -1;
enum control_ : uint8_t {
control_continue = 0,
control_jump_over,
control_break
};
protected: protected:
struct node { struct node {
optional<TypeT> value; optional<TypeT> value;
size_t parent, child, prev, next; size_t parent, child, prev, next;
size_t depth, num_children;
template<typename...ArgsT> template<typename...ArgsT>
constexpr node(size_t p, size_t c, size_t v, size_t n, ArgsT&&...args) constexpr node(size_t p, size_t c, size_t v, size_t n, size_t d, ArgsT&&...args)
: value(fennec::forward<ArgsT>(args)...) : value(fennec::forward<ArgsT>(args)...)
, parent(p), child(c), prev(v), next(n) { , parent(p), child(c), prev(v), next(n)
, depth(d), num_children(0) {
} }
constexpr ~node() { constexpr ~node() {
@@ -66,6 +62,8 @@ protected:
child = npos; child = npos;
prev = npos; prev = npos;
next = npos; next = npos;
depth = npos;
num_children = 0;
} }
}; };
@@ -77,7 +75,7 @@ public:
explicit constexpr rdtree(ArgsT&&...args) explicit constexpr rdtree(ArgsT&&...args)
: _table(), _freed(), _size(1) { : _table(), _freed(), _size(1) {
_table.callocate(8); _table.callocate(8);
fennec::construct(&_table[0], npos, npos, npos, npos, fennec::forward<ArgsT>(args)...); fennec::construct(&_table[0], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
} }
constexpr rdtree(const rdtree& tree) constexpr rdtree(const rdtree& tree)
@@ -133,34 +131,74 @@ public:
/// \param i The id of the node to check /// \param i The id of the node to check
/// \returns The id of the parent node /// \returns The id of the parent node
constexpr size_t parent(size_t i) const { constexpr size_t parent(size_t i) const {
if (i >= _table.capacity()) return npos;
return i == npos ? npos : _table[i].parent; return i == npos ? npos : _table[i].parent;
} }
/// ///
/// \param i The id of the node to check /// \param i The id of the node to check
/// \returns The id of the child node /// \returns The id of the child node
constexpr size_t child(size_t i) const { constexpr size_t child(size_t i, size_t n = 0) const {
return i == npos ? npos : _table[i].child; if (i >= _table.capacity()) return npos;
size_t c = i == npos ? npos : _table[i].child;
if (n != 0)
return next(c, n == npos ? npos : n - 1);
return c;
} }
/// ///
/// \param i The id of the node to check /// \param i The id of the node to check
/// \returns The id of the next node /// \returns The id of the next node
constexpr size_t next(size_t i) const { constexpr size_t next(size_t i, size_t n = 0) const {
return i == npos ? npos : _table[i].next; if (i >= _table.capacity()) return npos;
if (i == npos) {
return npos;
}
size_t org = i;
size_t nxt = _table[i].next;
while (nxt != npos) {
i = nxt;
nxt = _table[i].next;
if (n != npos) {
if (n-- == 0) {
break;
}
}
}
return i == org && n != npos ? npos : i;
} }
/// ///
/// \param i The id of the node to check /// \param i The id of the node to check
/// \returns The id of the previous node /// \returns The id of the previous node
constexpr size_t prev(size_t i) const { constexpr size_t prev(size_t i, size_t n = 0) const {
return i == npos ? npos : _table[i].prev; if (i >= _table.capacity()) return npos;
if (i == npos) {
return npos;
}
size_t org = i;
size_t prv = _table[i].prev;
while (prv != npos) {
i = prv;
prv = _table[i].prev;
if (n != npos) {
if (n-- == 0) {
break;
}
}
}
return i == org && n != npos ? npos : i;
} }
/// ///
/// \param i the node to start at /// \param i the node to start at
/// \returns the left-most child of node `i` /// \returns the left-most child of node `i`
constexpr size_t left_most(size_t i) const { constexpr size_t left_most(size_t i) const {
if (i >= _table.capacity()) return npos;
size_t n = i; size_t n = i;
if ((n = child(n)) == npos) { if ((n = child(n)) == npos) {
return i; return i;
@@ -177,6 +215,7 @@ public:
/// \param i the node to start at /// \param i the node to start at
/// \returns the right-most child of node `i` /// \returns the right-most child of node `i`
constexpr size_t right_most(size_t i) const { constexpr size_t right_most(size_t i) const {
if (i >= _table.capacity()) return npos;
if ((i = child(i)) == npos) { if ((i = child(i)) == npos) {
return npos; return npos;
} }
@@ -192,18 +231,54 @@ public:
} }
} }
///
/// \param i The id of the node to check
/// \returns The depth of the node
constexpr size_t depth(size_t i) const {
if (i >= _table.capacity()) return npos;
return i == npos ? npos : _table[i].depth;
}
///
/// \param i The id of the node to check
/// \returns The number of children the node has
constexpr size_t num_children(size_t i) const {
if (i >= _table.capacity()) return 0;
return i == npos ? 0 : _table[i].num_children;
}
///
/// \returns The next node id were `insert` or `emplace` to be called
constexpr size_t next_free() const {
size_t i = _size;
if (not _freed.empty()) {
i = _freed.front();
}
return i;
}
/// ///
/// \param i The id of the node to access /// \param i The id of the node to access
/// \returns A reference to the value of the node wrapped in an optional /// \returns A reference to the value of the node wrapped in an optional
constexpr optional<value_t>& operator[](size_t i) { constexpr value_t* operator[](size_t i) {
return _table[i].value; auto& it = _table[i].value;
if (it) {
return &*_table[i].value;
} else {
return nullptr;
}
} }
/// ///
/// \param i The id of the node to access /// \param i The id of the node to access
/// \returns A const-qualified reference to the value of the node wrapped in an optional /// \returns A const-qualified reference to the value of the node wrapped in an optional
constexpr const optional<value_t>& operator[](size_t i) const { constexpr const value_t* operator[](size_t i) const {
return _table[i].value; const auto& it = _table[i].value;
if (it) {
return &*_table[i].value;
} else {
return nullptr;
}
} }
@@ -212,21 +287,49 @@ public:
/// ///
/// \brief Insertion, creates a node in the tree with parent `parent` /// \brief Insertion, creates a node in the tree with parent `parent`
/// \param parent the parent node, if `npos` sets the value of the root node /// \param parent the parent node, if `npos` sets the value of the root node
/// \param next the next node, as an index relative to the parent
/// \param val the value to insert /// \param val the value to insert
/// \returns the index of the created node /// \returns the index of the created node
constexpr size_t insert(size_t parent, const value_t& val) { constexpr size_t insert(size_t parent, size_t next, const value_t& val) {
return this->_insert(parent, val); return this->_insert(parent, next, val);
} }
/// ///
/// \brief Insertion, creates a node in the tree with parent `parent` /// \brief Insertion, creates a node in the tree with parent `parent`
/// \param parent the parent node, if `npos` sets the value of the root node /// \param parent the parent node, if `npos` sets the value of the root node
/// \param next the next node, as an index relative to the parent
/// \param val the value to insert /// \param val the value to insert
/// \returns the index of the created node /// \returns the index of the created node
constexpr size_t insert(size_t parent, value_t&& val) { constexpr size_t insert(size_t parent, size_t next, value_t&& val) {
return this->_insert(parent, fennec::forward<value_t>(val)); return this->_insert(parent, next, fennec::forward<value_t>(val));
} }
///
/// \brief Insertion, creates a node in the tree with parent `parent`
/// \param parent the parent node, if `npos` sets the value of the root node
/// \param next the next node, as an index relative to the parent
/// \param args the args to construct the value to insert
/// \returns the index of the created node
template<typename...ArgsT>
constexpr size_t emplace(size_t parent, size_t next, ArgsT&&...args) {
return this->_insert(parent, next, fennec::forward<ArgsT>(args)...);
}
///
/// \brief Swap two nodes
/// \param i0 The id of the first node
/// \param i1 The id of the second node
constexpr void swap(size_t i0, size_t i1) {
size_t p0 = parent(i0);
size_t p1 = parent(i1);
fennec::swap(_table[i0], _table[i1]);
if (child(p0) == i0) _table[p0].child = i1;
if (child(p1) == i1) _table[p1].child = i0;
}
/// ///
/// \brief Erase a node in the tree and all of it's children /// \brief Erase a node in the tree and all of it's children
/// \param i the index of the node /// \param i the index of the node
@@ -332,7 +435,7 @@ public:
OrderT order; OrderT order;
i = order(*this, i); i = order(*this, i);
while (i != npos) { while (i != npos) {
uint8_t mode = visit(*_table[i].value); uint8_t mode = visit(*_table[i].value, i);
if (mode == traversal_control_break) { if (mode == traversal_control_break) {
break; break;
} }
@@ -353,8 +456,8 @@ protected:
size_t _next_free() { size_t _next_free() {
size_t next = _size; size_t next = _size;
if (not _freed.empty()) { if (not _freed.empty()) {
next = _freed.back(); next = _freed.front();
_freed.pop_back(); _freed.pop_front();
} }
if (_size >= capacity()) { if (_size >= capacity()) {
_expand(); _expand();
@@ -364,9 +467,9 @@ protected:
} }
template<typename...ArgsT> template<typename...ArgsT>
constexpr size_t _insert(size_t p, ArgsT&&...args) { constexpr size_t _insert(size_t p, size_t n, ArgsT&&...args) {
if (_size == 0) { if (_size == 0) {
fennec::construct(&_table[root], npos, npos, npos, npos, fennec::forward<ArgsT>(args)...); fennec::construct(&_table[root], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
_size = 1; _size = 1;
return root; return root;
} }
@@ -377,17 +480,34 @@ protected:
return root; return root;
} }
size_t i = _next_free(); size_t idx = _next_free();
size_t n = child(p); size_t nxt = child(p, n);
_table[p].child = i; size_t prv = n == npos ? npos : prev(n);
if (n != npos) _table[n].prev = i;
fennec::construct(&_table[i], p, npos, npos, n, fennec::forward<ArgsT>(args)...); ++_table[p].num_children;
return i; if ((nxt == child(p) && n != npos) || nxt == npos) {
_table[p].child = idx;
}
if (n == npos) {
if (nxt != npos) {
_table[nxt].next = idx;
}
fennec::construct(&_table[idx], p, npos, nxt, npos, depth(p) + 1, fennec::forward<ArgsT>(args)...);
} else {
if (nxt != npos) {
_table[nxt].prev = idx;
}
if (prv != npos) {
_table[prv].next = idx;
}
fennec::construct(&_table[idx], p, npos, prv, nxt, depth(p) + 1, fennec::forward<ArgsT>(args)...);
}
return idx;
} }
constexpr void _erase(size_t i) { constexpr void _erase(size_t i) {
list<size_t> queue; list<size_t> queue;
size_t j = 0;
queue.push_back(child(i)); queue.push_back(child(i));
while (queue.empty() == false) { while (queue.empty() == false) {
size_t n = queue.front(); queue.pop_front(); size_t n = queue.front(); queue.pop_front();
@@ -397,7 +517,6 @@ protected:
fennec::destruct(&_table[n]); fennec::destruct(&_table[n]);
_freed.push_back(n); _freed.push_back(n);
--_size; --_size;
++j;
} }
fennec::destruct(&_table[i]); fennec::destruct(&_table[i]);

View File

@@ -45,7 +45,7 @@ inline void fennec_test_containers_rdtree() {
const size_t prev = rdtree<size_t>::npos; const size_t prev = rdtree<size_t>::npos;
const size_t next = test.child(parent); const size_t next = test.child(parent);
size_t l; size_t l;
assertf((l = test.insert(parent, i)) == i + 1, "Tree Construct Test Failed."); assertf((l = test.insert(parent, 0, i)) == i + 1, "Tree Construct Test Failed.");
assertf(test.parent(l) == parent, "Tree Construct Test Failed."); assertf(test.parent(l) == parent, "Tree Construct Test Failed.");
assertf(test.child(l) == child, "Tree Construct Test Failed."); assertf(test.child(l) == child, "Tree Construct Test Failed.");
assertf(test.prev(l) == prev, "Tree Construct Test Failed."); assertf(test.prev(l) == prev, "Tree Construct Test Failed.");
@@ -55,12 +55,12 @@ inline void fennec_test_containers_rdtree() {
test.erase(0); test.erase(0);
size_t n1 = test.insert(npos, 1); size_t n1 = test.insert(npos, 0, 1);
size_t n3 = test.insert(n1, 3); size_t n2 = test.insert(n1, 0, 2);
size_t n2 = test.insert(n1, 2); size_t n3 = test.insert(n1, npos, 3);
size_t n5 = test.insert(n2, 5); size_t n4 = test.insert(n2, 0, 4);
size_t n4 = test.insert(n2, 4); size_t n5 = test.insert(n2, npos, 5);
size_t n6 = test.insert(n3, 6); size_t n6 = test.insert(n3, 0, 6);
fennec_test_run(n1 != npos, true); fennec_test_run(n1 != npos, true);
fennec_test_run(n2 != npos, true); fennec_test_run(n2 != npos, true);
fennec_test_run(n3 != npos, true); fennec_test_run(n3 != npos, true);
@@ -73,7 +73,7 @@ inline void fennec_test_containers_rdtree() {
size_t i; size_t i;
i = 0; i = 0;
test.traverse<tree_t::pre_order>([&](size_t x) -> uint8_t { test.traverse<tree_t::pre_order>([&](size_t x, size_t) -> uint8_t {
fennec_test_run(x, pre_order[i++]); fennec_test_run(x, pre_order[i++]);
return traversal_control_continue; return traversal_control_continue;
}); });
@@ -81,7 +81,7 @@ inline void fennec_test_containers_rdtree() {
fennec_test_spacer(1); fennec_test_spacer(1);
i = 0; i = 0;
test.traverse<tree_t::in_order>([&](size_t x) -> uint8_t { test.traverse<tree_t::in_order>([&](size_t x, size_t) -> uint8_t {
fennec_test_run(x, in_order[i++]); fennec_test_run(x, in_order[i++]);
return traversal_control_continue; return traversal_control_continue;
}); });
@@ -89,7 +89,7 @@ inline void fennec_test_containers_rdtree() {
fennec_test_spacer(1); fennec_test_spacer(1);
i = 0; i = 0;
test.traverse<tree_t::post_order>([&](size_t x) -> uint8_t { test.traverse<tree_t::post_order>([&](size_t x, size_t) -> uint8_t {
fennec_test_run(x, post_order[i++]); fennec_test_run(x, post_order[i++]);
return traversal_control_continue; return traversal_control_continue;
}); });