xo-ordinaltree: refactor: split -> multiple .hpp files

This commit is contained in:
Roland Conybeare 2025-11-30 18:12:03 -05:00
commit 3d7ddd2f8c
11 changed files with 3018 additions and 2873 deletions

File diff suppressed because it is too large Load diff

View 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 */

View 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 */

View 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 */

View file

@ -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 */

View 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 */

File diff suppressed because it is too large Load diff

View 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 */

View 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 */

View file

@ -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 {

View file

@ -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 {