about summary refs log tree commit diff
path: root/third_party/immer/immer/detail/rbts/rrbtree.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/immer/immer/detail/rbts/rrbtree.hpp')
-rw-r--r--third_party/immer/immer/detail/rbts/rrbtree.hpp1396
1 files changed, 0 insertions, 1396 deletions
diff --git a/third_party/immer/immer/detail/rbts/rrbtree.hpp b/third_party/immer/immer/detail/rbts/rrbtree.hpp
deleted file mode 100644
index 7bf59e6e92b1..000000000000
--- a/third_party/immer/immer/detail/rbts/rrbtree.hpp
+++ /dev/null
@@ -1,1396 +0,0 @@
-//
-// 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 <immer/config.hpp>
-#include <immer/detail/rbts/node.hpp>
-#include <immer/detail/rbts/operations.hpp>
-#include <immer/detail/rbts/position.hpp>
-
-#include <immer/detail/type_traits.hpp>
-
-#include <cassert>
-#include <memory>
-#include <numeric>
-
-namespace immer {
-namespace detail {
-namespace rbts {
-
-template <typename T, typename MemoryPolicy, bits_t B, bits_t BL>
-struct rrbtree_iterator;
-
-template <typename T, typename MemoryPolicy, bits_t B, bits_t BL>
-struct rrbtree
-{
-    using node_t  = node<T, MemoryPolicy, B, BL>;
-    using edit_t  = typename node_t::edit_t;
-    using owner_t = typename MemoryPolicy::transience_t::owner;
-
-    size_t size;
-    shift_t shift;
-    node_t* root;
-    node_t* tail;
-
-    static const rrbtree& empty()
-    {
-        static const rrbtree empty_{
-            0, BL, node_t::make_inner_n(0u), node_t::make_leaf_n(0u)};
-        return empty_;
-    }
-
-    template <typename U>
-    static auto from_initializer_list(std::initializer_list<U> values)
-    {
-        auto e      = owner_t{};
-        auto result = rrbtree{empty()};
-        for (auto&& v : values)
-            result.push_back_mut(e, v);
-        return result;
-    }
-
-    template <typename Iter,
-              typename Sent,
-              std::enable_if_t<compatible_sentinel_v<Iter, Sent>, bool> = true>
-    static auto from_range(Iter first, Sent last)
-    {
-        auto e      = owner_t{};
-        auto result = rrbtree{empty()};
-        for (; first != last; ++first)
-            result.push_back_mut(e, *first);
-        return result;
-    }
-
-    static auto from_fill(size_t n, T v)
-    {
-        auto e      = owner_t{};
-        auto result = rrbtree{empty()};
-        while (n-- > 0)
-            result.push_back_mut(e, v);
-        return result;
-    }
-
-    rrbtree(size_t sz, shift_t sh, node_t* r, node_t* t)
-        : size{sz}
-        , shift{sh}
-        , root{r}
-        , tail{t}
-    {
-        assert(check_tree());
-    }
-
-    rrbtree(const rrbtree& other)
-        : rrbtree{other.size, other.shift, other.root, other.tail}
-    {
-        inc();
-    }
-
-    rrbtree(rrbtree&& other)
-        : rrbtree{empty()}
-    {
-        swap(*this, other);
-    }
-
-    rrbtree& operator=(const rrbtree& other)
-    {
-        auto next{other};
-        swap(*this, next);
-        return *this;
-    }
-
-    rrbtree& operator=(rrbtree&& other)
-    {
-        swap(*this, other);
-        return *this;
-    }
-
-    friend void swap(rrbtree& x, rrbtree& y)
-    {
-        using std::swap;
-        swap(x.size, y.size);
-        swap(x.shift, y.shift);
-        swap(x.root, y.root);
-        swap(x.tail, y.tail);
-    }
-
-    ~rrbtree() { dec(); }
-
-    void inc() const
-    {
-        root->inc();
-        tail->inc();
-    }
-
-    void dec() const { traverse(dec_visitor()); }
-
-    auto tail_size() const { return size - tail_offset(); }
-
-    auto tail_offset() const
-    {
-        auto r = root->relaxed();
-        assert(r == nullptr || r->d.count);
-        return r ? r->d.sizes[r->d.count - 1]
-                 : size ? (size - 1) & ~mask<BL>
-                        /* otherwise */ : 0;
-    }
-
-    template <typename Visitor, typename... Args>
-    void traverse(Visitor v, Args&&... args) const
-    {
-        auto tail_off  = tail_offset();
-        auto tail_size = size - tail_off;
-
-        if (tail_off)
-            visit_maybe_relaxed_sub(root, shift, tail_off, v, args...);
-        else
-            make_empty_regular_pos(root).visit(v, args...);
-
-        if (tail_size)
-            make_leaf_sub_pos(tail, tail_size).visit(v, args...);
-        else
-            make_empty_leaf_pos(tail).visit(v, args...);
-    }
-
-    template <typename Visitor, typename... Args>
-    void traverse(Visitor v, size_t first, size_t last, Args&&... args) const
-    {
-        auto tail_off  = tail_offset();
-        auto tail_size = size - tail_off;
-
-        if (first < tail_off)
-            visit_maybe_relaxed_sub(root,
-                                    shift,
-                                    tail_off,
-                                    v,
-                                    first,
-                                    last < tail_off ? last : tail_off,
-                                    args...);
-        if (last > tail_off)
-            make_leaf_sub_pos(tail, tail_size)
-                .visit(v,
-                       first > tail_off ? first - tail_off : 0,
-                       last - tail_off,
-                       args...);
-    }
-
-    template <typename Visitor, typename... Args>
-    bool traverse_p(Visitor v, Args&&... args) const
-    {
-        auto tail_off  = tail_offset();
-        auto tail_size = size - tail_off;
-        return (tail_off
-                    ? visit_maybe_relaxed_sub(root, shift, tail_off, v, args...)
-                    : make_empty_regular_pos(root).visit(v, args...)) &&
-               (tail_size ? make_leaf_sub_pos(tail, tail_size).visit(v, args...)
-                          : make_empty_leaf_pos(tail).visit(v, args...));
-    }
-
-    template <typename Visitor, typename... Args>
-    bool traverse_p(Visitor v, size_t first, size_t last, Args&&... args) const
-    {
-        auto tail_off  = tail_offset();
-        auto tail_size = size - tail_off;
-        return (first < tail_off
-                    ? visit_maybe_relaxed_sub(root,
-                                              shift,
-                                              tail_off,
-                                              v,
-                                              first,
-                                              last < tail_off ? last : tail_off,
-                                              args...)
-                    : true) &&
-               (last > tail_off
-                    ? make_leaf_sub_pos(tail, tail_size)
-                          .visit(v,
-                                 first > tail_off ? first - tail_off : 0,
-                                 last - tail_off,
-                                 args...)
-                    : true);
-    }
-
-    template <typename Visitor>
-    decltype(auto) descend(Visitor v, size_t idx) const
-    {
-        auto tail_off = tail_offset();
-        return idx >= tail_off
-                   ? make_leaf_descent_pos(tail).visit(v, idx - tail_off)
-                   : visit_maybe_relaxed_descent(root, shift, v, idx);
-    }
-
-    template <typename Fn>
-    void for_each_chunk(Fn&& fn) const
-    {
-        traverse(for_each_chunk_visitor{}, std::forward<Fn>(fn));
-    }
-
-    template <typename Fn>
-    void for_each_chunk(size_t first, size_t last, Fn&& fn) const
-    {
-        traverse(for_each_chunk_i_visitor{}, first, last, std::forward<Fn>(fn));
-    }
-
-    template <typename Fn>
-    bool for_each_chunk_p(Fn&& fn) const
-    {
-        return traverse_p(for_each_chunk_p_visitor{}, std::forward<Fn>(fn));
-    }
-
-    template <typename Fn>
-    bool for_each_chunk_p(size_t first, size_t last, Fn&& fn) const
-    {
-        return traverse_p(
-            for_each_chunk_p_i_visitor{}, first, last, std::forward<Fn>(fn));
-    }
-
-    bool equals(const rrbtree& other) const
-    {
-        using iter_t = rrbtree_iterator<T, MemoryPolicy, B, BL>;
-        if (size != other.size)
-            return false;
-        if (size == 0)
-            return true;
-        auto tail_off       = tail_offset();
-        auto tail_off_other = other.tail_offset();
-        // compare trees
-        if (tail_off > 0 && tail_off_other > 0) {
-            // other.shift != shift is a theoretical possibility for
-            // relaxed trees that sadly we haven't managed to exercise
-            // in tests yet...
-            if (other.shift >= shift) {
-                if (!visit_maybe_relaxed_sub(other.root,
-                                             other.shift,
-                                             tail_off_other,
-                                             equals_visitor::rrb{},
-                                             iter_t{other},
-                                             root,
-                                             shift,
-                                             tail_off))
-                    return false;
-            } else {
-                if (!visit_maybe_relaxed_sub(root,
-                                             shift,
-                                             tail_off,
-                                             equals_visitor::rrb{},
-                                             iter_t{*this},
-                                             other.root,
-                                             other.shift,
-                                             tail_off_other))
-                    return false;
-            }
-        }
-        return tail_off == tail_off_other
-                   ? make_leaf_sub_pos(tail, tail_size())
-                         .visit(equals_visitor{}, other.tail)
-                   : tail_off > tail_off_other
-                         ? std::equal(tail->leaf(),
-                                      tail->leaf() + (size - tail_off),
-                                      other.tail->leaf() +
-                                          (tail_off - tail_off_other))
-                         /* otherwise */
-                         : std::equal(tail->leaf(),
-                                      tail->leaf() + (size - tail_off),
-                                      iter_t{other} + tail_off);
-    }
-
-    std::tuple<shift_t, node_t*> push_tail(node_t* root,
-                                           shift_t shift,
-                                           size_t size,
-                                           node_t* tail,
-                                           count_t tail_size) const
-    {
-        if (auto r = root->relaxed()) {
-            auto new_root =
-                make_relaxed_pos(root, shift, r)
-                    .visit(push_tail_visitor<node_t>{}, tail, tail_size);
-            if (new_root)
-                return std::make_tuple(shift, new_root);
-            else {
-                auto new_root = node_t::make_inner_r_n(2);
-                try {
-                    auto new_path        = node_t::make_path(shift, tail);
-                    new_root->inner()[0] = root->inc();
-                    new_root->inner()[1] = new_path;
-                    new_root->relaxed()->d.sizes[0] = size;
-                    new_root->relaxed()->d.sizes[1] = size + tail_size;
-                    new_root->relaxed()->d.count    = 2u;
-                } catch (...) {
-                    node_t::delete_inner_r(new_root, 2);
-                    throw;
-                }
-                return std::make_tuple(shift + B, new_root);
-            }
-        } else if (size == size_t{branches<B>} << shift) {
-            auto new_root = node_t::make_inner_n(2);
-            try {
-                auto new_path        = node_t::make_path(shift, tail);
-                new_root->inner()[0] = root->inc();
-                new_root->inner()[1] = new_path;
-            } catch (...) {
-                node_t::delete_inner(new_root, 2);
-                throw;
-            }
-            return std::make_tuple(shift + B, new_root);
-        } else if (size) {
-            auto new_root = make_regular_sub_pos(root, shift, size)
-                                .visit(push_tail_visitor<node_t>{}, tail);
-            return std::make_tuple(shift, new_root);
-        } else {
-            return std::make_tuple(shift, node_t::make_path(shift, tail));
-        }
-    }
-
-    void
-    push_tail_mut(edit_t e, size_t tail_off, node_t* tail, count_t tail_size)
-    {
-        if (auto r = root->relaxed()) {
-            auto new_root =
-                make_relaxed_pos(root, shift, r)
-                    .visit(push_tail_mut_visitor<node_t>{}, e, tail, tail_size);
-            if (new_root) {
-                root = new_root;
-            } else {
-                auto new_root = node_t::make_inner_r_e(e);
-                try {
-                    auto new_path        = node_t::make_path_e(e, shift, tail);
-                    new_root->inner()[0] = root;
-                    new_root->inner()[1] = new_path;
-                    new_root->relaxed()->d.sizes[0] = tail_off;
-                    new_root->relaxed()->d.sizes[1] = tail_off + tail_size;
-                    new_root->relaxed()->d.count    = 2u;
-                    root                            = new_root;
-                    shift += B;
-                } catch (...) {
-                    node_t::delete_inner_r_e(new_root);
-                    throw;
-                }
-            }
-        } else if (tail_off == size_t{branches<B>} << shift) {
-            auto new_root = node_t::make_inner_e(e);
-            try {
-                auto new_path        = node_t::make_path_e(e, shift, tail);
-                new_root->inner()[0] = root;
-                new_root->inner()[1] = new_path;
-                root                 = new_root;
-                shift += B;
-            } catch (...) {
-                node_t::delete_inner_e(new_root);
-                throw;
-            }
-        } else if (tail_off) {
-            auto new_root =
-                make_regular_sub_pos(root, shift, tail_off)
-                    .visit(push_tail_mut_visitor<node_t>{}, e, tail);
-            root = new_root;
-        } else {
-            auto new_root = node_t::make_path_e(e, shift, tail);
-            dec_empty_regular(root);
-            root = new_root;
-        }
-    }
-
-    void ensure_mutable_tail(edit_t e, count_t n)
-    {
-        if (!tail->can_mutate(e)) {
-            auto new_tail = node_t::copy_leaf_e(e, tail, n);
-            dec_leaf(tail, n);
-            tail = new_tail;
-        }
-    }
-
-    void push_back_mut(edit_t e, T value)
-    {
-        auto ts = tail_size();
-        if (ts < branches<BL>) {
-            ensure_mutable_tail(e, ts);
-            new (&tail->leaf()[ts]) T{std::move(value)};
-        } else {
-            using std::get;
-            auto new_tail = node_t::make_leaf_e(e, std::move(value));
-            auto tail_off = tail_offset();
-            try {
-                push_tail_mut(e, tail_off, tail, ts);
-                tail = new_tail;
-            } catch (...) {
-                node_t::delete_leaf(new_tail, 1u);
-                throw;
-            }
-        }
-        ++size;
-    }
-
-    rrbtree push_back(T value) const
-    {
-        auto ts = tail_size();
-        if (ts < branches<BL>) {
-            auto new_tail =
-                node_t::copy_leaf_emplace(tail, ts, std::move(value));
-            return {size + 1, shift, root->inc(), new_tail};
-        } else {
-            using std::get;
-            auto new_tail = node_t::make_leaf_n(1u, std::move(value));
-            auto tail_off = tail_offset();
-            try {
-                auto new_root =
-                    push_tail(root, shift, tail_off, tail, size - tail_off);
-                tail->inc();
-                return {size + 1, get<0>(new_root), get<1>(new_root), new_tail};
-            } catch (...) {
-                node_t::delete_leaf(new_tail, 1u);
-                throw;
-            }
-        }
-    }
-
-    std::tuple<const T*, size_t, size_t> region_for(size_t idx) const
-    {
-        using std::get;
-        auto tail_off = tail_offset();
-        if (idx >= tail_off) {
-            return std::make_tuple(tail->leaf(), tail_off, size);
-        } else {
-            auto subs = visit_maybe_relaxed_sub(
-                root, shift, tail_off, region_for_visitor<T>(), idx);
-            auto first = idx - get<1>(subs);
-            auto end   = first + get<2>(subs);
-            return std::make_tuple(get<0>(subs), first, end);
-        }
-    }
-
-    T& get_mut(edit_t e, size_t idx)
-    {
-        auto tail_off = tail_offset();
-        if (idx >= tail_off) {
-            ensure_mutable_tail(e, size - tail_off);
-            return tail->leaf()[(idx - tail_off) & mask<BL>];
-        } else {
-            return visit_maybe_relaxed_sub(root,
-                                           shift,
-                                           tail_off,
-                                           get_mut_visitor<node_t>{},
-                                           idx,
-                                           e,
-                                           &root);
-        }
-    }
-
-    const T& get(size_t index) const
-    {
-        return descend(get_visitor<T>(), index);
-    }
-
-    const T& get_check(size_t index) const
-    {
-        if (index >= size)
-            throw std::out_of_range{"out of range"};
-        return descend(get_visitor<T>(), index);
-    }
-
-    const T& front() const { return get(0); }
-
-    const T& back() const { return get(size - 1); }
-
-    template <typename FnT>
-    void update_mut(edit_t e, size_t idx, FnT&& fn)
-    {
-        auto& elem = get_mut(e, idx);
-        elem       = std::forward<FnT>(fn)(std::move(elem));
-    }
-
-    template <typename FnT>
-    rrbtree update(size_t idx, FnT&& fn) const
-    {
-        auto tail_off = tail_offset();
-        if (idx >= tail_off) {
-            auto tail_size = size - tail_off;
-            auto new_tail =
-                make_leaf_sub_pos(tail, tail_size)
-                    .visit(update_visitor<node_t>{}, idx - tail_off, fn);
-            return {size, shift, root->inc(), new_tail};
-        } else {
-            auto new_root = visit_maybe_relaxed_sub(
-                root, shift, tail_off, update_visitor<node_t>{}, idx, fn);
-            return {size, shift, new_root, tail->inc()};
-        }
-    }
-
-    void assoc_mut(edit_t e, size_t idx, T value)
-    {
-        update_mut(e, idx, [&](auto&&) { return std::move(value); });
-    }
-
-    rrbtree assoc(size_t idx, T value) const
-    {
-        return update(idx, [&](auto&&) { return std::move(value); });
-    }
-
-    void take_mut(edit_t e, size_t new_size)
-    {
-        auto tail_off = tail_offset();
-        if (new_size == 0) {
-            *this = empty();
-        } else if (new_size >= size) {
-            return;
-        } else if (new_size > tail_off) {
-            auto ts    = size - tail_off;
-            auto newts = new_size - tail_off;
-            if (tail->can_mutate(e)) {
-                destroy_n(tail->leaf() + newts, ts - newts);
-            } else {
-                auto new_tail = node_t::copy_leaf_e(e, tail, newts);
-                dec_leaf(tail, ts);
-                tail = new_tail;
-            }
-            size = new_size;
-            return;
-        } else {
-            using std::get;
-            auto l = new_size - 1;
-            auto v = slice_right_mut_visitor<node_t>();
-            auto r = visit_maybe_relaxed_sub(root, shift, tail_off, v, l, e);
-            auto new_shift = get<0>(r);
-            auto new_root  = get<1>(r);
-            auto new_tail  = get<3>(r);
-            if (new_root) {
-                root  = new_root;
-                shift = new_shift;
-            } else {
-                root  = empty().root->inc();
-                shift = BL;
-            }
-            dec_leaf(tail, size - tail_off);
-            size = new_size;
-            tail = new_tail;
-            return;
-        }
-    }
-
-    rrbtree take(size_t new_size) const
-    {
-        auto tail_off = tail_offset();
-        if (new_size == 0) {
-            return empty();
-        } else if (new_size >= size) {
-            return *this;
-        } else if (new_size > tail_off) {
-            auto new_tail = node_t::copy_leaf(tail, new_size - tail_off);
-            return {new_size, shift, root->inc(), new_tail};
-        } else {
-            using std::get;
-            auto l = new_size - 1;
-            auto v = slice_right_visitor<node_t>();
-            auto r = visit_maybe_relaxed_sub(root, shift, tail_off, v, l);
-            auto new_shift = get<0>(r);
-            auto new_root  = get<1>(r);
-            auto new_tail  = get<3>(r);
-            if (new_root) {
-                IMMER_ASSERT_TAGGED(new_root->compute_shift() == get<0>(r));
-                assert(new_root->check(new_shift, new_size - get<2>(r)));
-                return {new_size, new_shift, new_root, new_tail};
-            } else {
-                return {new_size, BL, empty().root->inc(), new_tail};
-            }
-        }
-    }
-
-    void drop_mut(edit_t e, size_t elems)
-    {
-        using std::get;
-        auto tail_off = tail_offset();
-        if (elems == 0) {
-            return;
-        } else if (elems >= size) {
-            *this = empty();
-        } else if (elems == tail_off) {
-            dec_inner(root, shift, tail_off);
-            shift = BL;
-            root  = empty().root->inc();
-            size -= elems;
-            return;
-        } else if (elems > tail_off) {
-            auto v = slice_left_mut_visitor<node_t>();
-            tail   = get<1>(make_leaf_sub_pos(tail, size - tail_off)
-                              .visit(v, elems - tail_off, e));
-            if (root != empty().root) {
-                dec_inner(root, shift, tail_off);
-                shift = BL;
-                root  = empty().root->inc();
-            }
-            size -= elems;
-            return;
-        } else {
-            auto v = slice_left_mut_visitor<node_t>();
-            auto r =
-                visit_maybe_relaxed_sub(root, shift, tail_off, v, elems, e);
-            shift = get<0>(r);
-            root  = get<1>(r);
-            size -= elems;
-            return;
-        }
-    }
-
-    rrbtree drop(size_t elems) const
-    {
-        if (elems == 0) {
-            return *this;
-        } else if (elems >= size) {
-            return empty();
-        } else if (elems == tail_offset()) {
-            return {size - elems, BL, empty().root->inc(), tail->inc()};
-        } else if (elems > tail_offset()) {
-            auto tail_off = tail_offset();
-            auto new_tail =
-                node_t::copy_leaf(tail, elems - tail_off, size - tail_off);
-            return {size - elems, BL, empty().root->inc(), new_tail};
-        } else {
-            using std::get;
-            auto v = slice_left_visitor<node_t>();
-            auto r =
-                visit_maybe_relaxed_sub(root, shift, tail_offset(), v, elems);
-            auto new_root  = get<1>(r);
-            auto new_shift = get<0>(r);
-            return {size - elems, new_shift, new_root, tail->inc()};
-        }
-        return *this;
-    }
-
-    rrbtree concat(const rrbtree& r) const
-    {
-        assert(r.size < (std::numeric_limits<size_t>::max() - size));
-        using std::get;
-        if (size == 0)
-            return r;
-        else if (r.size == 0)
-            return *this;
-        else if (r.tail_offset() == 0) {
-            // just concat the tail, similar to push_back
-            auto tail_offst = tail_offset();
-            auto tail_size  = size - tail_offst;
-            if (tail_size == branches<BL>) {
-                auto new_root =
-                    push_tail(root, shift, tail_offst, tail, tail_size);
-                tail->inc();
-                return {size + r.size,
-                        get<0>(new_root),
-                        get<1>(new_root),
-                        r.tail->inc()};
-            } else if (tail_size + r.size <= branches<BL>) {
-                auto new_tail =
-                    node_t::copy_leaf(tail, tail_size, r.tail, r.size);
-                return {size + r.size, shift, root->inc(), new_tail};
-            } else {
-                auto remaining = branches<BL> - tail_size;
-                auto add_tail =
-                    node_t::copy_leaf(tail, tail_size, r.tail, remaining);
-                try {
-                    auto new_tail =
-                        node_t::copy_leaf(r.tail, remaining, r.size);
-                    try {
-                        auto new_root = push_tail(
-                            root, shift, tail_offst, add_tail, branches<BL>);
-                        return {size + r.size,
-                                get<0>(new_root),
-                                get<1>(new_root),
-                                new_tail};
-                    } catch (...) {
-                        node_t::delete_leaf(new_tail, r.size - remaining);
-                        throw;
-                    }
-                } catch (...) {
-                    node_t::delete_leaf(add_tail, branches<BL>);
-                    throw;
-                }
-            }
-        } else if (tail_offset() == 0) {
-            auto tail_offst = tail_offset();
-            auto tail_size  = size - tail_offst;
-            auto concated =
-                concat_trees(tail, tail_size, r.root, r.shift, r.tail_offset());
-            auto new_shift = concated.shift();
-            auto new_root  = concated.node();
-            IMMER_ASSERT_TAGGED(new_shift == new_root->compute_shift());
-            assert(new_root->check(new_shift, size + r.tail_offset()));
-            return {size + r.size, new_shift, new_root, r.tail->inc()};
-        } else {
-            auto tail_offst = tail_offset();
-            auto tail_size  = size - tail_offst;
-            auto concated   = concat_trees(root,
-                                         shift,
-                                         tail_offst,
-                                         tail,
-                                         tail_size,
-                                         r.root,
-                                         r.shift,
-                                         r.tail_offset());
-            auto new_shift  = concated.shift();
-            auto new_root   = concated.node();
-            IMMER_ASSERT_TAGGED(new_shift == new_root->compute_shift());
-            assert(new_root->check(new_shift, size + r.tail_offset()));
-            return {size + r.size, new_shift, new_root, r.tail->inc()};
-        }
-    }
-
-    constexpr static bool supports_transient_concat =
-        !std::is_empty<edit_t>::value;
-
-    friend void concat_mut_l(rrbtree& l, edit_t el, const rrbtree& r)
-    {
-        assert(&l != &r);
-        assert(r.size < (std::numeric_limits<size_t>::max() - l.size));
-        using std::get;
-        if (l.size == 0)
-            l = r;
-        else if (r.size == 0)
-            return;
-        else if (r.tail_offset() == 0) {
-            // just concat the tail, similar to push_back
-            auto tail_offst = l.tail_offset();
-            auto tail_size  = l.size - tail_offst;
-            if (tail_size == branches<BL>) {
-                l.push_tail_mut(el, tail_offst, l.tail, tail_size);
-                l.tail = r.tail->inc();
-                l.size += r.size;
-                return;
-            } else if (tail_size + r.size <= branches<BL>) {
-                l.ensure_mutable_tail(el, tail_size);
-                std::uninitialized_copy(r.tail->leaf(),
-                                        r.tail->leaf() + r.size,
-                                        l.tail->leaf() + tail_size);
-                l.size += r.size;
-                return;
-            } else {
-                auto remaining = branches<BL> - tail_size;
-                l.ensure_mutable_tail(el, tail_size);
-                std::uninitialized_copy(r.tail->leaf(),
-                                        r.tail->leaf() + remaining,
-                                        l.tail->leaf() + tail_size);
-                try {
-                    auto new_tail =
-                        node_t::copy_leaf_e(el, r.tail, remaining, r.size);
-                    try {
-                        l.push_tail_mut(el, tail_offst, l.tail, branches<BL>);
-                        l.tail = new_tail;
-                        l.size += r.size;
-                        return;
-                    } catch (...) {
-                        node_t::delete_leaf(new_tail, r.size - remaining);
-                        throw;
-                    }
-                } catch (...) {
-                    destroy_n(r.tail->leaf() + tail_size, remaining);
-                    throw;
-                }
-            }
-        } else if (l.tail_offset() == 0) {
-            if (supports_transient_concat) {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated =
-                    concat_trees_mut(el,
-                                     el,
-                                     l.tail,
-                                     tail_size,
-                                     MemoryPolicy::transience_t::noone,
-                                     r.root,
-                                     r.shift,
-                                     r.tail_offset());
-                IMMER_ASSERT_TAGGED(concated.shift() ==
-                                    concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(),
-                                              l.size + r.tail_offset()));
-                l.size += r.size;
-                l.shift = concated.shift();
-                l.root  = concated.node();
-                l.tail  = r.tail;
-            } else {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees(
-                    l.tail, tail_size, r.root, r.shift, r.tail_offset());
-                l = {l.size + r.size,
-                     concated.shift(),
-                     concated.node(),
-                     r.tail->inc()};
-                return;
-            }
-        } else {
-            if (supports_transient_concat) {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated =
-                    concat_trees_mut(el,
-                                     el,
-                                     l.root,
-                                     l.shift,
-                                     tail_offst,
-                                     l.tail,
-                                     tail_size,
-                                     MemoryPolicy::transience_t::noone,
-                                     r.root,
-                                     r.shift,
-                                     r.tail_offset());
-                IMMER_ASSERT_TAGGED(concated.shift() ==
-                                    concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(),
-                                              l.size + r.tail_offset()));
-                l.size += r.size;
-                l.shift = concated.shift();
-                l.root  = concated.node();
-                l.tail  = r.tail;
-            } else {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees(l.root,
-                                             l.shift,
-                                             tail_offst,
-                                             l.tail,
-                                             tail_size,
-                                             r.root,
-                                             r.shift,
-                                             r.tail_offset());
-                l               = {l.size + r.size,
-                     concated.shift(),
-                     concated.node(),
-                     r.tail->inc()};
-            }
-        }
-    }
-
-    friend void concat_mut_r(const rrbtree& l, rrbtree& r, edit_t er)
-    {
-        assert(&l != &r);
-        assert(r.size < (std::numeric_limits<size_t>::max() - l.size));
-        using std::get;
-        if (r.size == 0)
-            r = std::move(l);
-        else if (l.size == 0)
-            return;
-        else if (r.tail_offset() == 0) {
-            // just concat the tail, similar to push_back
-            auto tail_offst = l.tail_offset();
-            auto tail_size  = l.size - tail_offst;
-            if (tail_size == branches<BL>) {
-                // this could be improved by making sure that the
-                // newly created nodes as part of the `push_tail()`
-                // are tagged with `er`
-                auto res =
-                    l.push_tail(l.root, l.shift, tail_offst, l.tail, tail_size);
-                l.tail->inc(); // note: leak if mutably concatenated
-                               // with itself, but this is forbidden
-                               // by the interface
-                r = {l.size + r.size, get<0>(res), get<1>(res), r.tail->inc()};
-                return;
-            } else if (tail_size + r.size <= branches<BL>) {
-                // doing this in a exception way mutating way is very
-                // tricky while potential performance gains are
-                // minimal (we need to move every element of the right
-                // tail anyways to make space for the left tail)
-                //
-                // we could however improve this by at least moving the
-                // elements of the right tail...
-                auto new_tail =
-                    node_t::copy_leaf(l.tail, tail_size, r.tail, r.size);
-                r = {l.size + r.size, l.shift, l.root->inc(), new_tail};
-                return;
-            } else {
-                // like the immutable version
-                auto remaining = branches<BL> - tail_size;
-                auto add_tail  = node_t::copy_leaf_e(
-                    er, l.tail, tail_size, r.tail, remaining);
-                try {
-                    auto new_tail =
-                        node_t::copy_leaf_e(er, r.tail, remaining, r.size);
-                    try {
-                        // this could be improved by making sure that the
-                        // newly created nodes as part of the `push_tail()`
-                        // are tagged with `er`
-                        auto new_root = l.push_tail(l.root,
-                                                    l.shift,
-                                                    tail_offst,
-                                                    add_tail,
-                                                    branches<BL>);
-                        r             = {l.size + r.size,
-                             get<0>(new_root),
-                             get<1>(new_root),
-                             new_tail};
-                        return;
-                    } catch (...) {
-                        node_t::delete_leaf(new_tail, r.size - remaining);
-                        throw;
-                    }
-                } catch (...) {
-                    node_t::delete_leaf(add_tail, branches<BL>);
-                    throw;
-                }
-                return;
-            }
-        } else if (l.tail_offset() == 0) {
-            if (supports_transient_concat) {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated =
-                    concat_trees_mut(er,
-                                     MemoryPolicy::transience_t::noone,
-                                     l.tail,
-                                     tail_size,
-                                     er,
-                                     r.root,
-                                     r.shift,
-                                     r.tail_offset());
-                IMMER_ASSERT_TAGGED(concated.shift() ==
-                                    concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(),
-                                              l.size + r.tail_offset()));
-                r.size += l.size;
-                r.shift = concated.shift();
-                r.root  = concated.node();
-            } else {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees(
-                    l.tail, tail_size, r.root, r.shift, r.tail_offset());
-                r = {l.size + r.size,
-                     concated.shift(),
-                     concated.node(),
-                     r.tail->inc()};
-                return;
-            }
-        } else {
-            if (supports_transient_concat) {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated =
-                    concat_trees_mut(er,
-                                     MemoryPolicy::transience_t::noone,
-                                     l.root,
-                                     l.shift,
-                                     tail_offst,
-                                     l.tail,
-                                     tail_size,
-                                     er,
-                                     r.root,
-                                     r.shift,
-                                     r.tail_offset());
-                IMMER_ASSERT_TAGGED(concated.shift() ==
-                                    concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(),
-                                              l.size + r.tail_offset()));
-                r.size += l.size;
-                r.shift = concated.shift();
-                r.root  = concated.node();
-                return;
-            } else {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees(l.root,
-                                             l.shift,
-                                             tail_offst,
-                                             l.tail,
-                                             tail_size,
-                                             r.root,
-                                             r.shift,
-                                             r.tail_offset());
-                r               = {l.size + r.size,
-                     concated.shift(),
-                     concated.node(),
-                     r.tail->inc()};
-                return;
-            }
-        }
-    }
-
-    friend void concat_mut_lr_l(rrbtree& l, edit_t el, rrbtree& r, edit_t er)
-    {
-        assert(&l != &r);
-        assert(r.size < (std::numeric_limits<size_t>::max() - l.size));
-        using std::get;
-        if (l.size == 0)
-            l = r;
-        else if (r.size == 0)
-            return;
-        else if (r.tail_offset() == 0) {
-            // just concat the tail, similar to push_back
-            auto tail_offst = l.tail_offset();
-            auto tail_size  = l.size - tail_offst;
-            if (tail_size == branches<BL>) {
-                l.push_tail_mut(el, tail_offst, l.tail, tail_size);
-                l.tail = r.tail->inc();
-                l.size += r.size;
-                return;
-            } else if (tail_size + r.size <= branches<BL>) {
-                l.ensure_mutable_tail(el, tail_size);
-                if (r.tail->can_mutate(er))
-                    detail::uninitialized_move(r.tail->leaf(),
-                                               r.tail->leaf() + r.size,
-                                               l.tail->leaf() + tail_size);
-                else
-                    std::uninitialized_copy(r.tail->leaf(),
-                                            r.tail->leaf() + r.size,
-                                            l.tail->leaf() + tail_size);
-                l.size += r.size;
-                return;
-            } else {
-                auto remaining = branches<BL> - tail_size;
-                l.ensure_mutable_tail(el, tail_size);
-                if (r.tail->can_mutate(er))
-                    detail::uninitialized_move(r.tail->leaf(),
-                                               r.tail->leaf() + remaining,
-                                               l.tail->leaf() + tail_size);
-                else
-                    std::uninitialized_copy(r.tail->leaf(),
-                                            r.tail->leaf() + remaining,
-                                            l.tail->leaf() + tail_size);
-                try {
-                    auto new_tail =
-                        node_t::copy_leaf_e(el, r.tail, remaining, r.size);
-                    try {
-                        l.push_tail_mut(el, tail_offst, l.tail, branches<BL>);
-                        l.tail = new_tail;
-                        l.size += r.size;
-                        return;
-                    } catch (...) {
-                        node_t::delete_leaf(new_tail, r.size - remaining);
-                        throw;
-                    }
-                } catch (...) {
-                    destroy_n(r.tail->leaf() + tail_size, remaining);
-                    throw;
-                }
-            }
-        } else if (l.tail_offset() == 0) {
-            if (supports_transient_concat) {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees_mut(el,
-                                                 el,
-                                                 l.tail,
-                                                 tail_size,
-                                                 er,
-                                                 r.root,
-                                                 r.shift,
-                                                 r.tail_offset());
-                IMMER_ASSERT_TAGGED(concated.shift() ==
-                                    concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(),
-                                              l.size + r.tail_offset()));
-                l.size += r.size;
-                l.shift = concated.shift();
-                l.root  = concated.node();
-                l.tail  = r.tail;
-                r.hard_reset();
-            } else {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees(
-                    l.tail, tail_size, r.root, r.shift, r.tail_offset());
-                l = {l.size + r.size,
-                     concated.shift(),
-                     concated.node(),
-                     r.tail->inc()};
-                return;
-            }
-        } else {
-            if (supports_transient_concat) {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees_mut(el,
-                                                 el,
-                                                 l.root,
-                                                 l.shift,
-                                                 tail_offst,
-                                                 l.tail,
-                                                 tail_size,
-                                                 er,
-                                                 r.root,
-                                                 r.shift,
-                                                 r.tail_offset());
-                IMMER_ASSERT_TAGGED(concated.shift() ==
-                                    concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(),
-                                              l.size + r.tail_offset()));
-                l.size += r.size;
-                l.shift = concated.shift();
-                l.root  = concated.node();
-                l.tail  = r.tail;
-                r.hard_reset();
-            } else {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees(l.root,
-                                             l.shift,
-                                             tail_offst,
-                                             l.tail,
-                                             tail_size,
-                                             r.root,
-                                             r.shift,
-                                             r.tail_offset());
-                l               = {l.size + r.size,
-                     concated.shift(),
-                     concated.node(),
-                     r.tail->inc()};
-            }
-        }
-    }
-
-    friend void concat_mut_lr_r(rrbtree& l, edit_t el, rrbtree& r, edit_t er)
-    {
-        assert(&l != &r);
-        assert(r.size < (std::numeric_limits<size_t>::max() - l.size));
-        using std::get;
-        if (r.size == 0)
-            r = l;
-        else if (l.size == 0)
-            return;
-        else if (r.tail_offset() == 0) {
-            // just concat the tail, similar to push_back
-            auto tail_offst = l.tail_offset();
-            auto tail_size  = l.size - tail_offst;
-            if (tail_size == branches<BL>) {
-                // this could be improved by making sure that the
-                // newly created nodes as part of the `push_tail()`
-                // are tagged with `er`
-                auto res =
-                    l.push_tail(l.root, l.shift, tail_offst, l.tail, tail_size);
-                r = {l.size + r.size, get<0>(res), get<1>(res), r.tail->inc()};
-                return;
-            } else if (tail_size + r.size <= branches<BL>) {
-                // doing this in a exception way mutating way is very
-                // tricky while potential performance gains are
-                // minimal (we need to move every element of the right
-                // tail anyways to make space for the left tail)
-                //
-                // we could however improve this by at least moving the
-                // elements of the mutable tails...
-                auto new_tail =
-                    node_t::copy_leaf(l.tail, tail_size, r.tail, r.size);
-                r = {l.size + r.size, l.shift, l.root->inc(), new_tail};
-                return;
-            } else {
-                // like the immutable version.
-                // we could improve this also by moving elements
-                // instead of just copying them
-                auto remaining = branches<BL> - tail_size;
-                auto add_tail  = node_t::copy_leaf_e(
-                    er, l.tail, tail_size, r.tail, remaining);
-                try {
-                    auto new_tail =
-                        node_t::copy_leaf_e(er, r.tail, remaining, r.size);
-                    try {
-                        // this could be improved by making sure that the
-                        // newly created nodes as part of the `push_tail()`
-                        // are tagged with `er`
-                        auto new_root = l.push_tail(l.root,
-                                                    l.shift,
-                                                    tail_offst,
-                                                    add_tail,
-                                                    branches<BL>);
-                        r             = {l.size + r.size,
-                             get<0>(new_root),
-                             get<1>(new_root),
-                             new_tail};
-                        return;
-                    } catch (...) {
-                        node_t::delete_leaf(new_tail, r.size - remaining);
-                        throw;
-                    }
-                } catch (...) {
-                    node_t::delete_leaf(add_tail, branches<BL>);
-                    throw;
-                }
-                return;
-            }
-        } else if (l.tail_offset() == 0) {
-            if (supports_transient_concat) {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees_mut(er,
-                                                 el,
-                                                 l.tail,
-                                                 tail_size,
-                                                 er,
-                                                 r.root,
-                                                 r.shift,
-                                                 r.tail_offset());
-                IMMER_ASSERT_TAGGED(concated.shift() ==
-                                    concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(),
-                                              l.size + r.tail_offset()));
-                r.size += l.size;
-                r.shift = concated.shift();
-                r.root  = concated.node();
-                l.hard_reset();
-            } else {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees(
-                    l.tail, tail_size, r.root, r.shift, r.tail_offset());
-                r = {l.size + r.size,
-                     concated.shift(),
-                     concated.node(),
-                     r.tail->inc()};
-                return;
-            }
-        } else {
-            if (supports_transient_concat) {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees_mut(er,
-                                                 el,
-                                                 l.root,
-                                                 l.shift,
-                                                 tail_offst,
-                                                 l.tail,
-                                                 tail_size,
-                                                 er,
-                                                 r.root,
-                                                 r.shift,
-                                                 r.tail_offset());
-                IMMER_ASSERT_TAGGED(concated.shift() ==
-                                    concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(),
-                                              l.size + r.tail_offset()));
-                r.size += l.size;
-                r.shift = concated.shift();
-                r.root  = concated.node();
-                l.hard_reset();
-            } else {
-                auto tail_offst = l.tail_offset();
-                auto tail_size  = l.size - tail_offst;
-                auto concated   = concat_trees(l.root,
-                                             l.shift,
-                                             tail_offst,
-                                             l.tail,
-                                             tail_size,
-                                             r.root,
-                                             r.shift,
-                                             r.tail_offset());
-                r               = {l.size + r.size,
-                     concated.shift(),
-                     concated.node(),
-                     r.tail->inc()};
-            }
-        }
-    }
-
-    void hard_reset()
-    {
-        assert(supports_transient_concat);
-        auto&& empty_ = empty();
-        size          = empty_.size;
-        shift         = empty_.shift;
-        root          = empty_.root;
-        tail          = empty_.tail;
-    }
-
-    bool check_tree() const
-    {
-        assert(shift <= sizeof(size_t) * 8 - BL);
-        assert(shift >= BL);
-        assert(tail_offset() <= size);
-        assert(tail_size() <= branches<BL>);
-#if IMMER_DEBUG_DEEP_CHECK
-        assert(check_root());
-        assert(check_tail());
-#endif
-        return true;
-    }
-
-    bool check_tail() const
-    {
-#if IMMER_DEBUG_DEEP_CHECK
-        if (tail_size() > 0)
-            assert(tail->check(endshift<B, BL>, tail_size()));
-#endif
-        return true;
-    }
-
-    bool check_root() const
-    {
-#if IMMER_DEBUG_DEEP_CHECK
-        if (tail_offset() > 0)
-            assert(root->check(shift, tail_offset()));
-        else {
-            IMMER_ASSERT_TAGGED(root->kind() == node_t::kind_t::inner);
-            assert(shift == BL);
-        }
-#endif
-        return true;
-    }
-
-#if IMMER_DEBUG_PRINT
-    void debug_print(std::ostream& out) const
-    {
-        out << "--" << std::endl
-            << "{" << std::endl
-            << "  size  = " << size << std::endl
-            << "  shift = " << shift << std::endl
-            << "  root  = " << std::endl;
-        debug_print_node(out, root, shift, tail_offset());
-        out << "  tail  = " << std::endl;
-        debug_print_node(out, tail, endshift<B, BL>, tail_size());
-        out << "}" << std::endl;
-    }
-
-    void debug_print_indent(std::ostream& out, unsigned indent) const
-    {
-        while (indent-- > 0)
-            out << ' ';
-    }
-
-    void debug_print_node(std::ostream& out,
-                          node_t* node,
-                          shift_t shift,
-                          size_t size,
-                          unsigned indent = 8) const
-    {
-        const auto indent_step = 4;
-
-        if (shift == endshift<B, BL>) {
-            debug_print_indent(out, indent);
-            out << "- {" << size << "} "
-                << pretty_print_array(node->leaf(), size) << std::endl;
-        } else if (auto r = node->relaxed()) {
-            auto count = r->d.count;
-            debug_print_indent(out, indent);
-            out << "# {" << size << "} "
-                << pretty_print_array(r->d.sizes, r->d.count) << std::endl;
-            auto last_size = size_t{};
-            for (auto i = count_t{}; i < count; ++i) {
-                debug_print_node(out,
-                                 node->inner()[i],
-                                 shift - B,
-                                 r->d.sizes[i] - last_size,
-                                 indent + indent_step);
-                last_size = r->d.sizes[i];
-            }
-        } else {
-            debug_print_indent(out, indent);
-            out << "+ {" << size << "}" << std::endl;
-            auto count =
-                (size >> shift) + (size - ((size >> shift) << shift) > 0);
-            if (count) {
-                for (auto i = count_t{}; i < count - 1; ++i)
-                    debug_print_node(out,
-                                     node->inner()[i],
-                                     shift - B,
-                                     1 << shift,
-                                     indent + indent_step);
-                debug_print_node(out,
-                                 node->inner()[count - 1],
-                                 shift - B,
-                                 size - ((count - 1) << shift),
-                                 indent + indent_step);
-            }
-        }
-    }
-#endif // IMMER_DEBUG_PRINT
-};
-
-} // namespace rbts
-} // namespace detail
-} // namespace immer