diff options
Diffstat (limited to 'immer/detail/rbts/operations.hpp')
-rw-r--r-- | immer/detail/rbts/operations.hpp | 2461 |
1 files changed, 2461 insertions, 0 deletions
diff --git a/immer/detail/rbts/operations.hpp b/immer/detail/rbts/operations.hpp new file mode 100644 index 000000000000..ff703e892b42 --- /dev/null +++ b/immer/detail/rbts/operations.hpp @@ -0,0 +1,2461 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include <algorithm> +#include <memory> +#include <numeric> +#include <utility> + +#include <immer/config.hpp> +#include <immer/detail/rbts/position.hpp> +#include <immer/detail/rbts/visitor.hpp> +#include <immer/detail/util.hpp> +#include <immer/heap/tags.hpp> + +namespace immer { +namespace detail { +namespace rbts { + +template <typename T> +struct array_for_visitor : visitor_base<array_for_visitor<T>> +{ + using this_t = array_for_visitor; + + template <typename PosT> + static T* visit_inner(PosT&& pos, size_t idx) + { + return pos.descend(this_t{}, idx); + } + + template <typename PosT> + static T* visit_leaf(PosT&& pos, size_t) + { + return pos.node()->leaf(); + } +}; + +template <typename T> +struct region_for_visitor : visitor_base<region_for_visitor<T>> +{ + using this_t = region_for_visitor; + using result_t = std::tuple<T*, size_t, size_t>; + + template <typename PosT> + static result_t visit_inner(PosT&& pos, size_t idx) + { + return pos.towards(this_t{}, idx); + } + + template <typename PosT> + static result_t visit_leaf(PosT&& pos, size_t idx) + { + return std::make_tuple(pos.node()->leaf(), pos.index(idx), pos.count()); + } +}; + +template <typename T> +struct get_visitor : visitor_base<get_visitor<T>> +{ + using this_t = get_visitor; + + template <typename PosT> + static const T& visit_inner(PosT&& pos, size_t idx) + { + return pos.descend(this_t{}, idx); + } + + template <typename PosT> + static const T& visit_leaf(PosT&& pos, size_t idx) + { + return pos.node()->leaf()[pos.index(idx)]; + } +}; + +struct for_each_chunk_visitor : visitor_base<for_each_chunk_visitor> +{ + using this_t = for_each_chunk_visitor; + + template <typename Pos, typename Fn> + static void visit_inner(Pos&& pos, Fn&& fn) + { + pos.each(this_t{}, fn); + } + + template <typename Pos, typename Fn> + static void visit_leaf(Pos&& pos, Fn&& fn) + { + auto data = pos.node()->leaf(); + fn(data, data + pos.count()); + } +}; + +struct for_each_chunk_p_visitor : visitor_base<for_each_chunk_p_visitor> +{ + using this_t = for_each_chunk_p_visitor; + + template <typename Pos, typename Fn> + static bool visit_inner(Pos&& pos, Fn&& fn) + { + return pos.each_pred(this_t{}, fn); + } + + template <typename Pos, typename Fn> + static bool visit_leaf(Pos&& pos, Fn&& fn) + { + auto data = pos.node()->leaf(); + return fn(data, data + pos.count()); + } +}; + +struct for_each_chunk_left_visitor : visitor_base<for_each_chunk_left_visitor> +{ + using this_t = for_each_chunk_left_visitor; + + template <typename Pos, typename Fn> + static void visit_inner(Pos&& pos, size_t last, Fn&& fn) + { + auto l = pos.index(last); + pos.each_left(for_each_chunk_visitor{}, l, fn); + pos.towards_oh(this_t{}, last, l, fn); + } + + template <typename Pos, typename Fn> + static void visit_leaf(Pos&& pos, size_t last, Fn&& fn) + { + auto data = pos.node()->leaf(); + auto l = pos.index(last); + fn(data, data + l + 1); + } +}; + +struct for_each_chunk_right_visitor : visitor_base<for_each_chunk_right_visitor> +{ + using this_t = for_each_chunk_right_visitor; + + template <typename Pos, typename Fn> + static void visit_inner(Pos&& pos, size_t first, Fn&& fn) + { + auto f = pos.index(first); + pos.towards_oh(this_t{}, first, f, fn); + pos.each_right(for_each_chunk_visitor{}, f + 1, fn); + } + + template <typename Pos, typename Fn> + static void visit_leaf(Pos&& pos, size_t first, Fn&& fn) + { + auto data = pos.node()->leaf(); + auto f = pos.index(first); + fn(data + f, data + pos.count()); + } +}; + +struct for_each_chunk_i_visitor : visitor_base<for_each_chunk_i_visitor> +{ + using this_t = for_each_chunk_i_visitor; + + template <typename Pos, typename Fn> + static void visit_relaxed(Pos&& pos, size_t first, size_t last, Fn&& fn) + { + // we are going towards *two* indices, so we need to do the + // relaxed as a special case to correct the second index + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + if (f == l) { + auto sbh = pos.size_before(f); + pos.towards_oh_sbh(this_t{}, first, f, sbh, last - sbh, fn); + } else { + assert(f < l); + pos.towards_oh(for_each_chunk_right_visitor{}, first, f, fn); + pos.each_i(for_each_chunk_visitor{}, f + 1, l, fn); + pos.towards_oh(for_each_chunk_left_visitor{}, last - 1, l, fn); + } + } + } + + template <typename Pos, typename Fn> + static void visit_regular(Pos&& pos, size_t first, size_t last, Fn&& fn) + { + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + if (f == l) + pos.towards_oh(this_t{}, first, f, last, fn); + else { + assert(f < l); + pos.towards_oh(for_each_chunk_right_visitor{}, first, f, fn); + pos.each_i(for_each_chunk_visitor{}, f + 1, l, fn); + pos.towards_oh(for_each_chunk_left_visitor{}, last - 1, l, fn); + } + } + } + + template <typename Pos, typename Fn> + static void visit_leaf(Pos&& pos, size_t first, size_t last, Fn&& fn) + { + auto data = pos.node()->leaf(); + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + fn(data + f, data + l + 1); + } + } +}; + +struct for_each_chunk_p_left_visitor + : visitor_base<for_each_chunk_p_left_visitor> +{ + using this_t = for_each_chunk_p_left_visitor; + + template <typename Pos, typename Fn> + static bool visit_inner(Pos&& pos, size_t last, Fn&& fn) + { + auto l = pos.index(last); + return pos.each_pred_left(for_each_chunk_p_visitor{}, l, fn) && + pos.towards_oh(this_t{}, last, l, fn); + } + + template <typename Pos, typename Fn> + static bool visit_leaf(Pos&& pos, size_t last, Fn&& fn) + { + auto data = pos.node()->leaf(); + auto l = pos.index(last); + return fn(data, data + l + 1); + } +}; + +struct for_each_chunk_p_right_visitor + : visitor_base<for_each_chunk_p_right_visitor> +{ + using this_t = for_each_chunk_p_right_visitor; + + template <typename Pos, typename Fn> + static bool visit_inner(Pos&& pos, size_t first, Fn&& fn) + { + auto f = pos.index(first); + return pos.towards_oh(this_t{}, first, f, fn) && + pos.each_pred_right(for_each_chunk_p_visitor{}, f + 1, fn); + } + + template <typename Pos, typename Fn> + static bool visit_leaf(Pos&& pos, size_t first, Fn&& fn) + { + auto data = pos.node()->leaf(); + auto f = pos.index(first); + return fn(data + f, data + pos.count()); + } +}; + +struct for_each_chunk_p_i_visitor : visitor_base<for_each_chunk_p_i_visitor> +{ + using this_t = for_each_chunk_p_i_visitor; + + template <typename Pos, typename Fn> + static bool visit_relaxed(Pos&& pos, size_t first, size_t last, Fn&& fn) + { + // we are going towards *two* indices, so we need to do the + // relaxed as a special case to correct the second index + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + if (f == l) { + auto sbh = pos.size_before(f); + return pos.towards_oh_sbh( + this_t{}, first, f, sbh, last - sbh, fn); + } else { + assert(f < l); + return pos.towards_oh( + for_each_chunk_p_right_visitor{}, first, f, fn) && + pos.each_pred_i( + for_each_chunk_p_visitor{}, f + 1, l, fn) && + pos.towards_oh( + for_each_chunk_p_left_visitor{}, last - 1, l, fn); + } + } + return true; + } + + template <typename Pos, typename Fn> + static bool visit_regular(Pos&& pos, size_t first, size_t last, Fn&& fn) + { + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + if (f == l) + return pos.towards_oh(this_t{}, first, f, last, fn); + else { + assert(f < l); + return pos.towards_oh( + for_each_chunk_p_right_visitor{}, first, f, fn) && + pos.each_pred_i( + for_each_chunk_p_visitor{}, f + 1, l, fn) && + pos.towards_oh( + for_each_chunk_p_left_visitor{}, last - 1, l, fn); + } + } + return true; + } + + template <typename Pos, typename Fn> + static bool visit_leaf(Pos&& pos, size_t first, size_t last, Fn&& fn) + { + auto data = pos.node()->leaf(); + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + return fn(data + f, data + l + 1); + } + return true; + } +}; + +struct equals_visitor : visitor_base<equals_visitor> +{ + using this_t = equals_visitor; + + struct this_aux_t : visitor_base<this_aux_t> + { + template <typename PosR, typename PosL, typename Iter> + static bool visit_inner( + PosR&& posr, count_t i, PosL&& posl, Iter&& first, size_t idx) + { + return posl.nth_sub(i, this_t{}, posr, first, idx); + } + + template <typename PosR, typename PosL, typename Iter> + static bool visit_leaf( + PosR&& posr, count_t i, PosL&& posl, Iter&& first, size_t idx) + { + return posl.nth_sub_leaf(i, this_t{}, posr, first, idx); + } + }; + + struct rrb : visitor_base<rrb> + { + template <typename PosR, typename Iter, typename Node> + static bool visit_node(PosR&& posr, + Iter&& first, + Node* rootl, + shift_t shiftl, + size_t sizel) + { + assert(shiftl <= posr.shift()); + return shiftl == posr.shift() + ? visit_maybe_relaxed_sub(rootl, + shiftl, + sizel, + this_t{}, + posr, + first, + size_t{}) + : posr.first_sub_inner( + rrb{}, first, rootl, shiftl, sizel); + } + }; + + template <typename Iter> + static auto equal_chunk_p(Iter&& iter) + { + return [iter](auto f, auto e) mutable { + if (f == &*iter) { + iter += e - f; + return true; + } + for (; f != e; ++f, ++iter) + if (*f != *iter) + return false; + return true; + }; + } + + template <typename PosL, typename PosR, typename Iter> + static bool + visit_relaxed(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) + { + auto nl = posl.node(); + auto nr = posr.node(); + if (nl == nr) + return true; + auto cl = posl.count(); + auto cr = posr.count(); + assert(cr > 0); + auto sbr = size_t{}; + auto i = count_t{}; + auto j = count_t{}; + for (; i < cl; ++i) { + auto sbl = posl.size_before(i); + for (; j + 1 < cr && (sbr = posr.size_before(j)) < sbl; ++j) + ; + auto res = + sbl == sbr + ? posr.nth_sub(j, this_aux_t{}, i, posl, first, idx + sbl) + : posl.nth_sub(i, + for_each_chunk_p_visitor{}, + this_t::equal_chunk_p(first + (idx + sbl))); + if (!res) + return false; + } + return true; + } + + template <typename PosL, typename PosR, typename Iter> + static std::enable_if_t<is_relaxed_v<PosR>, bool> + visit_regular(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) + { + return this_t::visit_relaxed(posl, posr, first, idx); + } + + template <typename PosL, typename PosR, typename Iter> + static std::enable_if_t<!is_relaxed_v<PosR>, bool> + visit_regular(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) + { + return posl.count() >= posr.count() + ? this_t::visit_regular(posl, posr.node()) + : this_t::visit_regular(posr, posl.node()); + } + + template <typename PosL, typename PosR, typename Iter> + static bool visit_leaf(PosL&& posl, PosR&& posr, Iter&& first, size_t idx) + { + if (posl.node() == posr.node()) + return true; + auto cl = posl.count(); + auto cr = posr.count(); + auto mp = std::min(cl, cr); + return std::equal(posl.node()->leaf(), + posl.node()->leaf() + mp, + posr.node()->leaf()) && + std::equal(posl.node()->leaf() + mp, + posl.node()->leaf() + posl.count(), + first + (idx + mp)); + } + + template <typename Pos, typename NodeT> + static bool visit_regular(Pos&& pos, NodeT* other) + { + auto node = pos.node(); + return node == other || pos.each_pred_zip(this_t{}, other); + } + + template <typename Pos, typename NodeT> + static bool visit_leaf(Pos&& pos, NodeT* other) + { + auto node = pos.node(); + return node == other || std::equal(node->leaf(), + node->leaf() + pos.count(), + other->leaf()); + } +}; + +template <typename NodeT> +struct update_visitor : visitor_base<update_visitor<NodeT>> +{ + using node_t = NodeT; + using this_t = update_visitor; + + template <typename Pos, typename Fn> + static node_t* visit_relaxed(Pos&& pos, size_t idx, Fn&& fn) + { + auto offset = pos.index(idx); + auto count = pos.count(); + auto node = node_t::make_inner_sr_n(count, pos.relaxed()); + try { + auto child = pos.towards_oh(this_t{}, idx, offset, fn); + node_t::do_copy_inner_sr(node, pos.node(), count); + node->inner()[offset]->dec_unsafe(); + node->inner()[offset] = child; + return node; + } catch (...) { + node_t::delete_inner_r(node, count); + throw; + } + } + + template <typename Pos, typename Fn> + static node_t* visit_regular(Pos&& pos, size_t idx, Fn&& fn) + { + auto offset = pos.index(idx); + auto count = pos.count(); + auto node = node_t::make_inner_n(count); + try { + auto child = pos.towards_oh_ch(this_t{}, idx, offset, count, fn); + node_t::do_copy_inner(node, pos.node(), count); + node->inner()[offset]->dec_unsafe(); + node->inner()[offset] = child; + return node; + } catch (...) { + node_t::delete_inner(node, count); + throw; + } + } + + template <typename Pos, typename Fn> + static node_t* visit_leaf(Pos&& pos, size_t idx, Fn&& fn) + { + auto offset = pos.index(idx); + auto node = node_t::copy_leaf(pos.node(), pos.count()); + try { + node->leaf()[offset] = + std::forward<Fn>(fn)(std::move(node->leaf()[offset])); + return node; + } catch (...) { + node_t::delete_leaf(node, pos.count()); + throw; + } + } +}; + +struct dec_visitor : visitor_base<dec_visitor> +{ + using this_t = dec_visitor; + + template <typename Pos> + static void visit_relaxed(Pos&& p) + { + using node_t = node_type<Pos>; + auto node = p.node(); + if (node->dec()) { + p.each(this_t{}); + node_t::delete_inner_r(node, p.count()); + } + } + + template <typename Pos> + static void visit_regular(Pos&& p) + { + using node_t = node_type<Pos>; + auto node = p.node(); + if (node->dec()) { + p.each(this_t{}); + node_t::delete_inner(node, p.count()); + } + } + + template <typename Pos> + static void visit_leaf(Pos&& p) + { + using node_t = node_type<Pos>; + auto node = p.node(); + if (node->dec()) { + node_t::delete_leaf(node, p.count()); + } + } +}; + +template <typename NodeT> +void dec_leaf(NodeT* node, count_t n) +{ + make_leaf_sub_pos(node, n).visit(dec_visitor{}); +} + +template <typename NodeT> +void dec_inner(NodeT* node, shift_t shift, size_t size) +{ + visit_maybe_relaxed_sub(node, shift, size, dec_visitor()); +} + +template <typename NodeT> +void dec_relaxed(NodeT* node, shift_t shift) +{ + make_relaxed_pos(node, shift, node->relaxed()).visit(dec_visitor()); +} + +template <typename NodeT> +void dec_regular(NodeT* node, shift_t shift, size_t size) +{ + make_regular_pos(node, shift, size).visit(dec_visitor()); +} + +template <typename NodeT> +void dec_empty_regular(NodeT* node) +{ + make_empty_regular_pos(node).visit(dec_visitor()); +} + +template <typename NodeT> +struct get_mut_visitor : visitor_base<get_mut_visitor<NodeT>> +{ + using node_t = NodeT; + using this_t = get_mut_visitor; + using value_t = typename NodeT::value_t; + using edit_t = typename NodeT::edit_t; + + template <typename Pos> + static value_t& + visit_relaxed(Pos&& pos, size_t idx, edit_t e, node_t** location) + { + auto offset = pos.index(idx); + auto count = pos.count(); + auto node = pos.node(); + if (node->can_mutate(e)) { + return pos.towards_oh( + this_t{}, idx, offset, e, &node->inner()[offset]); + } else { + auto new_node = node_t::copy_inner_sr_e(e, node, count); + try { + auto& res = pos.towards_oh( + this_t{}, idx, offset, e, &new_node->inner()[offset]); + pos.visit(dec_visitor{}); + *location = new_node; + return res; + } catch (...) { + dec_relaxed(new_node, pos.shift()); + throw; + } + } + } + + template <typename Pos> + static value_t& + visit_regular(Pos&& pos, size_t idx, edit_t e, node_t** location) + { + assert(pos.node() == *location); + auto offset = pos.index(idx); + auto count = pos.count(); + auto node = pos.node(); + if (node->can_mutate(e)) { + return pos.towards_oh_ch( + this_t{}, idx, offset, count, e, &node->inner()[offset]); + } else { + auto new_node = node_t::copy_inner_e(e, node, count); + try { + auto& res = pos.towards_oh_ch(this_t{}, + idx, + offset, + count, + e, + &new_node->inner()[offset]); + pos.visit(dec_visitor{}); + *location = new_node; + return res; + } catch (...) { + dec_regular(new_node, pos.shift(), pos.size()); + throw; + } + } + } + + template <typename Pos> + static value_t& + visit_leaf(Pos&& pos, size_t idx, edit_t e, node_t** location) + { + assert(pos.node() == *location); + auto node = pos.node(); + if (node->can_mutate(e)) { + return node->leaf()[pos.index(idx)]; + } else { + auto new_node = node_t::copy_leaf_e(e, pos.node(), pos.count()); + pos.visit(dec_visitor{}); + *location = new_node; + return new_node->leaf()[pos.index(idx)]; + } + } +}; + +template <typename NodeT, bool Mutating = true> +struct push_tail_mut_visitor + : visitor_base<push_tail_mut_visitor<NodeT, Mutating>> +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using this_t = push_tail_mut_visitor; + using this_no_mut_t = push_tail_mut_visitor<NodeT, false>; + using node_t = NodeT; + using edit_t = typename NodeT::edit_t; + + template <typename Pos> + static node_t* visit_relaxed(Pos&& pos, edit_t e, node_t* tail, count_t ts) + { + auto node = pos.node(); + auto level = pos.shift(); + auto idx = pos.count() - 1; + auto children = pos.size(idx); + auto new_idx = + children == size_t{1} << level || level == BL ? idx + 1 : idx; + auto new_child = static_cast<node_t*>(nullptr); + auto mutate = Mutating && node->can_mutate(e); + + if (new_idx >= branches<B>) + return nullptr; + else if (idx == new_idx) { + new_child = + mutate ? pos.last_oh_csh(this_t{}, idx, children, e, tail, ts) + : pos.last_oh_csh( + this_no_mut_t{}, idx, children, e, tail, ts); + if (!new_child) { + if (++new_idx < branches<B>) + new_child = node_t::make_path_e(e, level - B, tail); + else + return nullptr; + } + } else + new_child = node_t::make_path_e(e, level - B, tail); + + if (mutate) { + auto count = new_idx + 1; + auto relaxed = node->ensure_mutable_relaxed_n(e, new_idx); + node->inner()[new_idx] = new_child; + relaxed->d.sizes[new_idx] = pos.size() + ts; + relaxed->d.count = count; + return node; + } else { + try { + auto count = new_idx + 1; + auto new_node = node_t::copy_inner_r_e(e, pos.node(), new_idx); + auto relaxed = new_node->relaxed(); + new_node->inner()[new_idx] = new_child; + relaxed->d.sizes[new_idx] = pos.size() + ts; + relaxed->d.count = count; + if (Mutating) + pos.visit(dec_visitor{}); + return new_node; + } catch (...) { + auto shift = pos.shift(); + auto size = new_idx == idx ? children + ts : ts; + if (shift > BL) { + tail->inc(); + dec_inner(new_child, shift - B, size); + } + throw; + } + } + } + + template <typename Pos, typename... Args> + static node_t* visit_regular(Pos&& pos, edit_t e, node_t* tail, Args&&...) + { + assert((pos.size() & mask<BL>) == 0); + auto node = pos.node(); + auto idx = pos.index(pos.size() - 1); + auto new_idx = pos.index(pos.size() + branches<BL> - 1); + auto mutate = Mutating && node->can_mutate(e); + if (mutate) { + node->inner()[new_idx] = + idx == new_idx ? pos.last_oh(this_t{}, idx, e, tail) + /* otherwise */ + : node_t::make_path_e(e, pos.shift() - B, tail); + return node; + } else { + auto new_parent = node_t::make_inner_e(e); + try { + new_parent->inner()[new_idx] = + idx == new_idx + ? pos.last_oh(this_no_mut_t{}, idx, e, tail) + /* otherwise */ + : node_t::make_path_e(e, pos.shift() - B, tail); + node_t::do_copy_inner(new_parent, node, new_idx); + if (Mutating) + pos.visit(dec_visitor{}); + return new_parent; + } catch (...) { + node_t::delete_inner_e(new_parent); + throw; + } + } + } + + template <typename Pos, typename... Args> + static node_t* visit_leaf(Pos&& pos, edit_t e, node_t* tail, Args&&...) + { + IMMER_UNREACHABLE; + } +}; + +template <typename NodeT> +struct push_tail_visitor : visitor_base<push_tail_visitor<NodeT>> +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using this_t = push_tail_visitor; + using node_t = NodeT; + + template <typename Pos> + static node_t* visit_relaxed(Pos&& pos, node_t* tail, count_t ts) + { + auto level = pos.shift(); + auto idx = pos.count() - 1; + auto children = pos.size(idx); + auto new_idx = + children == size_t{1} << level || level == BL ? idx + 1 : idx; + auto new_child = static_cast<node_t*>(nullptr); + if (new_idx >= branches<B>) + return nullptr; + else if (idx == new_idx) { + new_child = pos.last_oh_csh(this_t{}, idx, children, tail, ts); + if (!new_child) { + if (++new_idx < branches<B>) + new_child = node_t::make_path(level - B, tail); + else + return nullptr; + } + } else + new_child = node_t::make_path(level - B, tail); + try { + auto count = new_idx + 1; + auto new_parent = + node_t::copy_inner_r_n(count, pos.node(), new_idx); + auto new_relaxed = new_parent->relaxed(); + new_parent->inner()[new_idx] = new_child; + new_relaxed->d.sizes[new_idx] = pos.size() + ts; + new_relaxed->d.count = count; + return new_parent; + } catch (...) { + auto shift = pos.shift(); + auto size = new_idx == idx ? children + ts : ts; + if (shift > BL) { + tail->inc(); + dec_inner(new_child, shift - B, size); + } + throw; + } + } + + template <typename Pos, typename... Args> + static node_t* visit_regular(Pos&& pos, node_t* tail, Args&&...) + { + assert((pos.size() & mask<BL>) == 0); + auto idx = pos.index(pos.size() - 1); + auto new_idx = pos.index(pos.size() + branches<BL> - 1); + auto count = new_idx + 1; + auto new_parent = node_t::make_inner_n(count); + try { + new_parent->inner()[new_idx] = + idx == new_idx ? pos.last_oh(this_t{}, idx, tail) + /* otherwise */ + : node_t::make_path(pos.shift() - B, tail); + } catch (...) { + node_t::delete_inner(new_parent, count); + throw; + } + return node_t::do_copy_inner(new_parent, pos.node(), new_idx); + } + + template <typename Pos, typename... Args> + static node_t* visit_leaf(Pos&& pos, node_t* tail, Args&&...) + { + IMMER_UNREACHABLE; + } +}; + +struct dec_right_visitor : visitor_base<dec_right_visitor> +{ + using this_t = dec_right_visitor; + using dec_t = dec_visitor; + + template <typename Pos> + static void visit_relaxed(Pos&& p, count_t idx) + { + using node_t = node_type<Pos>; + auto node = p.node(); + if (node->dec()) { + p.each_right(dec_t{}, idx); + node_t::delete_inner_r(node, p.count()); + } + } + + template <typename Pos> + static void visit_regular(Pos&& p, count_t idx) + { + using node_t = node_type<Pos>; + auto node = p.node(); + if (node->dec()) { + p.each_right(dec_t{}, idx); + node_t::delete_inner(node, p.count()); + } + } + + template <typename Pos> + static void visit_leaf(Pos&& p, count_t idx) + { + IMMER_UNREACHABLE; + } +}; + +template <typename NodeT, bool Collapse = true, bool Mutating = true> +struct slice_right_mut_visitor + : visitor_base<slice_right_mut_visitor<NodeT, Collapse, Mutating>> +{ + using node_t = NodeT; + using this_t = slice_right_mut_visitor; + using edit_t = typename NodeT::edit_t; + + // returns a new shift, new root, the new tail size and the new tail + using result_t = std::tuple<shift_t, NodeT*, count_t, NodeT*>; + using no_collapse_t = slice_right_mut_visitor<NodeT, false, true>; + using no_collapse_no_mut_t = slice_right_mut_visitor<NodeT, false, false>; + using no_mut_t = slice_right_mut_visitor<NodeT, Collapse, false>; + + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + template <typename PosT> + static result_t visit_relaxed(PosT&& pos, size_t last, edit_t e) + { + auto idx = pos.index(last); + auto node = pos.node(); + auto mutate = Mutating && node->can_mutate(e); + if (Collapse && idx == 0) { + auto res = mutate ? pos.towards_oh(this_t{}, last, idx, e) + : pos.towards_oh(no_mut_t{}, last, idx, e); + if (Mutating) + pos.visit(dec_right_visitor{}, count_t{1}); + return res; + } else { + using std::get; + auto subs = + mutate ? pos.towards_oh(no_collapse_t{}, last, idx, e) + : pos.towards_oh(no_collapse_no_mut_t{}, last, idx, e); + auto next = get<1>(subs); + auto ts = get<2>(subs); + auto tail = get<3>(subs); + try { + if (next) { + if (mutate) { + auto nodr = node->ensure_mutable_relaxed_n(e, idx); + pos.each_right(dec_visitor{}, idx + 1); + node->inner()[idx] = next; + nodr->d.sizes[idx] = last + 1 - ts; + nodr->d.count = idx + 1; + return std::make_tuple(pos.shift(), node, ts, tail); + } else { + auto newn = node_t::copy_inner_r_e(e, node, idx); + auto newr = newn->relaxed(); + newn->inner()[idx] = next; + newr->d.sizes[idx] = last + 1 - ts; + newr->d.count = idx + 1; + if (Mutating) + pos.visit(dec_visitor{}); + return std::make_tuple(pos.shift(), newn, ts, tail); + } + } else if (idx == 0) { + if (Mutating) + pos.visit(dec_right_visitor{}, count_t{1}); + return std::make_tuple(pos.shift(), nullptr, ts, tail); + } else if (Collapse && idx == 1 && pos.shift() > BL) { + auto newn = pos.node()->inner()[0]; + if (!mutate) + newn->inc(); + if (Mutating) + pos.visit(dec_right_visitor{}, count_t{2}); + return std::make_tuple(pos.shift() - B, newn, ts, tail); + } else { + if (mutate) { + pos.each_right(dec_visitor{}, idx + 1); + node->ensure_mutable_relaxed_n(e, idx)->d.count = idx; + return std::make_tuple(pos.shift(), node, ts, tail); + } else { + auto newn = node_t::copy_inner_r_e(e, node, idx); + if (Mutating) + pos.visit(dec_visitor{}); + return std::make_tuple(pos.shift(), newn, ts, tail); + } + } + } catch (...) { + assert(!mutate); + assert(!next || pos.shift() > BL); + if (next) + dec_inner(next, + pos.shift() - B, + last + 1 - ts - pos.size_before(idx)); + dec_leaf(tail, ts); + throw; + } + } + } + + template <typename PosT> + static result_t visit_regular(PosT&& pos, size_t last, edit_t e) + { + auto idx = pos.index(last); + auto node = pos.node(); + auto mutate = Mutating && node->can_mutate(e); + if (Collapse && idx == 0) { + auto res = mutate ? pos.towards_oh(this_t{}, last, idx, e) + : pos.towards_oh(no_mut_t{}, last, idx, e); + if (Mutating) + pos.visit(dec_right_visitor{}, count_t{1}); + return res; + } else { + using std::get; + auto subs = + mutate ? pos.towards_oh(no_collapse_t{}, last, idx, e) + : pos.towards_oh(no_collapse_no_mut_t{}, last, idx, e); + auto next = get<1>(subs); + auto ts = get<2>(subs); + auto tail = get<3>(subs); + try { + if (next) { + if (mutate) { + node->inner()[idx] = next; + pos.each_right(dec_visitor{}, idx + 1); + return std::make_tuple(pos.shift(), node, ts, tail); + } else { + auto newn = node_t::copy_inner_e(e, node, idx); + newn->inner()[idx] = next; + if (Mutating) + pos.visit(dec_visitor{}); + return std::make_tuple(pos.shift(), newn, ts, tail); + } + } else if (idx == 0) { + if (Mutating) + pos.visit(dec_right_visitor{}, count_t{1}); + return std::make_tuple(pos.shift(), nullptr, ts, tail); + } else if (Collapse && idx == 1 && pos.shift() > BL) { + auto newn = pos.node()->inner()[0]; + if (!mutate) + newn->inc(); + if (Mutating) + pos.visit(dec_right_visitor{}, count_t{2}); + return std::make_tuple(pos.shift() - B, newn, ts, tail); + } else { + if (mutate) { + pos.each_right(dec_visitor{}, idx + 1); + return std::make_tuple(pos.shift(), node, ts, tail); + } else { + auto newn = node_t::copy_inner_e(e, node, idx); + if (Mutating) + pos.visit(dec_visitor{}); + return std::make_tuple(pos.shift(), newn, ts, tail); + } + } + } catch (...) { + assert(!mutate); + assert(!next || pos.shift() > BL); + assert(tail); + if (next) + dec_regular(next, pos.shift() - B, last + 1 - ts); + dec_leaf(tail, ts); + throw; + } + } + } + + template <typename PosT> + static result_t visit_leaf(PosT&& pos, size_t last, edit_t e) + { + auto old_tail_size = pos.count(); + auto new_tail_size = pos.index(last) + 1; + auto node = pos.node(); + auto mutate = Mutating && node->can_mutate(e); + if (new_tail_size == old_tail_size) { + if (!Mutating) + node->inc(); + return std::make_tuple(0, nullptr, new_tail_size, node); + } else if (mutate) { + destroy_n(node->leaf() + new_tail_size, + old_tail_size - new_tail_size); + return std::make_tuple(0, nullptr, new_tail_size, node); + } else { + auto new_tail = node_t::copy_leaf_e(e, node, new_tail_size); + if (Mutating) + pos.visit(dec_visitor{}); + return std::make_tuple(0, nullptr, new_tail_size, new_tail); + } + } +}; + +template <typename NodeT, bool Collapse = true> +struct slice_right_visitor : visitor_base<slice_right_visitor<NodeT, Collapse>> +{ + using node_t = NodeT; + using this_t = slice_right_visitor; + + // returns a new shift, new root, the new tail size and the new tail + using result_t = std::tuple<shift_t, NodeT*, count_t, NodeT*>; + using no_collapse_t = slice_right_visitor<NodeT, false>; + + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + template <typename PosT> + static result_t visit_relaxed(PosT&& pos, size_t last) + { + auto idx = pos.index(last); + if (Collapse && idx == 0) { + return pos.towards_oh(this_t{}, last, idx); + } else { + using std::get; + auto subs = pos.towards_oh(no_collapse_t{}, last, idx); + auto next = get<1>(subs); + auto ts = get<2>(subs); + auto tail = get<3>(subs); + try { + if (next) { + auto count = idx + 1; + auto newn = node_t::copy_inner_r_n(count, pos.node(), idx); + auto newr = newn->relaxed(); + newn->inner()[idx] = next; + newr->d.sizes[idx] = last + 1 - ts; + newr->d.count = count; + return std::make_tuple(pos.shift(), newn, ts, tail); + } else if (idx == 0) { + return std::make_tuple(pos.shift(), nullptr, ts, tail); + } else if (Collapse && idx == 1 && pos.shift() > BL) { + auto newn = pos.node()->inner()[0]; + return std::make_tuple( + pos.shift() - B, newn->inc(), ts, tail); + } else { + auto newn = node_t::copy_inner_r(pos.node(), idx); + return std::make_tuple(pos.shift(), newn, ts, tail); + } + } catch (...) { + assert(!next || pos.shift() > BL); + if (next) + dec_inner(next, + pos.shift() - B, + last + 1 - ts - pos.size_before(idx)); + if (tail) + dec_leaf(tail, ts); + throw; + } + } + } + + template <typename PosT> + static result_t visit_regular(PosT&& pos, size_t last) + { + auto idx = pos.index(last); + if (Collapse && idx == 0) { + return pos.towards_oh(this_t{}, last, idx); + } else { + using std::get; + auto subs = pos.towards_oh(no_collapse_t{}, last, idx); + auto next = get<1>(subs); + auto ts = get<2>(subs); + auto tail = get<3>(subs); + try { + if (next) { + auto newn = node_t::copy_inner_n(idx + 1, pos.node(), idx); + newn->inner()[idx] = next; + return std::make_tuple(pos.shift(), newn, ts, tail); + } else if (idx == 0) { + return std::make_tuple(pos.shift(), nullptr, ts, tail); + } else if (Collapse && idx == 1 && pos.shift() > BL) { + auto newn = pos.node()->inner()[0]; + return std::make_tuple( + pos.shift() - B, newn->inc(), ts, tail); + } else { + auto newn = node_t::copy_inner_n(idx, pos.node(), idx); + return std::make_tuple(pos.shift(), newn, ts, tail); + } + } catch (...) { + assert(!next || pos.shift() > BL); + assert(tail); + if (next) + dec_regular(next, pos.shift() - B, last + 1 - ts); + dec_leaf(tail, ts); + throw; + } + } + } + + template <typename PosT> + static result_t visit_leaf(PosT&& pos, size_t last) + { + auto old_tail_size = pos.count(); + auto new_tail_size = pos.index(last) + 1; + auto new_tail = new_tail_size == old_tail_size + ? pos.node()->inc() + : node_t::copy_leaf(pos.node(), new_tail_size); + return std::make_tuple(0, nullptr, new_tail_size, new_tail); + } +}; + +struct dec_left_visitor : visitor_base<dec_left_visitor> +{ + using this_t = dec_left_visitor; + using dec_t = dec_visitor; + + template <typename Pos> + static void visit_relaxed(Pos&& p, count_t idx) + { + using node_t = node_type<Pos>; + auto node = p.node(); + if (node->dec()) { + p.each_left(dec_t{}, idx); + node_t::delete_inner_r(node, p.count()); + } + } + + template <typename Pos> + static void visit_regular(Pos&& p, count_t idx) + { + using node_t = node_type<Pos>; + auto node = p.node(); + if (node->dec()) { + p.each_left(dec_t{}, idx); + node_t::delete_inner(node, p.count()); + } + } + + template <typename Pos> + static void visit_leaf(Pos&& p, count_t idx) + { + IMMER_UNREACHABLE; + } +}; + +template <typename NodeT, bool Collapse = true, bool Mutating = true> +struct slice_left_mut_visitor + : visitor_base<slice_left_mut_visitor<NodeT, Collapse, Mutating>> +{ + using node_t = NodeT; + using this_t = slice_left_mut_visitor; + using edit_t = typename NodeT::edit_t; + using value_t = typename NodeT::value_t; + using relaxed_t = typename NodeT::relaxed_t; + // returns a new shift and new root + using result_t = std::tuple<shift_t, NodeT*>; + + using no_collapse_t = slice_left_mut_visitor<NodeT, false, true>; + using no_collapse_no_mut_t = slice_left_mut_visitor<NodeT, false, false>; + using no_mut_t = slice_left_mut_visitor<NodeT, Collapse, false>; + + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + template <typename PosT> + static result_t visit_relaxed(PosT&& pos, size_t first, edit_t e) + { + auto idx = pos.subindex(first); + auto count = pos.count(); + auto node = pos.node(); + auto mutate = Mutating && node->can_mutate(e); + auto left_size = pos.size_before(idx); + auto child_size = pos.size_sbh(idx, left_size); + auto dropped_size = first; + auto child_dropped_size = dropped_size - left_size; + if (Collapse && pos.shift() > BL && idx == pos.count() - 1) { + auto r = mutate ? pos.towards_sub_oh(this_t{}, first, idx, e) + : pos.towards_sub_oh(no_mut_t{}, first, idx, e); + if (Mutating) + pos.visit(dec_left_visitor{}, idx); + return r; + } else { + using std::get; + auto newn = mutate ? (node->ensure_mutable_relaxed(e), node) + : node_t::make_inner_r_e(e); + auto newr = newn->relaxed(); + auto newcount = count - idx; + auto new_child_size = child_size - child_dropped_size; + try { + auto subs = + mutate ? pos.towards_sub_oh(no_collapse_t{}, first, idx, e) + : pos.towards_sub_oh( + no_collapse_no_mut_t{}, first, idx, e); + if (mutate) + pos.each_left(dec_visitor{}, idx); + pos.copy_sizes( + idx + 1, newcount - 1, new_child_size, newr->d.sizes + 1); + std::uninitialized_copy(node->inner() + idx + 1, + node->inner() + count, + newn->inner() + 1); + newn->inner()[0] = get<1>(subs); + newr->d.sizes[0] = new_child_size; + newr->d.count = newcount; + if (!mutate) { + node_t::inc_nodes(newn->inner() + 1, newcount - 1); + if (Mutating) + pos.visit(dec_visitor{}); + } + return std::make_tuple(pos.shift(), newn); + } catch (...) { + if (!mutate) + node_t::delete_inner_r_e(newn); + throw; + } + } + } + + template <typename PosT> + static result_t visit_regular(PosT&& pos, size_t first, edit_t e) + { + auto idx = pos.subindex(first); + auto count = pos.count(); + auto node = pos.node(); + auto mutate = Mutating + // this is more restrictive than actually needed because + // it causes the algorithm to also avoid mutating the leaf + // in place + && !node_t::embed_relaxed && node->can_mutate(e); + auto left_size = pos.size_before(idx); + auto child_size = pos.size_sbh(idx, left_size); + auto dropped_size = first; + auto child_dropped_size = dropped_size - left_size; + if (Collapse && pos.shift() > BL && idx == pos.count() - 1) { + auto r = mutate ? pos.towards_sub_oh(this_t{}, first, idx, e) + : pos.towards_sub_oh(no_mut_t{}, first, idx, e); + if (Mutating) + pos.visit(dec_left_visitor{}, idx); + return r; + } else { + using std::get; + // if possible, we convert the node to a relaxed one + // simply by allocating a `relaxed_t` size table for + // it... maybe some of this magic should be moved as a + // `node<...>` static method... + auto newcount = count - idx; + auto newn = + mutate ? (node->impl.d.data.inner.relaxed = new ( + node_t::heap::allocate(node_t::max_sizeof_relaxed, + norefs_tag{})) relaxed_t, + node) + : node_t::make_inner_r_e(e); + auto newr = newn->relaxed(); + try { + auto subs = + mutate ? pos.towards_sub_oh(no_collapse_t{}, first, idx, e) + : pos.towards_sub_oh( + no_collapse_no_mut_t{}, first, idx, e); + if (mutate) + pos.each_left(dec_visitor{}, idx); + newr->d.sizes[0] = child_size - child_dropped_size; + pos.copy_sizes( + idx + 1, newcount - 1, newr->d.sizes[0], newr->d.sizes + 1); + newr->d.count = newcount; + newn->inner()[0] = get<1>(subs); + std::uninitialized_copy(node->inner() + idx + 1, + node->inner() + count, + newn->inner() + 1); + if (!mutate) { + node_t::inc_nodes(newn->inner() + 1, newcount - 1); + if (Mutating) + pos.visit(dec_visitor{}); + } + return std::make_tuple(pos.shift(), newn); + } catch (...) { + if (!mutate) + node_t::delete_inner_r_e(newn); + else { + // restore the regular node that we were + // attempting to relax... + node_t::heap::deallocate(node_t::max_sizeof_relaxed, + node->impl.d.data.inner.relaxed); + node->impl.d.data.inner.relaxed = nullptr; + } + throw; + } + } + } + + template <typename PosT> + static result_t visit_leaf(PosT&& pos, size_t first, edit_t e) + { + auto node = pos.node(); + auto idx = pos.index(first); + auto count = pos.count(); + auto mutate = Mutating && + std::is_nothrow_move_constructible<value_t>::value && + node->can_mutate(e); + if (mutate) { + auto data = node->leaf(); + auto newcount = count - idx; + std::move(data + idx, data + count, data); + destroy_n(data + newcount, idx); + return std::make_tuple(0, node); + } else { + auto newn = node_t::copy_leaf_e(e, node, idx, count); + if (Mutating) + pos.visit(dec_visitor{}); + return std::make_tuple(0, newn); + } + } +}; + +template <typename NodeT, bool Collapse = true> +struct slice_left_visitor : visitor_base<slice_left_visitor<NodeT, Collapse>> +{ + using node_t = NodeT; + using this_t = slice_left_visitor; + + // returns a new shift and new root + using result_t = std::tuple<shift_t, NodeT*>; + using no_collapse_t = slice_left_visitor<NodeT, false>; + + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + template <typename PosT> + static result_t visit_inner(PosT&& pos, size_t first) + { + auto idx = pos.subindex(first); + auto count = pos.count(); + auto left_size = pos.size_before(idx); + auto child_size = pos.size_sbh(idx, left_size); + auto dropped_size = first; + auto child_dropped_size = dropped_size - left_size; + if (Collapse && pos.shift() > BL && idx == pos.count() - 1) { + return pos.towards_sub_oh(this_t{}, first, idx); + } else { + using std::get; + auto n = pos.node(); + auto newc = count - idx; + auto newn = node_t::make_inner_r_n(newc); + try { + auto subs = pos.towards_sub_oh(no_collapse_t{}, first, idx); + auto newr = newn->relaxed(); + newr->d.count = count - idx; + newr->d.sizes[0] = child_size - child_dropped_size; + pos.copy_sizes(idx + 1, + newr->d.count - 1, + newr->d.sizes[0], + newr->d.sizes + 1); + assert(newr->d.sizes[newr->d.count - 1] == + pos.size() - dropped_size); + newn->inner()[0] = get<1>(subs); + std::uninitialized_copy(n->inner() + idx + 1, + n->inner() + count, + newn->inner() + 1); + node_t::inc_nodes(newn->inner() + 1, newr->d.count - 1); + return std::make_tuple(pos.shift(), newn); + } catch (...) { + node_t::delete_inner_r(newn, newc); + throw; + } + } + } + + template <typename PosT> + static result_t visit_leaf(PosT&& pos, size_t first) + { + auto n = node_t::copy_leaf(pos.node(), pos.index(first), pos.count()); + return std::make_tuple(0, n); + } +}; + +template <typename Node> +struct concat_center_pos +{ + static constexpr auto B = Node::bits; + static constexpr auto BL = Node::bits_leaf; + + static constexpr count_t max_children = 3; + + using node_t = Node; + using edit_t = typename Node::edit_t; + + shift_t shift_ = 0u; + count_t count_ = 0u; + node_t* nodes_[max_children]; + size_t sizes_[max_children]; + + auto shift() const { return shift_; } + + concat_center_pos(shift_t s, Node* n0, size_t s0) + : shift_{s} + , count_{1} + , nodes_{n0} + , sizes_{s0} + {} + + concat_center_pos(shift_t s, Node* n0, size_t s0, Node* n1, size_t s1) + : shift_{s} + , count_{2} + , nodes_{n0, n1} + , sizes_{s0, s1} + {} + + concat_center_pos(shift_t s, + Node* n0, + size_t s0, + Node* n1, + size_t s1, + Node* n2, + size_t s2) + : shift_{s} + , count_{3} + , nodes_{n0, n1, n2} + , sizes_{s0, s1, s2} + {} + + template <typename Visitor, typename... Args> + void each_sub(Visitor v, Args&&... args) + { + if (shift_ == BL) { + for (auto i = count_t{0}; i < count_; ++i) + make_leaf_sub_pos(nodes_[i], sizes_[i]).visit(v, args...); + } else { + for (auto i = count_t{0}; i < count_; ++i) + make_relaxed_pos(nodes_[i], shift_ - B, nodes_[i]->relaxed()) + .visit(v, args...); + } + } + + relaxed_pos<Node> realize() && + { + if (count_ > 1) { + try { + auto result = node_t::make_inner_r_n(count_); + auto r = result->relaxed(); + r->d.count = count_; + std::copy(nodes_, nodes_ + count_, result->inner()); + std::copy(sizes_, sizes_ + count_, r->d.sizes); + return {result, shift_, r}; + } catch (...) { + each_sub(dec_visitor{}); + throw; + } + } else { + assert(shift_ >= B + BL); + return {nodes_[0], shift_ - B, nodes_[0]->relaxed()}; + } + } + + relaxed_pos<Node> realize_e(edit_t e) + { + if (count_ > 1) { + auto result = node_t::make_inner_r_e(e); + auto r = result->relaxed(); + r->d.count = count_; + std::copy(nodes_, nodes_ + count_, result->inner()); + std::copy(sizes_, sizes_ + count_, r->d.sizes); + return {result, shift_, r}; + } else { + assert(shift_ >= B + BL); + return {nodes_[0], shift_ - B, nodes_[0]->relaxed()}; + } + } +}; + +template <typename Node> +struct concat_merger +{ + using node_t = Node; + static constexpr auto B = Node::bits; + static constexpr auto BL = Node::bits_leaf; + + using result_t = concat_center_pos<Node>; + + count_t* curr_; + count_t n_; + result_t result_; + + concat_merger(shift_t shift, count_t* counts, count_t n) + : curr_{counts} + , n_{n} + , result_{ + shift + B, node_t::make_inner_r_n(std::min(n_, branches<B>)), 0} + {} + + node_t* to_ = {}; + count_t to_offset_ = {}; + size_t to_size_ = {}; + + void add_child(node_t* p, size_t size) + { + ++curr_; + auto parent = result_.nodes_[result_.count_ - 1]; + auto relaxed = parent->relaxed(); + if (relaxed->d.count == branches<B>) { + assert(result_.count_ < result_t::max_children); + n_ -= branches<B>; + parent = node_t::make_inner_r_n(std::min(n_, branches<B>)); + relaxed = parent->relaxed(); + result_.nodes_[result_.count_] = parent; + result_.sizes_[result_.count_] = result_.sizes_[result_.count_ - 1]; + ++result_.count_; + } + auto idx = relaxed->d.count++; + result_.sizes_[result_.count_ - 1] += size; + relaxed->d.sizes[idx] = size + (idx ? relaxed->d.sizes[idx - 1] : 0); + parent->inner()[idx] = p; + }; + + template <typename Pos> + void merge_leaf(Pos&& p) + { + auto from = p.node(); + auto from_size = p.size(); + auto from_count = p.count(); + assert(from_size); + if (!to_ && *curr_ == from_count) { + add_child(from, from_size); + from->inc(); + } else { + auto from_offset = count_t{}; + auto from_data = from->leaf(); + do { + if (!to_) { + to_ = node_t::make_leaf_n(*curr_); + to_offset_ = 0; + } + auto data = to_->leaf(); + auto to_copy = + std::min(from_count - from_offset, *curr_ - to_offset_); + std::uninitialized_copy(from_data + from_offset, + from_data + from_offset + to_copy, + data + to_offset_); + to_offset_ += to_copy; + from_offset += to_copy; + if (*curr_ == to_offset_) { + add_child(to_, to_offset_); + to_ = nullptr; + } + } while (from_offset != from_count); + } + } + + template <typename Pos> + void merge_inner(Pos&& p) + { + auto from = p.node(); + auto from_size = p.size(); + auto from_count = p.count(); + assert(from_size); + if (!to_ && *curr_ == from_count) { + add_child(from, from_size); + from->inc(); + } else { + auto from_offset = count_t{}; + auto from_data = from->inner(); + do { + if (!to_) { + to_ = node_t::make_inner_r_n(*curr_); + to_offset_ = 0; + to_size_ = 0; + } + auto data = to_->inner(); + auto to_copy = + std::min(from_count - from_offset, *curr_ - to_offset_); + std::uninitialized_copy(from_data + from_offset, + from_data + from_offset + to_copy, + data + to_offset_); + node_t::inc_nodes(from_data + from_offset, to_copy); + auto sizes = to_->relaxed()->d.sizes; + p.copy_sizes( + from_offset, to_copy, to_size_, sizes + to_offset_); + to_offset_ += to_copy; + from_offset += to_copy; + to_size_ = sizes[to_offset_ - 1]; + if (*curr_ == to_offset_) { + to_->relaxed()->d.count = to_offset_; + add_child(to_, to_size_); + to_ = nullptr; + } + } while (from_offset != from_count); + } + } + + concat_center_pos<Node> finish() const + { + assert(!to_); + return result_; + } + + void abort() + { + auto shift = result_.shift_ - B; + if (to_) { + if (shift == BL) + node_t::delete_leaf(to_, to_offset_); + else { + to_->relaxed()->d.count = to_offset_; + dec_relaxed(to_, shift - B); + } + } + result_.each_sub(dec_visitor()); + } +}; + +struct concat_merger_visitor : visitor_base<concat_merger_visitor> +{ + using this_t = concat_merger_visitor; + + template <typename Pos, typename Merger> + static void visit_inner(Pos&& p, Merger& merger) + { + merger.merge_inner(p); + } + + template <typename Pos, typename Merger> + static void visit_leaf(Pos&& p, Merger& merger) + { + merger.merge_leaf(p); + } +}; + +struct concat_rebalance_plan_fill_visitor + : visitor_base<concat_rebalance_plan_fill_visitor> +{ + using this_t = concat_rebalance_plan_fill_visitor; + + template <typename Pos, typename Plan> + static void visit_node(Pos&& p, Plan& plan) + { + auto count = p.count(); + assert(plan.n < Plan::max_children); + plan.counts[plan.n++] = count; + plan.total += count; + } +}; + +template <bits_t B, bits_t BL> +struct concat_rebalance_plan +{ + static constexpr auto max_children = 2 * branches<B> + 1; + + count_t counts[max_children]; + count_t n = 0u; + count_t total = 0u; + + template <typename LPos, typename CPos, typename RPos> + void fill(LPos&& lpos, CPos&& cpos, RPos&& rpos) + { + assert(n == 0u); + assert(total == 0u); + using visitor_t = concat_rebalance_plan_fill_visitor; + lpos.each_left_sub(visitor_t{}, *this); + cpos.each_sub(visitor_t{}, *this); + rpos.each_right_sub(visitor_t{}, *this); + } + + void shuffle(shift_t shift) + { + // gcc seems to not really understand this code... :( +#if !defined(_MSC_VER) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif + constexpr count_t rrb_extras = 2; + constexpr count_t rrb_invariant = 1; + const auto bits = shift == BL ? BL : B; + const auto branches = count_t{1} << bits; + const auto optimal = ((total - 1) >> bits) + 1; + count_t i = 0; + while (n >= optimal + rrb_extras) { + // skip ok nodes + while (counts[i] > branches - rrb_invariant) + i++; + // short node, redistribute + auto remaining = counts[i]; + do { + auto count = std::min(remaining + counts[i + 1], branches); + counts[i] = count; + remaining += counts[i + 1] - count; + ++i; + } while (remaining > 0); + // remove node + std::move(counts + i + 1, counts + n, counts + i); + --n; + --i; + } +#if !defined(_MSC_VER) +#pragma GCC diagnostic pop +#endif + } + + template <typename LPos, typename CPos, typename RPos> + concat_center_pos<node_type<CPos>> + merge(LPos&& lpos, CPos&& cpos, RPos&& rpos) + { + using node_t = node_type<CPos>; + using merger_t = concat_merger<node_t>; + using visitor_t = concat_merger_visitor; + auto merger = merger_t{cpos.shift(), counts, n}; + try { + lpos.each_left_sub(visitor_t{}, merger); + cpos.each_sub(visitor_t{}, merger); + rpos.each_right_sub(visitor_t{}, merger); + cpos.each_sub(dec_visitor{}); + return merger.finish(); + } catch (...) { + merger.abort(); + throw; + } + } +}; + +template <typename Node, typename LPos, typename CPos, typename RPos> +concat_center_pos<Node> concat_rebalance(LPos&& lpos, CPos&& cpos, RPos&& rpos) +{ + auto plan = concat_rebalance_plan<Node::bits, Node::bits_leaf>{}; + plan.fill(lpos, cpos, rpos); + plan.shuffle(cpos.shift()); + try { + return plan.merge(lpos, cpos, rpos); + } catch (...) { + cpos.each_sub(dec_visitor{}); + throw; + } +} + +template <typename Node, typename LPos, typename TPos, typename RPos> +concat_center_pos<Node> concat_leafs(LPos&& lpos, TPos&& tpos, RPos&& rpos) +{ + static_assert(Node::bits >= 2, ""); + assert(lpos.shift() == tpos.shift()); + assert(lpos.shift() == rpos.shift()); + assert(lpos.shift() == 0); + if (tpos.count() > 0) + return { + Node::bits_leaf, + lpos.node()->inc(), + lpos.count(), + tpos.node()->inc(), + tpos.count(), + rpos.node()->inc(), + rpos.count(), + }; + else + return { + Node::bits_leaf, + lpos.node()->inc(), + lpos.count(), + rpos.node()->inc(), + rpos.count(), + }; +} + +template <typename Node> +struct concat_left_visitor; +template <typename Node> +struct concat_right_visitor; +template <typename Node> +struct concat_both_visitor; + +template <typename Node, typename LPos, typename TPos, typename RPos> +concat_center_pos<Node> concat_inners(LPos&& lpos, TPos&& tpos, RPos&& rpos) +{ + auto lshift = lpos.shift(); + auto rshift = rpos.shift(); + if (lshift > rshift) { + auto cpos = lpos.last_sub(concat_left_visitor<Node>{}, tpos, rpos); + return concat_rebalance<Node>(lpos, cpos, null_sub_pos{}); + } else if (lshift < rshift) { + auto cpos = rpos.first_sub(concat_right_visitor<Node>{}, lpos, tpos); + return concat_rebalance<Node>(null_sub_pos{}, cpos, rpos); + } else { + assert(lshift == rshift); + assert(Node::bits_leaf == 0u || lshift > 0); + auto cpos = lpos.last_sub(concat_both_visitor<Node>{}, tpos, rpos); + return concat_rebalance<Node>(lpos, cpos, rpos); + } +} + +template <typename Node> +struct concat_left_visitor : visitor_base<concat_left_visitor<Node>> +{ + using this_t = concat_left_visitor; + + template <typename LPos, typename TPos, typename RPos> + static concat_center_pos<Node> + visit_inner(LPos&& lpos, TPos&& tpos, RPos&& rpos) + { + return concat_inners<Node>(lpos, tpos, rpos); + } + + template <typename LPos, typename TPos, typename RPos> + static concat_center_pos<Node> + visit_leaf(LPos&& lpos, TPos&& tpos, RPos&& rpos) + { + IMMER_UNREACHABLE; + } +}; + +template <typename Node> +struct concat_right_visitor : visitor_base<concat_right_visitor<Node>> +{ + using this_t = concat_right_visitor; + + template <typename RPos, typename LPos, typename TPos> + static concat_center_pos<Node> + visit_inner(RPos&& rpos, LPos&& lpos, TPos&& tpos) + { + return concat_inners<Node>(lpos, tpos, rpos); + } + + template <typename RPos, typename LPos, typename TPos> + static concat_center_pos<Node> + visit_leaf(RPos&& rpos, LPos&& lpos, TPos&& tpos) + { + return concat_leafs<Node>(lpos, tpos, rpos); + } +}; + +template <typename Node> +struct concat_both_visitor : visitor_base<concat_both_visitor<Node>> +{ + using this_t = concat_both_visitor; + + template <typename LPos, typename TPos, typename RPos> + static concat_center_pos<Node> + visit_inner(LPos&& lpos, TPos&& tpos, RPos&& rpos) + { + return rpos.first_sub(concat_right_visitor<Node>{}, lpos, tpos); + } + + template <typename LPos, typename TPos, typename RPos> + static concat_center_pos<Node> + visit_leaf(LPos&& lpos, TPos&& tpos, RPos&& rpos) + { + return rpos.first_sub_leaf(concat_right_visitor<Node>{}, lpos, tpos); + } +}; + +template <typename Node> +struct concat_trees_right_visitor + : visitor_base<concat_trees_right_visitor<Node>> +{ + using this_t = concat_trees_right_visitor; + + template <typename RPos, typename LPos, typename TPos> + static concat_center_pos<Node> + visit_node(RPos&& rpos, LPos&& lpos, TPos&& tpos) + { + return concat_inners<Node>(lpos, tpos, rpos); + } +}; + +template <typename Node> +struct concat_trees_left_visitor : visitor_base<concat_trees_left_visitor<Node>> +{ + using this_t = concat_trees_left_visitor; + + template <typename LPos, typename TPos, typename... Args> + static concat_center_pos<Node> + visit_node(LPos&& lpos, TPos&& tpos, Args&&... args) + { + return visit_maybe_relaxed_sub( + args..., concat_trees_right_visitor<Node>{}, lpos, tpos); + } +}; + +template <typename Node> +relaxed_pos<Node> concat_trees(Node* lroot, + shift_t lshift, + size_t lsize, + Node* ltail, + count_t ltcount, + Node* rroot, + shift_t rshift, + size_t rsize) +{ + return visit_maybe_relaxed_sub(lroot, + lshift, + lsize, + concat_trees_left_visitor<Node>{}, + make_leaf_pos(ltail, ltcount), + rroot, + rshift, + rsize) + .realize(); +} + +template <typename Node> +relaxed_pos<Node> concat_trees( + Node* ltail, count_t ltcount, Node* rroot, shift_t rshift, size_t rsize) +{ + return make_singleton_regular_sub_pos(ltail, ltcount) + .visit(concat_trees_left_visitor<Node>{}, + empty_leaf_pos<Node>{}, + rroot, + rshift, + rsize) + .realize(); +} + +template <typename Node> +using concat_center_mut_pos = concat_center_pos<Node>; + +template <typename Node> +struct concat_merger_mut +{ + using node_t = Node; + using edit_t = typename Node::edit_t; + + static constexpr auto B = Node::bits; + static constexpr auto BL = Node::bits_leaf; + + using result_t = concat_center_pos<Node>; + + edit_t ec_ = {}; + + count_t* curr_; + count_t n_; + result_t result_; + count_t count_ = 0; + node_t* candidate_ = nullptr; + edit_t candidate_e_ = Node::memory::transience_t::noone; + + concat_merger_mut(edit_t ec, + shift_t shift, + count_t* counts, + count_t n, + edit_t candidate_e, + node_t* candidate) + : ec_{ec} + , curr_{counts} + , n_{n} + , result_{shift + B, nullptr, 0} + { + if (candidate) { + candidate->ensure_mutable_relaxed_e(candidate_e, ec); + result_.nodes_[0] = candidate; + } else { + result_.nodes_[0] = node_t::make_inner_r_e(ec); + } + } + + node_t* to_ = {}; + count_t to_offset_ = {}; + size_t to_size_ = {}; + + void set_candidate(edit_t candidate_e, node_t* candidate) + { + candidate_ = candidate; + candidate_e_ = candidate_e; + } + + void add_child(node_t* p, size_t size) + { + ++curr_; + auto parent = result_.nodes_[result_.count_ - 1]; + auto relaxed = parent->relaxed(); + if (count_ == branches<B>) { + parent->relaxed()->d.count = count_; + assert(result_.count_ < result_t::max_children); + n_ -= branches<B>; + if (candidate_) { + parent = candidate_; + parent->ensure_mutable_relaxed_e(candidate_e_, ec_); + candidate_ = nullptr; + } else + parent = node_t::make_inner_r_e(ec_); + count_ = 0; + relaxed = parent->relaxed(); + result_.nodes_[result_.count_] = parent; + result_.sizes_[result_.count_] = result_.sizes_[result_.count_ - 1]; + ++result_.count_; + } + auto idx = count_++; + result_.sizes_[result_.count_ - 1] += size; + relaxed->d.sizes[idx] = size + (idx ? relaxed->d.sizes[idx - 1] : 0); + parent->inner()[idx] = p; + }; + + template <typename Pos> + void merge_leaf(Pos&& p, edit_t e) + { + auto from = p.node(); + auto from_size = p.size(); + auto from_count = p.count(); + assert(from_size); + if (!to_ && *curr_ == from_count) { + add_child(from, from_size); + } else { + auto from_offset = count_t{}; + auto from_data = from->leaf(); + auto from_mutate = from->can_mutate(e); + do { + if (!to_) { + if (from_mutate) { + node_t::ownee(from) = ec_; + to_ = from->inc(); + assert(from_count); + } else { + to_ = node_t::make_leaf_e(ec_); + } + to_offset_ = 0; + } + auto data = to_->leaf(); + auto to_copy = + std::min(from_count - from_offset, *curr_ - to_offset_); + if (from == to_) { + if (from_offset != to_offset_) + std::move(from_data + from_offset, + from_data + from_offset + to_copy, + data + to_offset_); + } else { + if (!from_mutate) + std::uninitialized_copy(from_data + from_offset, + from_data + from_offset + + to_copy, + data + to_offset_); + else + detail::uninitialized_move(from_data + from_offset, + from_data + from_offset + + to_copy, + data + to_offset_); + } + to_offset_ += to_copy; + from_offset += to_copy; + if (*curr_ == to_offset_) { + add_child(to_, to_offset_); + to_ = nullptr; + } + } while (from_offset != from_count); + } + } + + template <typename Pos> + void merge_inner(Pos&& p, edit_t e) + { + auto from = p.node(); + auto from_size = p.size(); + auto from_count = p.count(); + assert(from_size); + if (!to_ && *curr_ == from_count) { + add_child(from, from_size); + } else { + auto from_offset = count_t{}; + auto from_data = from->inner(); + auto from_mutate = from->can_relax() && from->can_mutate(e); + do { + if (!to_) { + if (from_mutate) { + node_t::ownee(from) = ec_; + from->ensure_mutable_relaxed_e(e, ec_); + to_ = from; + } else { + to_ = node_t::make_inner_r_e(ec_); + } + to_offset_ = 0; + to_size_ = 0; + } + auto data = to_->inner(); + auto to_copy = + std::min(from_count - from_offset, *curr_ - to_offset_); + auto sizes = to_->relaxed()->d.sizes; + if (from != to_ || from_offset != to_offset_) { + std::copy(from_data + from_offset, + from_data + from_offset + to_copy, + data + to_offset_); + p.copy_sizes( + from_offset, to_copy, to_size_, sizes + to_offset_); + } + to_offset_ += to_copy; + from_offset += to_copy; + to_size_ = sizes[to_offset_ - 1]; + if (*curr_ == to_offset_) { + to_->relaxed()->d.count = to_offset_; + add_child(to_, to_size_); + to_ = nullptr; + } + } while (from_offset != from_count); + } + } + + concat_center_pos<Node> finish() const + { + assert(!to_); + result_.nodes_[result_.count_ - 1]->relaxed()->d.count = count_; + return result_; + } + + void abort() + { + // We may have mutated stuff the tree in place, leaving + // everything in a corrupted state... It should be possible + // to define cleanup properly, but that is a task for some + // other day... ;) + std::terminate(); + } +}; + +struct concat_merger_mut_visitor : visitor_base<concat_merger_mut_visitor> +{ + using this_t = concat_merger_mut_visitor; + + template <typename Pos, typename Merger> + static void visit_inner(Pos&& p, Merger& merger, edit_type<Pos> e) + { + merger.merge_inner(p, e); + } + + template <typename Pos, typename Merger> + static void visit_leaf(Pos&& p, Merger& merger, edit_type<Pos> e) + { + merger.merge_leaf(p, e); + } +}; + +template <bits_t B, bits_t BL> +struct concat_rebalance_plan_mut : concat_rebalance_plan<B, BL> +{ + using this_t = concat_rebalance_plan_mut; + + template <typename LPos, typename CPos, typename RPos> + concat_center_mut_pos<node_type<CPos>> merge(edit_type<CPos> ec, + edit_type<CPos> el, + LPos&& lpos, + CPos&& cpos, + edit_type<CPos> er, + RPos&& rpos) + { + using node_t = node_type<CPos>; + using merger_t = concat_merger_mut<node_t>; + using visitor_t = concat_merger_mut_visitor; + auto lnode = ((node_t*) lpos.node()); + auto rnode = ((node_t*) rpos.node()); + auto lmut2 = lnode && lnode->can_relax() && lnode->can_mutate(el); + auto rmut2 = rnode && rnode->can_relax() && rnode->can_mutate(er); + auto merger = merger_t{ec, + cpos.shift(), + this->counts, + this->n, + el, + lmut2 ? lnode : nullptr}; + try { + lpos.each_left_sub(visitor_t{}, merger, el); + cpos.each_sub(visitor_t{}, merger, ec); + if (rmut2) + merger.set_candidate(er, rnode); + rpos.each_right_sub(visitor_t{}, merger, er); + return merger.finish(); + } catch (...) { + merger.abort(); + throw; + } + } +}; + +template <typename Node, typename LPos, typename CPos, typename RPos> +concat_center_pos<Node> concat_rebalance_mut(edit_type<Node> ec, + edit_type<Node> el, + LPos&& lpos, + CPos&& cpos, + edit_type<Node> er, + RPos&& rpos) +{ + auto plan = concat_rebalance_plan_mut<Node::bits, Node::bits_leaf>{}; + plan.fill(lpos, cpos, rpos); + plan.shuffle(cpos.shift()); + return plan.merge(ec, el, lpos, cpos, er, rpos); +} + +template <typename Node, typename LPos, typename TPos, typename RPos> +concat_center_mut_pos<Node> concat_leafs_mut(edit_type<Node> ec, + edit_type<Node> el, + LPos&& lpos, + TPos&& tpos, + edit_type<Node> er, + RPos&& rpos) +{ + static_assert(Node::bits >= 2, ""); + assert(lpos.shift() == tpos.shift()); + assert(lpos.shift() == rpos.shift()); + assert(lpos.shift() == 0); + if (tpos.count() > 0) + return { + Node::bits_leaf, + lpos.node(), + lpos.count(), + tpos.node(), + tpos.count(), + rpos.node(), + rpos.count(), + }; + else + return { + Node::bits_leaf, + lpos.node(), + lpos.count(), + rpos.node(), + rpos.count(), + }; +} + +template <typename Node> +struct concat_left_mut_visitor; +template <typename Node> +struct concat_right_mut_visitor; +template <typename Node> +struct concat_both_mut_visitor; + +template <typename Node, typename LPos, typename TPos, typename RPos> +concat_center_mut_pos<Node> concat_inners_mut(edit_type<Node> ec, + edit_type<Node> el, + LPos&& lpos, + TPos&& tpos, + edit_type<Node> er, + RPos&& rpos) +{ + auto lshift = lpos.shift(); + auto rshift = rpos.shift(); + // lpos.node() can be null it is a singleton_regular_sub_pos<...>, + // this is, when the tree is just a tail... + if (lshift > rshift) { + auto cpos = lpos.last_sub( + concat_left_mut_visitor<Node>{}, ec, el, tpos, er, rpos); + return concat_rebalance_mut<Node>( + ec, el, lpos, cpos, er, null_sub_pos{}); + } else if (lshift < rshift) { + auto cpos = rpos.first_sub( + concat_right_mut_visitor<Node>{}, ec, el, lpos, tpos, er); + return concat_rebalance_mut<Node>( + ec, el, null_sub_pos{}, cpos, er, rpos); + } else { + assert(lshift == rshift); + assert(Node::bits_leaf == 0u || lshift > 0); + auto cpos = lpos.last_sub( + concat_both_mut_visitor<Node>{}, ec, el, tpos, er, rpos); + return concat_rebalance_mut<Node>(ec, el, lpos, cpos, er, rpos); + } +} + +template <typename Node> +struct concat_left_mut_visitor : visitor_base<concat_left_mut_visitor<Node>> +{ + using this_t = concat_left_mut_visitor; + using edit_t = typename Node::edit_t; + + template <typename LPos, typename TPos, typename RPos> + static concat_center_mut_pos<Node> visit_inner( + LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos) + { + return concat_inners_mut<Node>(ec, el, lpos, tpos, er, rpos); + } + + template <typename LPos, typename TPos, typename RPos> + static concat_center_mut_pos<Node> visit_leaf( + LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos) + { + IMMER_UNREACHABLE; + } +}; + +template <typename Node> +struct concat_right_mut_visitor : visitor_base<concat_right_mut_visitor<Node>> +{ + using this_t = concat_right_mut_visitor; + using edit_t = typename Node::edit_t; + + template <typename RPos, typename LPos, typename TPos> + static concat_center_mut_pos<Node> visit_inner( + RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er) + { + return concat_inners_mut<Node>(ec, el, lpos, tpos, er, rpos); + } + + template <typename RPos, typename LPos, typename TPos> + static concat_center_mut_pos<Node> visit_leaf( + RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er) + { + return concat_leafs_mut<Node>(ec, el, lpos, tpos, er, rpos); + } +}; + +template <typename Node> +struct concat_both_mut_visitor : visitor_base<concat_both_mut_visitor<Node>> +{ + using this_t = concat_both_mut_visitor; + using edit_t = typename Node::edit_t; + + template <typename LPos, typename TPos, typename RPos> + static concat_center_mut_pos<Node> visit_inner( + LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos) + { + return rpos.first_sub( + concat_right_mut_visitor<Node>{}, ec, el, lpos, tpos, er); + } + + template <typename LPos, typename TPos, typename RPos> + static concat_center_mut_pos<Node> visit_leaf( + LPos&& lpos, edit_t ec, edit_t el, TPos&& tpos, edit_t er, RPos&& rpos) + { + return rpos.first_sub_leaf( + concat_right_mut_visitor<Node>{}, ec, el, lpos, tpos, er); + } +}; + +template <typename Node> +struct concat_trees_right_mut_visitor + : visitor_base<concat_trees_right_mut_visitor<Node>> +{ + using this_t = concat_trees_right_mut_visitor; + using edit_t = typename Node::edit_t; + + template <typename RPos, typename LPos, typename TPos> + static concat_center_mut_pos<Node> visit_node( + RPos&& rpos, edit_t ec, edit_t el, LPos&& lpos, TPos&& tpos, edit_t er) + { + return concat_inners_mut<Node>(ec, el, lpos, tpos, er, rpos); + } +}; + +template <typename Node> +struct concat_trees_left_mut_visitor + : visitor_base<concat_trees_left_mut_visitor<Node>> +{ + using this_t = concat_trees_left_mut_visitor; + using edit_t = typename Node::edit_t; + + template <typename LPos, typename TPos, typename... Args> + static concat_center_mut_pos<Node> visit_node(LPos&& lpos, + edit_t ec, + edit_t el, + TPos&& tpos, + edit_t er, + Args&&... args) + { + return visit_maybe_relaxed_sub(args..., + concat_trees_right_mut_visitor<Node>{}, + ec, + el, + lpos, + tpos, + er); + } +}; + +template <typename Node> +relaxed_pos<Node> concat_trees_mut(edit_type<Node> ec, + edit_type<Node> el, + Node* lroot, + shift_t lshift, + size_t lsize, + Node* ltail, + count_t ltcount, + edit_type<Node> er, + Node* rroot, + shift_t rshift, + size_t rsize) +{ + return visit_maybe_relaxed_sub(lroot, + lshift, + lsize, + concat_trees_left_mut_visitor<Node>{}, + ec, + el, + make_leaf_pos(ltail, ltcount), + er, + rroot, + rshift, + rsize) + .realize_e(ec); +} + +template <typename Node> +relaxed_pos<Node> concat_trees_mut(edit_type<Node> ec, + edit_type<Node> el, + Node* ltail, + count_t ltcount, + edit_type<Node> er, + Node* rroot, + shift_t rshift, + size_t rsize) +{ + return make_singleton_regular_sub_pos(ltail, ltcount) + .visit(concat_trees_left_mut_visitor<Node>{}, + ec, + el, + empty_leaf_pos<Node>{}, + er, + rroot, + rshift, + rsize) + .realize_e(ec); +} + +} // namespace rbts +} // namespace detail +} // namespace immer |