xo-ordinaltree: refactor: split -> multiple .hpp files
This commit is contained in:
parent
792df8b297
commit
3d7ddd2f8c
11 changed files with 3018 additions and 2873 deletions
File diff suppressed because it is too large
Load diff
361
xo-ordinaltree/include/xo/ordinaltree/rbtree/Iterator.hpp
Normal file
361
xo-ordinaltree/include/xo/ordinaltree/rbtree/Iterator.hpp
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/** @file Iterator.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Node.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace tree {
|
||||
namespace detail {
|
||||
/* tragically, we can't partially specialize an alias template.
|
||||
* however we /can/ partially specialize a struct that nests a typealias.
|
||||
*/
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename Reduce,
|
||||
bool IsConst>
|
||||
struct NodeTypeTraits { using NodeType = void; };
|
||||
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename Reduce>
|
||||
struct NodeTypeTraits<Key, Value, Reduce, false> {
|
||||
using NativeNodeType = Node<Key, Value, Reduce>;
|
||||
using NodeType = NativeNodeType;
|
||||
using ContentsType = typename NodeType::value_type;
|
||||
using NodePtrType = NodeType *;
|
||||
};
|
||||
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename Reduce>
|
||||
struct NodeTypeTraits<Key, Value, Reduce, true> {
|
||||
using NativeNodeType = Node<Key, Value, Reduce>;
|
||||
using NodeType = NativeNodeType const;
|
||||
using ContentsType = typename NodeType::value_type const;
|
||||
using NodePtrType = NodeType const *;
|
||||
};
|
||||
|
||||
/* xo::tree::detail::IteratorBase
|
||||
*
|
||||
* shared between const & and non-const red-black-tree iterators.
|
||||
*
|
||||
* editor bait: BaseIterator
|
||||
*/
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename Reduce,
|
||||
bool IsConst>
|
||||
class IteratorBase {
|
||||
public:
|
||||
using RbUtil = RbTreeUtil<Key, Value, Reduce>;
|
||||
using RbNode = Node<Key, Value, Reduce>;
|
||||
using Traits = NodeTypeTraits<Key, Value, Reduce, IsConst>;
|
||||
using ReducedValue = typename Reduce::value_type;
|
||||
using RbNativeNodeType = typename Traits::NativeNodeType;
|
||||
using RbNodePtrType = typename Traits::NodePtrType;
|
||||
using RbContentsType = typename Traits::ContentsType;
|
||||
|
||||
protected:
|
||||
IteratorBase() = default;
|
||||
IteratorBase(IteratorDirection dirn, IteratorLocation loc, RbNodePtrType node)
|
||||
: dirn_{dirn}, location_{loc}, node_{node} {}
|
||||
IteratorBase(IteratorBase const & x) = default;
|
||||
|
||||
static IteratorBase prebegin_aux(RbNodePtrType node) {
|
||||
return IteratorBase(ID_Forward, IL_BeforeBegin, node);
|
||||
} /*prebegin_aux*/
|
||||
|
||||
static IteratorBase begin_aux(RbNodePtrType node) {
|
||||
return IteratorBase(ID_Forward, node ? IL_Regular : IL_AfterEnd, node);
|
||||
} /*begin_aux*/
|
||||
|
||||
static IteratorBase end_aux(RbNodePtrType node) {
|
||||
return IteratorBase(ID_Forward, IL_AfterEnd, node);
|
||||
} /*end_aux*/
|
||||
|
||||
static IteratorBase rprebegin_aux(RbNodePtrType node) {
|
||||
return IteratorBase(ID_Reverse, IL_AfterEnd, node);
|
||||
} /*rprebegin_aux*/
|
||||
|
||||
static IteratorBase rbegin_aux(RbNodePtrType node) {
|
||||
return IteratorBase(ID_Reverse,
|
||||
(node ? IL_Regular : IL_BeforeBegin),
|
||||
node);
|
||||
} /*rbegin_aux*/
|
||||
|
||||
static IteratorBase rend_aux(RbNodePtrType node) {
|
||||
return IteratorBase(ID_Reverse,
|
||||
IL_BeforeBegin,
|
||||
node);
|
||||
} /*rend_aux*/
|
||||
|
||||
public:
|
||||
IteratorLocation location() const { return location_; }
|
||||
RbNodePtrType node() const { return node_; }
|
||||
|
||||
ReducedValue const & reduced() const { return node_->reduced(); }
|
||||
|
||||
RbContentsType & operator*() const {
|
||||
this->check_regular();
|
||||
return this->node_->contents();
|
||||
} /*operator**/
|
||||
|
||||
RbContentsType * operator->() const {
|
||||
return &(this->operator*());
|
||||
}
|
||||
|
||||
/* true for "just before beginning" and "just after the end" states.
|
||||
* false otherwise
|
||||
*/
|
||||
bool is_sentinel() const { return (this->location_ != IL_Regular); }
|
||||
/* true unless iterator is in a sentinel state */
|
||||
bool is_dereferenceable() const { return !this->is_sentinel(); }
|
||||
|
||||
/* deferenceable iterators are truth-y;
|
||||
* sentinel iterators are false-y
|
||||
*/
|
||||
operator bool() const { return this->is_dereferenceable(); }
|
||||
|
||||
bool operator==(IteratorBase const & x) const {
|
||||
return (this->location_ == x.location_) && (this->node_ == x.node_);
|
||||
} /*operator==*/
|
||||
|
||||
bool operator!=(IteratorBase const & x) const {
|
||||
return (this->location_ != x.location_) || (this->node_ != x.node_);
|
||||
} /*operator!=*/
|
||||
|
||||
void print(std::ostream & os) const {
|
||||
using xo::xtag;
|
||||
|
||||
os << "<rbtree-iterator"
|
||||
<< xtag("dirn", dirn_)
|
||||
<< xtag("loc", location_)
|
||||
<< xtag("node", node_)
|
||||
<< ">";
|
||||
} /*print*/
|
||||
|
||||
/* pre-increment */
|
||||
IteratorBase & operator++() {
|
||||
return ((this->dirn_ == ID_Forward)
|
||||
? this->next_step()
|
||||
: this->prev_step());
|
||||
} /*operator++*/
|
||||
|
||||
/* pre-decrement */
|
||||
IteratorBase & operator--() {
|
||||
return ((this->dirn_ == ID_Forward)
|
||||
? this->prev_step()
|
||||
: this->next_step());
|
||||
} /*operator--*/
|
||||
|
||||
protected:
|
||||
void check_regular() const {
|
||||
using xo::tostr;
|
||||
|
||||
if(this->location_ != IL_Regular)
|
||||
throw std::runtime_error(tostr("rbtree iterator: cannot deref iterator"
|
||||
" in non-regular state"));
|
||||
} /*check_regular*/
|
||||
|
||||
private:
|
||||
IteratorBase & next_step() {
|
||||
switch(this->location_) {
|
||||
case IL_BeforeBegin:
|
||||
/* .node is first node in tree */
|
||||
this->location_ = IL_Regular;
|
||||
break;
|
||||
case IL_Regular:
|
||||
{
|
||||
RbNodePtrType next_node
|
||||
= RbUtil::next_inorder_node(const_cast<RbNativeNodeType *>(this->node_));
|
||||
|
||||
if(next_node) {
|
||||
this->node_ = next_node;
|
||||
} else {
|
||||
this->location_ = IL_AfterEnd;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IL_AfterEnd:
|
||||
break;
|
||||
} /*operator++*/
|
||||
|
||||
return *this;
|
||||
} /*next_step*/
|
||||
|
||||
IteratorBase & prev_step() {
|
||||
switch(this->location_) {
|
||||
case IL_BeforeBegin:
|
||||
break;
|
||||
case IL_Regular:
|
||||
{
|
||||
RbNode * prev_node = RbUtil::prev_inorder_node(const_cast<RbNativeNodeType *>(this->node_));
|
||||
|
||||
if(prev_node) {
|
||||
this->node_ = prev_node;
|
||||
} else {
|
||||
this->location_ = IL_BeforeBegin;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IL_AfterEnd:
|
||||
/* .node is already last node in tree */
|
||||
this->location_ = IL_Regular;
|
||||
break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
} /*prev_step*/
|
||||
|
||||
protected:
|
||||
/* ID_Forward, ID_Reverse */
|
||||
IteratorDirection dirn_ = ID_Forward;
|
||||
/* IL_BeforeBegin, IL_Regular, IL_AfterEnd */
|
||||
IteratorLocation location_ = IL_AfterEnd;
|
||||
/* location = IL_BeforeBegin: .node is leftmost node in tree
|
||||
* location = IL_Regular: .node is some node in tree,
|
||||
* iterator refers to that node.
|
||||
* location = IL_AfterEnd: .node is rightmost node in tree
|
||||
*/
|
||||
RbNodePtrType node_ = nullptr;
|
||||
}; /*IteratorBase*/
|
||||
|
||||
/* xo::tree::detail::Iterator
|
||||
*
|
||||
* inorder iterator over nodes in a red-black tree.
|
||||
* invalidated on insert or remove operations on the parent tree.
|
||||
*
|
||||
* satisfies the std::bidirectional_iterator concept
|
||||
*/
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename Reduce>
|
||||
class Iterator : public IteratorBase<Key, Value, Reduce, false /*!IsConst*/> {
|
||||
public:
|
||||
using iterator_concept = std::bidirectional_iterator_tag;
|
||||
|
||||
using RbIteratorBase = IteratorBase<Key, Value, Reduce, false /*!IsConst*/>;
|
||||
using RbNode = typename RbIteratorBase::RbNode;
|
||||
using RbUtil = typename RbIteratorBase::RbUtil;
|
||||
using ReducedValue = typename Reduce::value_type;
|
||||
|
||||
public:
|
||||
Iterator() = default;
|
||||
Iterator(IteratorDirection dirn, IteratorLocation loc, RbNode * n)
|
||||
: RbIteratorBase(dirn, loc, n) {}
|
||||
Iterator(Iterator const & x) = default;
|
||||
Iterator(RbIteratorBase const & x) : RbIteratorBase(x) {}
|
||||
Iterator(RbIteratorBase && x) : RbIteratorBase(std::move(x)) {}
|
||||
|
||||
static Iterator begin_aux(RbNode const * n) { return RbIteratorBase::begin_aux(n); }
|
||||
|
||||
static Iterator end_aux(RbNode const * n) { return RbIteratorBase::end_aux(n); }
|
||||
|
||||
static Iterator rbegin_aux(RbNode const * n) { return RbIteratorBase::rbegin_aux(n); }
|
||||
static Iterator rend_aux(RbNode const * n) { return RbIteratorBase::rend_aux(n); }
|
||||
|
||||
/* pre-increment */
|
||||
Iterator & operator++() {
|
||||
RbIteratorBase::operator++();
|
||||
return *this;
|
||||
} /*operator++*/
|
||||
|
||||
/* post-increment */
|
||||
Iterator operator++(int) {
|
||||
Iterator retval = *this;
|
||||
|
||||
++(*this);
|
||||
|
||||
return retval;
|
||||
} /*operator++(int)*/
|
||||
|
||||
/* pre-decrement */
|
||||
Iterator & operator--() {
|
||||
RbIteratorBase::operator--();
|
||||
return *this;
|
||||
} /*operator--*/
|
||||
|
||||
/* post-decrement */
|
||||
Iterator operator--(int) {
|
||||
Iterator retval = *this;
|
||||
|
||||
--(*this);
|
||||
|
||||
return retval;
|
||||
} /*operator--(int)*/
|
||||
}; /*Iterator*/
|
||||
|
||||
/* xo::tree::detail::ConstIterator
|
||||
*
|
||||
* inorder iterator over nodes in a red-black tree.
|
||||
* invalidated on insert or remove operations on the parent tree.
|
||||
*
|
||||
* satisfies the std::bidirectional_iterator concept
|
||||
*/
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename Reduce>
|
||||
class ConstIterator : public IteratorBase<Key, Value, Reduce, true /*IsConst*/> {
|
||||
public:
|
||||
using iterator_concept = std::bidirectional_iterator_tag;
|
||||
|
||||
using RbIteratorBase = IteratorBase<Key, Value, Reduce, true /*IsConst*/>;
|
||||
using RbNode = typename RbIteratorBase::RbNode;
|
||||
using RbUtil = typename RbIteratorBase::RbUtil;
|
||||
using ReducedValue = typename Reduce::value_type;
|
||||
|
||||
public:
|
||||
ConstIterator() = default;
|
||||
ConstIterator(IteratorDirection dirn, IteratorLocation loc, RbNode const * node)
|
||||
: RbIteratorBase(dirn, loc, node) {}
|
||||
ConstIterator(ConstIterator const & x) = default;
|
||||
ConstIterator(RbIteratorBase const & x) : RbIteratorBase(x) {}
|
||||
ConstIterator(RbIteratorBase && x) : RbIteratorBase(std::move(x)) {}
|
||||
|
||||
static ConstIterator prebegin_aux(RbNode const * n) { return RbIteratorBase::prebegin_aux(n); }
|
||||
static ConstIterator begin_aux(RbNode const * n) { return RbIteratorBase::begin_aux(n); }
|
||||
static ConstIterator end_aux(RbNode const * n) { return RbIteratorBase::end_aux(n); }
|
||||
|
||||
static ConstIterator rprebegin_aux(RbNode const * n) { return RbIteratorBase::rprebegin_aux(n); }
|
||||
static ConstIterator rbegin_aux(RbNode const * n) { return RbIteratorBase::rbegin_aux(n); }
|
||||
static ConstIterator rend_aux(RbNode const * n) { return RbIteratorBase::rend_aux(n); }
|
||||
|
||||
/* pre-increment */
|
||||
ConstIterator & operator++() {
|
||||
RbIteratorBase::operator++();
|
||||
return *this;
|
||||
} /*operator++*/
|
||||
|
||||
/* post-increment */
|
||||
ConstIterator operator++(int) {
|
||||
ConstIterator retval = *this;
|
||||
|
||||
++(*this);
|
||||
|
||||
return retval;
|
||||
} /*operator++(int)*/
|
||||
|
||||
/* pre-decrement */
|
||||
ConstIterator & operator--() {
|
||||
RbIteratorBase::operator--();
|
||||
return *this;
|
||||
} /*operator--*/
|
||||
|
||||
/* post-decrement */
|
||||
ConstIterator operator--(int) {
|
||||
ConstIterator retval = *this;
|
||||
|
||||
--(*this);
|
||||
|
||||
return retval;
|
||||
} /*operator--(int)*/
|
||||
}; /*ConstIterator*/
|
||||
} /*namespace detail*/
|
||||
} /*namespace tree*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Iterator.hpp */
|
||||
384
xo-ordinaltree/include/xo/ordinaltree/rbtree/Node.hpp
Normal file
384
xo-ordinaltree/include/xo/ordinaltree/rbtree/Node.hpp
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
/** @file Node.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RbTypes.hpp"
|
||||
#include <utility>
|
||||
|
||||
namespace xo {
|
||||
namespace tree {
|
||||
namespace detail {
|
||||
/** see xo/ordinaltree/rbtree/RbTreeUtil.hpp */
|
||||
template <typename Key, typename Value, typename Reduce>
|
||||
class RbTreeUtil;
|
||||
|
||||
/* xo::tree::detail::Node
|
||||
*
|
||||
* Require:
|
||||
* - Key.operator<
|
||||
* - Key.operator==
|
||||
*
|
||||
*/
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename Reduce>
|
||||
class Node {
|
||||
public:
|
||||
using ReducedValue = typename Reduce::value_type;
|
||||
using ContentsType = std::pair<Key const, Value>;
|
||||
using value_type = std::pair<Key const , Value>;
|
||||
|
||||
public:
|
||||
Node() = default;
|
||||
Node(value_type const & kv_pair,
|
||||
std::pair<ReducedValue, ReducedValue> const & r)
|
||||
: color_(C_Red), size_(1), contents_{kv_pair}, reduced_(r) {}
|
||||
Node(value_type && kv_pair,
|
||||
std::pair<ReducedValue, ReducedValue> && r)
|
||||
: color_(C_Red), size_(1),
|
||||
contents_{std::move(kv_pair)},
|
||||
reduced_{std::move(r)} {}
|
||||
|
||||
template <typename NodeAllocator>
|
||||
static Node * make_leaf(NodeAllocator& alloc,
|
||||
value_type const & kv_pair,
|
||||
ReducedValue const & leaf_rv) {
|
||||
using traits = std::allocator_traits<NodeAllocator>;
|
||||
|
||||
// get memory
|
||||
Node * node = traits::allocate(alloc, 1);
|
||||
try {
|
||||
// placemenent new
|
||||
traits::construct(alloc, node, kv_pair,
|
||||
std::pair<ReducedValue, ReducedValue>(leaf_rv, leaf_rv));
|
||||
return node;
|
||||
} catch(...) {
|
||||
traits::deallocate(alloc, node, 1);
|
||||
throw;
|
||||
|
||||
}
|
||||
} /*make_leaf*/
|
||||
|
||||
static Node * make_leaf(value_type && kv_pair,
|
||||
ReducedValue const & leaf_rv) {
|
||||
return new Node(kv_pair,
|
||||
std::pair<ReducedValue, ReducedValue>(leaf_rv, leaf_rv));
|
||||
} /*make_leaf*/
|
||||
|
||||
/* return #of key/vaue pairs in tree rooted at x. */
|
||||
static size_t tree_size(Node *x) {
|
||||
if (x)
|
||||
return x->size();
|
||||
else
|
||||
return 0;
|
||||
} /*tree_size*/
|
||||
|
||||
static bool is_black(Node *x) {
|
||||
if (x)
|
||||
return x->is_black();
|
||||
else
|
||||
return true;
|
||||
} /*is_black*/
|
||||
|
||||
static bool is_red(Node *x) {
|
||||
if (x)
|
||||
return x->is_red();
|
||||
else
|
||||
return false;
|
||||
} /*is_red*/
|
||||
|
||||
static Direction child_direction(Node *p, Node *n) {
|
||||
if (p) {
|
||||
return p->child_direction(n);
|
||||
} else {
|
||||
return D_Invalid;
|
||||
}
|
||||
} /*child_direction*/
|
||||
|
||||
static ReducedValue reduce_aux(Reduce reduce, Node *x)
|
||||
{
|
||||
if(x)
|
||||
return x->reduced2();
|
||||
else
|
||||
return reduce.nil();
|
||||
} /*reduce_aux*/
|
||||
|
||||
/* calculate reduced values for node x.
|
||||
* does not used x.reduced
|
||||
*/
|
||||
static std::pair<ReducedValue,
|
||||
ReducedValue> reduced_pair(Reduce r, Node const * x)
|
||||
{
|
||||
if(!x)
|
||||
assert(false);
|
||||
|
||||
ReducedValue r1 = r(reduce_aux(r, x->left_child()),
|
||||
x->value());
|
||||
ReducedValue r2 = r.combine(r1,
|
||||
reduce_aux(r, x->right_child()));
|
||||
return std::pair<ReducedValue, ReducedValue>(r1, r2);
|
||||
} /*reduced_pair*/
|
||||
|
||||
/* replace root pointer *pp_root with x;
|
||||
* set x parent pointer to nil
|
||||
*/
|
||||
static void replace_root_reparent(Node *x, Node **pp_root) {
|
||||
*pp_root = x;
|
||||
if (x)
|
||||
x->parent_ = nullptr;
|
||||
} /*replace_root_reparent*/
|
||||
|
||||
/** swap values of all members except @ref contents_
|
||||
* between @p *lhs and @p *rhs
|
||||
**/
|
||||
static void swap_locations(Node * lhs, Node * rhs, bool debug_flag) {
|
||||
scope log(XO_DEBUG(debug_flag));
|
||||
|
||||
assert(lhs->parent() != rhs->parent());
|
||||
|
||||
Node * lhs_parent = lhs->parent();
|
||||
Node * rhs_parent = rhs->parent();
|
||||
|
||||
/* can have null parent if either {lhs, rhs} is root node */
|
||||
|
||||
if (log) {
|
||||
log("pre", xtag("lhs", lhs), xtag("rhs", rhs));
|
||||
log(xtag("lhs.left", lhs->left_child()),
|
||||
xtag("lhs.right", lhs->right_child()));
|
||||
log(xtag("rhs.left", rhs->left_child()),
|
||||
xtag("rhs.right", rhs->right_child()));
|
||||
}
|
||||
|
||||
assert(lhs != rhs->left_child() && "not implemented");
|
||||
assert(rhs != lhs->right_child() && "not implemented");
|
||||
assert(rhs != lhs->left_child() && "expected left-to-right key order");
|
||||
assert(lhs != rhs->right_child() && "expected left-to-right key order");
|
||||
|
||||
if (lhs_parent)
|
||||
lhs_parent->replace_child_reparent(lhs, rhs);
|
||||
|
||||
/* now have:
|
||||
* - rhs->parent() = lhs_parent
|
||||
* - lhs->parent() = nullptr
|
||||
*/
|
||||
|
||||
if (rhs_parent)
|
||||
rhs_parent->replace_child_reparent(rhs, lhs);
|
||||
|
||||
/* now have:
|
||||
* - lhs->parent() = rhs_parent
|
||||
* - rhs->parent() = lhs_parent
|
||||
*/
|
||||
|
||||
assert(lhs->parent() == rhs_parent);
|
||||
assert(rhs->parent() == lhs_parent);
|
||||
assert(lhs->parent() != lhs);
|
||||
assert(rhs->parent() != rhs);
|
||||
|
||||
std::swap(lhs->color_, rhs->color_);
|
||||
std::swap(lhs->size_, rhs->size_);
|
||||
// preserve lhs->contents_, rhs->contents_
|
||||
// don't bother fixing reduced_, will fixup that separately
|
||||
//std::swap(lhs->reduced_, rhs->reduced_);
|
||||
|
||||
Node * lhs_left = lhs->left_child();
|
||||
Node * lhs_right = lhs->right_child();
|
||||
|
||||
Node * rhs_left = rhs->left_child();
|
||||
Node * rhs_right = rhs->right_child();
|
||||
|
||||
lhs->assign_child_reparent(D_Left, rhs_left);
|
||||
lhs->assign_child_reparent(D_Right, rhs_right);
|
||||
rhs->assign_child_reparent(D_Left, lhs_left);
|
||||
rhs->assign_child_reparent(D_Right, lhs_right);
|
||||
|
||||
//std::swap(lhs->child_v_, rhs->child_v_);
|
||||
|
||||
/* also need to reparent */
|
||||
} /*swap_locations*/
|
||||
|
||||
size_t size() const { return size_; }
|
||||
/* const access */
|
||||
value_type const & contents() const { return contents_; }
|
||||
/* non-const value access.
|
||||
*
|
||||
* editorial: would prefer to return
|
||||
* std::pair<Key const, Value> &
|
||||
* here, so that tree[k].first = newk
|
||||
* prohibited, but std::pair<Key const, Value>
|
||||
* is considered unrelated to std::pair<Key, Value>,
|
||||
* so l-value conversion not allowed
|
||||
*/
|
||||
value_type & contents() { return contents_; }
|
||||
|
||||
Node * parent() const { return parent_; }
|
||||
Node * child(Direction d) const { return child_v_[d]; }
|
||||
Node * left_child() const { return child_v_[0]; }
|
||||
Node * right_child() const { return child_v_[1]; }
|
||||
ReducedValue const & reduced1() const { return reduced_.first; }
|
||||
ReducedValue const & reduced2() const { return reduced_.second; }
|
||||
|
||||
/* true if this node has 0 children */
|
||||
bool is_leaf() const {
|
||||
return ((child_v_[0] == nullptr) && (child_v_[1] == nullptr));
|
||||
}
|
||||
|
||||
/* identify which child x represents
|
||||
* Require:
|
||||
* - x != nullptr
|
||||
* - x is either this->left_child() or this->right_child()
|
||||
*/
|
||||
Direction child_direction(Node * x) const {
|
||||
if (x == this->left_child())
|
||||
return D_Left;
|
||||
else if (x == this->right_child())
|
||||
return D_Right;
|
||||
else
|
||||
return D_Invalid;
|
||||
} /*child_direction*/
|
||||
|
||||
bool is_black() const { return this->color_ == C_Black; }
|
||||
bool is_red() const { return this->color_ == C_Red; }
|
||||
|
||||
bool is_red_left() const { return is_red(this->left_child()); }
|
||||
bool is_red_right() const { return is_red(this->right_child()); }
|
||||
|
||||
/* true if this node is red, and either child is red */
|
||||
bool is_red_violation() const {
|
||||
if (this->color_ == C_Red) {
|
||||
Node *left = this->left_child();
|
||||
Node *right = this->right_child();
|
||||
|
||||
if (left && left->is_red())
|
||||
return true;
|
||||
|
||||
if (right && right->is_red())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} /*is_red_violation*/
|
||||
|
||||
Color color() const { return color_; }
|
||||
Key const & key() const { return contents_.first; }
|
||||
Value const & value() const { return contents_.second; }
|
||||
|
||||
/* recalculate size from immediate childrens' sizes
|
||||
* editor bait: recalc_local_size()
|
||||
*/
|
||||
void local_recalc_size(Reduce const & reduce_fn) {
|
||||
using xo::scope;
|
||||
using xo::xtag;
|
||||
|
||||
constexpr bool c_logging_enabled = false;
|
||||
|
||||
scope log(XO_DEBUG(c_logging_enabled));
|
||||
|
||||
this->size_ = (1
|
||||
+ Node::tree_size(this->left_child())
|
||||
+ Node::tree_size(this->right_child()));
|
||||
|
||||
/* (note: want reduce applied to all of left subtree) */
|
||||
this->reduced_ = Node::reduced_pair(reduce_fn, this);
|
||||
|
||||
log && log("done recalc for key k, value v, reduced r",
|
||||
xtag("k", this->key()),
|
||||
xtag("v", this->value()),
|
||||
xtag("r1", this->reduced1()),
|
||||
xtag("r2", this->reduced2()));
|
||||
} /*local_recalc_size*/
|
||||
|
||||
private:
|
||||
void assign_color(Color x) { this->color_ = x; }
|
||||
void assign_size(size_t z) { this->size_ = z; }
|
||||
|
||||
void assign_child_reparent(Direction d, Node *new_x) {
|
||||
Node *old_x = this->child_v_[d];
|
||||
|
||||
// trying to fix old_x can be counterproductive,
|
||||
// since old_x->parent_ may already have been corrected,
|
||||
//
|
||||
if (old_x && (old_x->parent_ == this))
|
||||
old_x->parent_ = nullptr;
|
||||
|
||||
this->child_v_[d] = new_x;
|
||||
|
||||
if (new_x) {
|
||||
new_x->parent_ = this;
|
||||
}
|
||||
} /*assign_child_reparent*/
|
||||
|
||||
/* replace child that points to x, with child that points to x_new
|
||||
* and return direction of the child that was replaced
|
||||
*
|
||||
* Require:
|
||||
* - x is a child of *this
|
||||
* - x_new is not a child of *this
|
||||
*
|
||||
* promise:
|
||||
* - x is nullptr or x.parent is nullptr
|
||||
* - x_new is nullptr or x_new.parent is this
|
||||
*/
|
||||
Direction replace_child_reparent(Node *x, Node *x_new) {
|
||||
Direction d = this->child_direction(x);
|
||||
|
||||
if (d == D_Left || d == D_Right) {
|
||||
this->assign_child_reparent(d, x_new);
|
||||
return d;
|
||||
} else {
|
||||
return D_Invalid;
|
||||
}
|
||||
} /*replace_child_reparent*/
|
||||
|
||||
friend class RbTreeUtil<Key, Value, Reduce>;
|
||||
friend class xo::tree::RedBlackTree<Key, Value, Reduce>;
|
||||
|
||||
private:
|
||||
/* red | black */
|
||||
Color color_ = C_Red;
|
||||
/* size of subtree (#of key/value pairs) rooted at this node */
|
||||
size_t size_ = 0;
|
||||
/* .first = key associated with this node
|
||||
* .second = value associated with this node
|
||||
* .third = reduced value
|
||||
*/
|
||||
value_type contents_;
|
||||
/* accumulator for some binary function of Values.
|
||||
* must be associative, since value will be produced
|
||||
* by any ordering of calls to Reduce::combine().
|
||||
*
|
||||
* e.g. {a, b, c, d} could be reduced by:
|
||||
* r(r(a,b), r(c,d))
|
||||
* or
|
||||
* r(a, r(r(b, c), d))
|
||||
* etc.
|
||||
*
|
||||
* examples:
|
||||
* - count #of keys
|
||||
* - sum key values
|
||||
*
|
||||
* .reduced.first: reduce applied to all values with keys <= .contents.first
|
||||
* .reduced.second: reduce applied to all values in this subtree.
|
||||
*/
|
||||
std::pair<ReducedValue, ReducedValue> reduced_;
|
||||
/* pointer to parent node, nullptr iff this is the root node */
|
||||
Node *parent_ = nullptr;
|
||||
/*
|
||||
* .child_v[0] = left child
|
||||
* .child_v[1] = right child
|
||||
*
|
||||
* invariants:
|
||||
* - if .child_v[x] non-null, then .child_v[0]->parent = this
|
||||
* - a red node may not have red children
|
||||
*/
|
||||
std::array<Node *, 2> child_v_ = {nullptr, nullptr};
|
||||
}; /*Node*/
|
||||
} /*namespace detail*/
|
||||
} /*namespace tree*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/* end Node.hpp */
|
||||
45
xo-ordinaltree/include/xo/ordinaltree/rbtree/NullReduce.hpp
Normal file
45
xo-ordinaltree/include/xo/ordinaltree/rbtree/NullReduce.hpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/** @file NullReduce.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace xo {
|
||||
namespace tree {
|
||||
struct null_reduce_value {};
|
||||
|
||||
/* for null reduce, just have it return empty struct;
|
||||
* otherwise breaks verification (e.g. verify_subtree_ok() below)
|
||||
*/
|
||||
template<typename NodeValue>
|
||||
struct NullReduce {
|
||||
static constexpr bool is_null_reduce() { return true; }
|
||||
static constexpr bool is_monotonic() { return false; }
|
||||
|
||||
/* data type for reduced values */
|
||||
using value_type = null_reduce_value;
|
||||
|
||||
value_type nil() const { return value_type(); }
|
||||
value_type leaf(NodeValue const & /*x*/) const {
|
||||
return nil();
|
||||
}
|
||||
value_type operator()(value_type /*x*/,
|
||||
NodeValue const & /*value*/) const { return nil(); }
|
||||
value_type combine(value_type /*x*/,
|
||||
value_type /*y*/) const { return nil(); }
|
||||
bool is_equal(value_type /*x*/, value_type /*y*/) const { return true; }
|
||||
}; /*NullReduce*/
|
||||
|
||||
inline std::ostream & operator<<(std::ostream & os,
|
||||
null_reduce_value /*x*/)
|
||||
{
|
||||
os << "{}";
|
||||
return os;
|
||||
} /*operator<<*/
|
||||
} /*namespace tree*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end NullReduce.hpp */
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/** @file OrdinalReduce.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace xo {
|
||||
namespace tree {
|
||||
|
||||
/* just counts #of distinct values;
|
||||
* redundant, same as detail::Node<>::size_.
|
||||
* providing for completeness' sake
|
||||
*/
|
||||
template <typename Value>
|
||||
class OrdinalReduce {
|
||||
public:
|
||||
using value_type = std::size_t;
|
||||
|
||||
public:
|
||||
static constexpr bool is_monotonic() { return true; }
|
||||
|
||||
value_type nil() const { return 0; }
|
||||
|
||||
value_type leaf(Value const & /*x*/) const {
|
||||
return 1;
|
||||
} /*leaf*/
|
||||
|
||||
value_type operator()(value_type acc,
|
||||
Value const & /*x*/) const {
|
||||
/* counts #of values */
|
||||
return acc + 1;
|
||||
}
|
||||
|
||||
value_type combine(value_type x, value_type y) const { return x + y; }
|
||||
bool is_equal(value_type x, value_type y) const { return x == y; }
|
||||
}; /*OrdinalReduce*/
|
||||
|
||||
} /*namespace tree*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end OrdinalReduce.hpp */
|
||||
166
xo-ordinaltree/include/xo/ordinaltree/rbtree/RbTreeLhs.hpp
Normal file
166
xo-ordinaltree/include/xo/ordinaltree/rbtree/RbTreeLhs.hpp
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/** @file RbTreeLhs.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/indentlog/print/tostr.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace xo {
|
||||
namespace tree {
|
||||
namespace detail {
|
||||
/* xo::tree::detail::RedBlackTreeLhsBase
|
||||
*
|
||||
* use for const version of RedBlackTree::operator[].
|
||||
*
|
||||
* Require: RbNode is either
|
||||
* RedBlackTree::RbNode
|
||||
* or
|
||||
* RedBlackTree::RbNode const
|
||||
*/
|
||||
template <class RedBlackTree, class RbNode>
|
||||
class RedBlackTreeLhsBase {
|
||||
public:
|
||||
using mapped_type = typename RedBlackTree::mapped_type;
|
||||
using RbUtil = typename RedBlackTree::RbUtil;
|
||||
|
||||
public:
|
||||
RedBlackTreeLhsBase() = default;
|
||||
RedBlackTreeLhsBase(RedBlackTree * tree, RbNode * node)
|
||||
: p_tree_(tree), node_(node)
|
||||
{}
|
||||
|
||||
operator mapped_type const & () const {
|
||||
using xo::tostr;
|
||||
|
||||
if (!this->node_) {
|
||||
throw std::runtime_error
|
||||
(tostr("rbtree: attempt to use empty lhs object as rvalue"));
|
||||
}
|
||||
|
||||
return this->node_->contents().second;
|
||||
} /*operator value_type const &*/
|
||||
|
||||
protected:
|
||||
RedBlackTree * p_tree_ = nullptr;
|
||||
/* invariant: if non-nil, .node belongs to .*p_tree */
|
||||
RbNode * node_ = nullptr;
|
||||
}; /*RedBlackTreeLhsBase*/
|
||||
|
||||
template<class RedBlackTree>
|
||||
class RedBlackTreeConstLhs : public RedBlackTreeLhsBase<RedBlackTree const,
|
||||
typename RedBlackTree::RbNode const>
|
||||
{
|
||||
public:
|
||||
RedBlackTreeConstLhs() = default;
|
||||
RedBlackTreeConstLhs(RedBlackTree const * tree,
|
||||
typename RedBlackTree::RbNode const * node)
|
||||
: RedBlackTreeLhsBase<RedBlackTree const,
|
||||
typename RedBlackTree::RbNode const>(tree, node) {}
|
||||
}; /*RedBlackTreeConstLhs*/
|
||||
|
||||
/* xo::tree::detail::RedBlackTreeLhs
|
||||
*
|
||||
* use for RedBlackTree::operator[].
|
||||
* can't return a regular lvalue,
|
||||
* because assignment within a Node N invalidates partial sums along
|
||||
* the path from tree root to N.
|
||||
*
|
||||
* instead interpolate instance of this class, that can intercept
|
||||
* asasignments.
|
||||
*/
|
||||
template <class RedBlackTree>
|
||||
class RedBlackTreeLhs : public RedBlackTreeLhsBase<RedBlackTree,
|
||||
typename RedBlackTree::RbNode>
|
||||
{
|
||||
public:
|
||||
using value_type = typename RedBlackTree::value_type;
|
||||
using key_type = typename RedBlackTree::key_type;
|
||||
using mapped_type = typename RedBlackTree::mapped_type;
|
||||
using RbUtil = typename RedBlackTree::RbUtil;
|
||||
using RbNode = typename RedBlackTree::RbNode;
|
||||
|
||||
public:
|
||||
RedBlackTreeLhs() = default;
|
||||
RedBlackTreeLhs(RedBlackTree * tree, typename RedBlackTree::RbNode * node, key_type key)
|
||||
: RedBlackTreeLhsBase<RedBlackTree, RbNode>(tree, node), key_(key) {}
|
||||
|
||||
RedBlackTreeLhs & operator=(mapped_type const & v) {
|
||||
using xo::tostr;
|
||||
|
||||
constexpr bool c_debug_flag = false;
|
||||
|
||||
if(this->p_tree_) {
|
||||
if(this->node_) {
|
||||
this->node_->contents().second = v;
|
||||
|
||||
/* after modifying a node n,
|
||||
* must recalculate reductions along path [root .. n]
|
||||
*/
|
||||
RbUtil::fixup_ancestor_size(this->p_tree_->reduce_fn(),
|
||||
this->node_,
|
||||
c_debug_flag);
|
||||
} else {
|
||||
/* insert (key, v) pair into this tree */
|
||||
this->p_tree_->insert(value_type(this->key_, v));
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
|
||||
throw std::runtime_error
|
||||
(tostr("rbtree: attempt to apply operator= thru empty lhs object"));
|
||||
}
|
||||
|
||||
return *this;
|
||||
} /*operator=*/
|
||||
|
||||
RedBlackTreeLhs & operator+=(mapped_type const & v) {
|
||||
using xo::tostr;
|
||||
|
||||
if(this->p_tree_) {
|
||||
if(this->node_) {
|
||||
this->node_->contents().second += v;
|
||||
|
||||
/* after modifying value at node n,
|
||||
* must recalculate order statistics along path [root .. n]
|
||||
*/
|
||||
RbUtil::fixup_ancestor_size(this->p_tree_->reduce_fn(),
|
||||
this->node_);
|
||||
} else {
|
||||
/* for form's sake, in case value_type is something unusual */
|
||||
mapped_type v2;
|
||||
v2 += v;
|
||||
|
||||
/* insert (key, v) pair into this tree */
|
||||
this->p_tree_->insert(value_type(this->key_, v2));
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
|
||||
throw std::runtime_error
|
||||
(tostr("rbtree: attempt to apply operator+= through empty lhs object"));
|
||||
}
|
||||
|
||||
return *this;
|
||||
} /*operator+=*/
|
||||
|
||||
/* TODO:
|
||||
* - operator-=()
|
||||
* - operator*=()
|
||||
* - operator/=()
|
||||
*/
|
||||
|
||||
private:
|
||||
/* capture key k used in expression tree[k]
|
||||
* Invariant:
|
||||
* - if .node is non-null, then .node.key = key
|
||||
*/
|
||||
key_type key_;
|
||||
}; /*RedBlackTreeLhs*/
|
||||
} /*namespace detail*/
|
||||
} /*namespace tree*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end RbTreeLhs.hpp */
|
||||
1873
xo-ordinaltree/include/xo/ordinaltree/rbtree/RbTreeUtil.hpp
Normal file
1873
xo-ordinaltree/include/xo/ordinaltree/rbtree/RbTreeUtil.hpp
Normal file
File diff suppressed because it is too large
Load diff
68
xo-ordinaltree/include/xo/ordinaltree/rbtree/RbTypes.hpp
Normal file
68
xo-ordinaltree/include/xo/ordinaltree/rbtree/RbTypes.hpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/** @file RbTypes.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "NullReduce.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace tree {
|
||||
/* red-black tree with order statistics
|
||||
*
|
||||
* require:
|
||||
* - Key is equality comparable
|
||||
* - Key, Value, Reduce are copyable and null-constructible
|
||||
* - Reduce.value_type = Accumulator
|
||||
* - Reduce.operator() :: (Accumulator x Key) -> Accumulator
|
||||
* - Reduce.operator() :: (Accumulator x Accumulator) -> Accumulator
|
||||
*/
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename Reduce = NullReduce<Key>,
|
||||
typename Allocator = std::allocator<std::pair<const Key, Value>>>
|
||||
class RedBlackTree;
|
||||
|
||||
namespace detail {
|
||||
enum Color { C_Invalid = -1, C_Black, C_Red, N_Color };
|
||||
|
||||
enum Direction { D_Invalid = -1, D_Left, D_Right, N_Direction };
|
||||
|
||||
inline Direction other(Direction d) {
|
||||
return static_cast<Direction>(1 - d);
|
||||
} /*other*/
|
||||
|
||||
enum IteratorDirection {
|
||||
/* ID_Forward. forward iterator
|
||||
* ID_Reverse. reverse iterator
|
||||
*/
|
||||
ID_Forward,
|
||||
ID_Reverse
|
||||
}; /*IteratorDirection*/
|
||||
|
||||
/* specify iterator location relative to Iterator::node.
|
||||
* using this to make it possible to correctly decrement an
|
||||
* iterator at RedBlackTree::end().
|
||||
*
|
||||
* IL_BeforeBegin. if non-empty tree, .node is the first node
|
||||
* in the tree (the one with smallest key),
|
||||
* and iterator refers to the location
|
||||
* "one before" that first node.
|
||||
* IL_Regular. iterator refers to member of the tree
|
||||
* given by Iterator::node
|
||||
* IL_AfterEnd. if non-empty tree, .node is the last node
|
||||
* in the tree (the one with largest key),
|
||||
* and iterator refers the the location
|
||||
* "one after" that last node.
|
||||
*/
|
||||
enum IteratorLocation {
|
||||
IL_BeforeBegin,
|
||||
IL_Regular,
|
||||
IL_AfterEnd,
|
||||
}; /*IteratorLocation*/
|
||||
} /*namespace detail*/
|
||||
} /*namespace tree*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end RbTypes.hpp */
|
||||
54
xo-ordinaltree/include/xo/ordinaltree/rbtree/SumReduce.hpp
Normal file
54
xo-ordinaltree/include/xo/ordinaltree/rbtree/SumReduce.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* @file SumReduce.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace xo {
|
||||
namespace tree {
|
||||
/* reduction for inverting the integral of a non-negative discrete function
|
||||
* computes sum of values for each subtree
|
||||
*/
|
||||
template<typename Value>
|
||||
struct SumReduce {
|
||||
using value_type = Value;
|
||||
|
||||
static constexpr bool is_monotonic() { return true; }
|
||||
|
||||
value_type nil() const { return -std::numeric_limits<value_type>::infinity(); }
|
||||
value_type leaf(Value const & x) const {
|
||||
return x;
|
||||
} /*leaf*/
|
||||
|
||||
value_type operator()(value_type reduced,
|
||||
Value const & x) const {
|
||||
/* sums tree values */
|
||||
if(std::isfinite(reduced)) {
|
||||
return reduced + x;
|
||||
} else {
|
||||
/* omit -oo reduced value from .nil() */
|
||||
return x;
|
||||
}
|
||||
} /*operator()*/
|
||||
|
||||
value_type combine(value_type const & x,
|
||||
value_type const & y) const {
|
||||
/* omit -oo reduced value from .nil() */
|
||||
if(!std::isfinite(x))
|
||||
return y;
|
||||
if(!std::isfinite(y))
|
||||
return x;
|
||||
|
||||
return x + y;
|
||||
} /*combine*/
|
||||
|
||||
bool is_equal(value_type const & x, value_type const & y) const { return x == y; }
|
||||
}; /*SumReduce*/
|
||||
}
|
||||
}
|
||||
|
||||
/* end SumReduce.hpp */
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "random_tree_ops.hpp"
|
||||
#include "xo/ordinaltree/RedBlackTree.hpp"
|
||||
#include "xo/ordinaltree/rbtree/SumReduce.hpp"
|
||||
#include "xo/ordinaltree/rbtree/OrdinalReduce.hpp"
|
||||
#include <map>
|
||||
|
||||
namespace {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "xo/printjson/PrintJson.hpp"
|
||||
#include "xo/reflect/Reflect.hpp"
|
||||
#include "xo/ordinaltree/RedBlackTree.hpp"
|
||||
#include "xo/ordinaltree/rbtree/OrdinalReduce.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace reactor {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue