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:
@@ -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 }'
|
||||||
|
|||||||
@@ -44,28 +44,26 @@ 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() {
|
||||||
parent = npos;
|
parent = npos;
|
||||||
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]);
|
||||||
|
|||||||
@@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user