From fe889ce72c81dacb099d4fe7bad8fb7bc2356123 Mon Sep 17 00:00:00 2001
From: aleflm <aleflm@proton.me>
Date: Mon, 1 Apr 2024 13:22:14 +0000
Subject: [PATCH 1/2] Update immer library to v0.8.1
 (5875f7739a6c642ad58cbedadb509c86d421217e)

---
 src/immer/algorithm.hpp                       |  155 +-
 src/immer/array.hpp                           |  189 +-
 src/immer/array_transient.hpp                 |   79 +-
 src/immer/atom.hpp                            |   93 +-
 src/immer/box.hpp                             |  134 +-
 src/immer/config.hpp                          |   83 +-
 src/immer/detail/arrays/no_capacity.hpp       |   94 +-
 src/immer/detail/arrays/node.hpp              |   69 +-
 src/immer/detail/arrays/with_capacity.hpp     |  168 +-
 src/immer/detail/combine_standard_layout.hpp  |   92 +-
 src/immer/detail/hamts/bits.hpp               |  116 +-
 src/immer/detail/hamts/champ.hpp              | 1410 ++++++++++++--
 src/immer/detail/hamts/champ_iterator.hpp     |   50 +-
 src/immer/detail/hamts/node.hpp               |  930 +++++++---
 src/immer/detail/iterator_facade.hpp          |   93 +-
 src/immer/detail/rbts/bits.hpp                |    5 +-
 src/immer/detail/rbts/node.hpp                |  671 ++++---
 src/immer/detail/rbts/operations.hpp          | 1612 +++++++++--------
 src/immer/detail/rbts/position.hpp            |  944 ++++++----
 src/immer/detail/rbts/rbtree.hpp              |  319 ++--
 src/immer/detail/rbts/rbtree_iterator.hpp     |   37 +-
 src/immer/detail/rbts/rrbtree.hpp             | 1042 ++++++-----
 src/immer/detail/rbts/rrbtree_iterator.hpp    |   32 +-
 src/immer/detail/rbts/visitor.hpp             |   10 +-
 src/immer/detail/ref_count_base.hpp           |    2 +-
 src/immer/detail/type_traits.hpp              |  232 +--
 src/immer/detail/util.hpp                     |  265 ++-
 .../experimental/detail/dvektor_impl.hpp      |  205 +--
 src/immer/experimental/dvektor.hpp            |   36 +-
 src/immer/flex_vector.hpp                     |  330 ++--
 src/immer/flex_vector_transient.hpp           |  114 +-
 src/immer/heap/cpp_heap.hpp                   |    3 +-
 src/immer/heap/debug_size_heap.hpp            |   27 +-
 src/immer/heap/free_list_heap.hpp             |    3 +-
 src/immer/heap/free_list_node.hpp             |    3 +-
 src/immer/heap/gc_heap.hpp                    |   29 +-
 src/immer/heap/heap_policy.hpp                |   46 +-
 src/immer/heap/malloc_heap.hpp                |   11 +-
 src/immer/heap/split_heap.hpp                 |    6 +-
 src/immer/heap/tags.hpp                       |    3 +-
 .../heap/thread_local_free_list_heap.hpp      |   12 +-
 src/immer/heap/unsafe_free_list_heap.hpp      |   23 +-
 src/immer/lock/no_lock_policy.hpp             |   25 +
 src/immer/lock/spinlock_policy.hpp            |   68 +
 src/immer/map.hpp                             |  369 +++-
 src/immer/map_transient.hpp                   |  301 ++-
 src/immer/memory_policy.hpp                   |   52 +-
 src/immer/refcount/no_refcount_policy.hpp     |   20 +-
 src/immer/refcount/refcount_policy.hpp        |   89 +-
 src/immer/refcount/unsafe_refcount_policy.hpp |   10 +-
 src/immer/set.hpp                             |  196 +-
 src/immer/set_transient.hpp                   |  168 +-
 src/immer/table.hpp                           |  547 ++++++
 src/immer/table_transient.hpp                 |  281 +++
 src/immer/transience/gc_transience_policy.hpp |   11 +-
 src/immer/transience/no_transience_policy.hpp |    8 +-
 src/immer/vector.hpp                          |  193 +-
 src/immer/vector_transient.hpp                |   79 +-
 58 files changed, 8360 insertions(+), 3834 deletions(-)
 create mode 100644 src/immer/lock/no_lock_policy.hpp
 create mode 100644 src/immer/lock/spinlock_policy.hpp
 create mode 100644 src/immer/table.hpp
 create mode 100644 src/immer/table_transient.hpp

diff --git a/src/immer/algorithm.hpp b/src/immer/algorithm.hpp
index df9ff28a83..382df02dbc 100644
--- a/src/immer/algorithm.hpp
+++ b/src/immer/algorithm.hpp
@@ -9,6 +9,8 @@
 #pragma once
 
 #include <algorithm>
+#include <cassert>
+#include <functional>
 #include <numeric>
 #include <type_traits>
 
@@ -47,8 +49,8 @@ template <typename Iterator, typename Fn>
 void for_each_chunk(const Iterator& first, const Iterator& last, Fn&& fn)
 {
     assert(&first.impl() == &last.impl());
-    first.impl().for_each_chunk(first.index(), last.index(),
-                                std::forward<Fn>(fn));
+    first.impl().for_each_chunk(
+        first.index(), last.index(), std::forward<Fn>(fn));
 }
 
 template <typename T, typename Fn>
@@ -81,8 +83,8 @@ template <typename Iterator, typename Fn>
 bool for_each_chunk_p(const Iterator& first, const Iterator& last, Fn&& fn)
 {
     assert(&first.impl() == &last.impl());
-    return first.impl().for_each_chunk_p(first.index(), last.index(),
-                                         std::forward<Fn>(fn));
+    return first.impl().for_each_chunk_p(
+        first.index(), last.index(), std::forward<Fn>(fn));
 }
 
 template <typename T, typename Fn>
@@ -91,14 +93,34 @@ bool for_each_chunk_p(const T* first, const T* last, Fn&& fn)
     return std::forward<Fn>(fn)(first, last);
 }
 
+namespace detail {
+
+template <class Iter, class T>
+T accumulate_move(Iter first, Iter last, T init)
+{
+    for (; first != last; ++first)
+        init = std::move(init) + *first;
+    return init;
+}
+
+template <class Iter, class T, class Fn>
+T accumulate_move(Iter first, Iter last, T init, Fn op)
+{
+    for (; first != last; ++first)
+        init = op(std::move(init), *first);
+    return init;
+}
+
+} // namespace detail
+
 /*!
  * Equivalent of `std::accumulate` applied to the range `r`.
  */
 template <typename Range, typename T>
 T accumulate(Range&& r, T init)
 {
-    for_each_chunk(r, [&] (auto first, auto last) {
-        init = std::accumulate(first, last, init);
+    for_each_chunk(r, [&](auto first, auto last) {
+        init = detail::accumulate_move(first, last, init);
     });
     return init;
 }
@@ -106,8 +128,8 @@ T accumulate(Range&& r, T init)
 template <typename Range, typename T, typename Fn>
 T accumulate(Range&& r, T init, Fn fn)
 {
-    for_each_chunk(r, [&] (auto first, auto last) {
-        init = std::accumulate(first, last, init, fn);
+    for_each_chunk(r, [&](auto first, auto last) {
+        init = detail::accumulate_move(first, last, init, fn);
     });
     return init;
 }
@@ -119,8 +141,8 @@ T accumulate(Range&& r, T init, Fn fn)
 template <typename Iterator, typename T>
 T accumulate(Iterator first, Iterator last, T init)
 {
-    for_each_chunk(first, last, [&] (auto first, auto last) {
-        init = std::accumulate(first, last, init);
+    for_each_chunk(first, last, [&](auto first, auto last) {
+        init = detail::accumulate_move(first, last, init);
     });
     return init;
 }
@@ -128,8 +150,8 @@ T accumulate(Iterator first, Iterator last, T init)
 template <typename Iterator, typename T, typename Fn>
 T accumulate(Iterator first, Iterator last, T init, Fn fn)
 {
-    for_each_chunk(first, last, [&] (auto first, auto last) {
-        init = std::accumulate(first, last, init, fn);
+    for_each_chunk(first, last, [&](auto first, auto last) {
+        init = detail::accumulate_move(first, last, init, fn);
     });
     return init;
 }
@@ -140,7 +162,7 @@ T accumulate(Iterator first, Iterator last, T init, Fn fn)
 template <typename Range, typename Fn>
 Fn&& for_each(Range&& r, Fn&& fn)
 {
-    for_each_chunk(r, [&] (auto first, auto last) {
+    for_each_chunk(r, [&](auto first, auto last) {
         for (; first != last; ++first)
             fn(*first);
     });
@@ -154,7 +176,7 @@ Fn&& for_each(Range&& r, Fn&& fn)
 template <typename Iterator, typename Fn>
 Fn&& for_each(Iterator first, Iterator last, Fn&& fn)
 {
-    for_each_chunk(first, last, [&] (auto first, auto last) {
+    for_each_chunk(first, last, [&](auto first, auto last) {
         for (; first != last; ++first)
             fn(*first);
     });
@@ -167,9 +189,8 @@ Fn&& for_each(Iterator first, Iterator last, Fn&& fn)
 template <typename Range, typename OutIter>
 OutIter copy(Range&& r, OutIter out)
 {
-    for_each_chunk(r, [&] (auto first, auto last) {
-        out = std::copy(first, last, out);
-    });
+    for_each_chunk(
+        r, [&](auto first, auto last) { out = std::copy(first, last, out); });
     return out;
 }
 
@@ -180,7 +201,7 @@ OutIter copy(Range&& r, OutIter out)
 template <typename InIter, typename OutIter>
 OutIter copy(InIter first, InIter last, OutIter out)
 {
-    for_each_chunk(first, last, [&] (auto first, auto last) {
+    for_each_chunk(first, last, [&](auto first, auto last) {
         out = std::copy(first, last, out);
     });
     return out;
@@ -192,9 +213,8 @@ OutIter copy(InIter first, InIter last, OutIter out)
 template <typename Range, typename Pred>
 bool all_of(Range&& r, Pred p)
 {
-    return for_each_chunk_p(r, [&] (auto first, auto last) {
-        return std::all_of(first, last, p);
-    });
+    return for_each_chunk_p(
+        r, [&](auto first, auto last) { return std::all_of(first, last, p); });
 }
 
 /*!
@@ -204,11 +224,102 @@ bool all_of(Range&& r, Pred p)
 template <typename Iter, typename Pred>
 bool all_of(Iter first, Iter last, Pred p)
 {
-    return for_each_chunk_p(first, last, [&] (auto first, auto last) {
+    return for_each_chunk_p(first, last, [&](auto first, auto last) {
         return std::all_of(first, last, p);
     });
 }
 
+/*!
+ * Object that can be used to process changes as computed by the @a diff
+ * algorithm.
+ *
+ * @tparam AddedFn Unary function that is be called whenever an added element is
+ *         found. It is called with the added element as argument.
+ *
+ * @tparam RemovedFn Unary function that is called whenever a removed element is
+ *         found.  It is called with the removed element as argument.
+ *
+ * @tparam ChangedFn Unary function that is called whenever a changed element is
+ *         found.  It is called with the changed element as argument.
+ */
+template <class AddedFn, class RemovedFn, class ChangedFn>
+struct differ
+{
+    AddedFn added;
+    RemovedFn removed;
+    ChangedFn changed;
+};
+
+/*!
+ * Produces a @a differ object with `added`, `removed` and `changed` functions.
+ */
+template <class AddedFn, class RemovedFn, class ChangedFn>
+auto make_differ(AddedFn&& added, RemovedFn&& removed, ChangedFn&& changed)
+    -> differ<std::decay_t<AddedFn>,
+              std::decay_t<RemovedFn>,
+              std::decay_t<ChangedFn>>
+{
+    return {std::forward<AddedFn>(added),
+            std::forward<RemovedFn>(removed),
+            std::forward<ChangedFn>(changed)};
+}
+
+/*!
+ * Produces a @a differ object with `added` and `removed` functions and no
+ * `changed` function.
+ */
+template <class AddedFn, class RemovedFn>
+auto make_differ(AddedFn&& added, RemovedFn&& removed)
+{
+    return make_differ(std::forward<AddedFn>(added),
+                       std::forward<RemovedFn>(removed),
+                       [](auto&&...) {});
+}
+
+/*!
+ * Compute the differences between `a` and `b`.
+ *
+ * Changes detected are notified via the differ object, which should support the
+ * following expressions:
+ *
+ *   - `differ.added(x)`, invoked when element `x` is found in `b` but not in
+ *      `a`.
+ *
+ *   - `differ.removed(x)`, invoked when element `x` is found in `a` but not in
+ *      `b`.
+ *
+ *   - `differ.changed(x, y)`, invoked when element `x` and `y` from `a` and `b`
+ *      share the same key but map to a different value.
+ *
+ * This method leverages structural sharing to offer a complexity @f$ O(|diff|)
+ * @f$ when `b` is derived from `a` by performing @f$ |diff| @f$ updates.  This
+ * is, this function can detect changes in effectively constant time per update,
+ * as oposed to the @f$ O(|a|+|b|) @f$ complexity of a trivial implementation.
+ *
+ * @rst
+ *
+ * .. note:: This method is only implemented for ``map`` and ``set``. When sets
+ *           are diffed, the ``changed`` function is never called.
+ *
+ * @endrst
+ */
+template <typename T, typename Differ>
+void diff(const T& a, const T& b, Differ&& differ)
+{
+    a.impl().template diff<std::equal_to<typename T::value_type>>(
+        b.impl(), std::forward<Differ>(differ));
+}
+
+/*!
+ * Compute the differences between `a` and `b` using the callbacks in `fns` as
+ * differ.  Equivalent to `diff(a, b, make_differ(fns)...)`.
+ */
+template <typename T, typename... Fns>
+void diff(const T& a, const T& b, Fns&&... fns)
+{
+    diff(a, b, make_differ(std::forward<Fns>(fns)...));
+}
+
 /** @} */ // group: algorithm
 
 } // namespace immer
diff --git a/src/immer/array.hpp b/src/immer/array.hpp
index 0f73649fb3..f71477c239 100644
--- a/src/immer/array.hpp
+++ b/src/immer/array.hpp
@@ -8,8 +8,10 @@
 
 #pragma once
 
-#include <immer/memory_policy.hpp>
 #include <immer/detail/arrays/with_capacity.hpp>
+#include <immer/memory_policy.hpp>
+
+#include <cstddef>
 
 namespace immer {
 
@@ -31,7 +33,7 @@ class array_transient;
  * .. tip:: Don't be fooled by the bad complexity of this data
  *    structure.  It is a great choice for short sequence or when it
  *    is seldom or never changed.  This depends on the ``sizeof(T)``
- *    and the expensiveness of its ``T``'s copy constructor, in case
+ *    and the expensiveness of its ``T``'s copy constructor. In case
  *    of doubt, measure.  For basic types, using an `array` when
  *    :math:`n < 100` is a good heuristic.
  *
@@ -40,18 +42,18 @@ class array_transient;
 template <typename T, typename MemoryPolicy = default_memory_policy>
 class array
 {
-    using impl_t = std::conditional_t<
-        MemoryPolicy::use_transient_rvalues,
-        detail::arrays::with_capacity<T, MemoryPolicy>,
-        detail::arrays::no_capacity<T, MemoryPolicy>>;
+    using impl_t =
+        std::conditional_t<MemoryPolicy::use_transient_rvalues,
+                           detail::arrays::with_capacity<T, MemoryPolicy>,
+                           detail::arrays::no_capacity<T, MemoryPolicy>>;
 
     using move_t =
         std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>;
 
 public:
-    using value_type = T;
-    using reference = const T&;
-    using size_type = std::size_t;
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = std::size_t;
     using difference_type = std::ptrdiff_t;
     using const_reference = const T&;
 
@@ -79,16 +81,17 @@ class array
      * Constructs a array containing the elements in the range
      * defined by the forward iterator `first` and range sentinel `last`.
      */
-    template <typename Iter, typename Sent,
-              std::enable_if_t
-              <detail::compatible_sentinel_v<Iter, Sent>
-               && detail::is_forward_iterator_v<Iter>, bool> = true>
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent> &&
+                                   detail::is_forward_iterator_v<Iter>,
+                               bool> = true>
     array(Iter first, Sent last)
         : impl_{impl_t::from_range(first, last)}
     {}
 
     /*!
-     * Constructs a array containing the element `val` repeated `n`
+     * Constructs an array containing the element `val` repeated `n`
      * times.
      */
     array(size_type n, T v = {})
@@ -100,63 +103,72 @@ class array
      * collection. It does not allocate memory and its complexity is
      * @f$ O(1) @f$.
      */
-    iterator begin() const { return impl_.data(); }
+    IMMER_NODISCARD iterator begin() const { return impl_.data(); }
 
     /*!
      * Returns an iterator pointing just after the last element of the
-     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     * collection. It does not allocate memory and its complexity is @f$ O(1)
+     * @f$.
      */
-    iterator end()   const { return impl_.data() + impl_.size; }
+    IMMER_NODISCARD iterator end() const { return impl_.data() + impl_.size; }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing at the first element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rbegin() const { return reverse_iterator{end()}; }
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing after the last element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rend()   const { return reverse_iterator{begin()}; }
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
 
     /*!
      * Returns the number of elements in the container.  It does
      * not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    std::size_t size() const { return impl_.size; }
+    IMMER_NODISCARD std::size_t size() const { return impl_.size; }
 
     /*!
      * Returns `true` if there are no elements in the container.  It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    bool empty() const { return impl_.d->empty(); }
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
 
     /*!
      * Access the raw data.
      */
-    const T* data() const { return impl_.data(); }
+    IMMER_NODISCARD const T* data() const { return impl_.data(); }
 
     /*!
      * Access the last element.
      */
-    const T& back() const { return data()[size() - 1]; }
+    IMMER_NODISCARD const T& back() const { return data()[size() - 1]; }
 
     /*!
      * Access the first element.
      */
-    const T& front() const { return data()[0]; }
+    IMMER_NODISCARD const T& front() const { return data()[0]; }
 
     /*!
      * Returns a `const` reference to the element at position `index`.
-     * It is undefined when @f$ 0 index \geq size() @f$.  It does not
+     * It is undefined when @f$ index \geq size() @f$.  It does not
      * allocate memory and its complexity is *effectively* @f$ O(1)
      * @f$.
      */
-    reference operator[] (size_type index) const
-    { return impl_.get(index); }
+    IMMER_NODISCARD reference operator[](size_type index) const
+    {
+        return impl_.get(index);
+    }
 
     /*!
      * Returns a `const` reference to the element at position
@@ -164,16 +176,19 @@ class array
      * index \geq size() @f$.  It does not allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
-    reference at(size_type index) const
-    { return impl_.get_check(index); }
+    reference at(size_type index) const { return impl_.get_check(index); }
 
     /*!
      * Returns whether the vectors are equal.
      */
-    bool operator==(const array& other) const
-    { return impl_.equals(other.impl_); }
-    bool operator!=(const array& other) const
-    { return !(*this == other); }
+    IMMER_NODISCARD bool operator==(const array& other) const
+    {
+        return impl_.equals(other.impl_);
+    }
+    IMMER_NODISCARD bool operator!=(const array& other) const
+    {
+        return !(*this == other);
+    }
 
     /*!
      * Returns an array with `value` inserted at the end.  It may
@@ -190,11 +205,15 @@ class array
      *
      * @endrst
      */
-    array push_back(value_type value) const&
-    { return impl_.push_back(std::move(value)); }
+    IMMER_NODISCARD array push_back(value_type value) const&
+    {
+        return impl_.push_back(std::move(value));
+    }
 
-    decltype(auto) push_back(value_type value) &&
-    { return push_back_move(move_t{}, std::move(value)); }
+    IMMER_NODISCARD decltype(auto) push_back(value_type value) &&
+    {
+        return push_back_move(move_t{}, std::move(value));
+    }
 
     /*!
      * Returns an array containing value `value` at position `idx`.
@@ -212,11 +231,15 @@ class array
      *
      * @endrst
      */
-    array set(std::size_t index, value_type value) const&
-    { return impl_.assoc(index, std::move(value)); }
+    IMMER_NODISCARD array set(std::size_t index, value_type value) const&
+    {
+        return impl_.assoc(index, std::move(value));
+    }
 
-    decltype(auto) set(size_type index, value_type value) &&
-    { return set_move(move_t{}, index, std::move(value)); }
+    IMMER_NODISCARD decltype(auto) set(size_type index, value_type value) &&
+    {
+        return set_move(move_t{}, index, std::move(value));
+    }
 
     /*!
      * Returns an array containing the result of the expression
@@ -236,12 +259,16 @@ class array
      * @endrst
      */
     template <typename FnT>
-    array update(std::size_t index, FnT&& fn) const&
-    { return impl_.update(index, std::forward<FnT>(fn)); }
+    IMMER_NODISCARD array update(std::size_t index, FnT&& fn) const&
+    {
+        return impl_.update(index, std::forward<FnT>(fn));
+    }
 
     template <typename FnT>
-    decltype(auto) update(size_type index, FnT&& fn) &&
-    { return update_move(move_t{}, index, std::forward<FnT>(fn)); }
+    IMMER_NODISCARD decltype(auto) update(size_type index, FnT&& fn) &&
+    {
+        return update_move(move_t{}, index, std::forward<FnT>(fn));
+    }
 
     /*!
      * Returns a array containing only the first `min(elems, size())`
@@ -259,20 +286,36 @@ class array
      *
      * @endrst
      */
-    array take(size_type elems) const&
-    { return impl_.take(elems); }
+    IMMER_NODISCARD array take(size_type elems) const&
+    {
+        return impl_.take(elems);
+    }
 
-    decltype(auto) take(size_type elems) &&
-    { return take_move(move_t{}, elems); }
+    IMMER_NODISCARD decltype(auto) take(size_type elems) &&
+    {
+        return take_move(move_t{}, elems);
+    }
 
     /*!
      * Returns an @a transient form of this container, an
      * `immer::array_transient`.
      */
-    transient_type transient() const&
-    { return transient_type{ impl_ }; }
-    transient_type transient() &&
-    { return transient_type{ std::move(impl_) }; }
+    IMMER_NODISCARD transient_type transient() const&
+    {
+        return transient_type{impl_};
+    }
+    IMMER_NODISCARD transient_type transient() &&
+    {
+        return transient_type{std::move(impl_)};
+    }
+
+    /*!
+     * Returns a value that can be used as identity for the container.  If two
+     * values have the same identity, they are guaranteed to be equal and to
+     * contain the same objects.  However, two equal containers are not
+     * guaranteed to have the same identity.
+     */
+    void* identity() const { return impl_.ptr; }
 
     // Semi-private
     const impl_t& impl() const { return impl_; }
@@ -280,29 +323,51 @@ class array
 private:
     friend transient_type;
 
-    array(impl_t impl) : impl_(std::move(impl)) {}
+    array(impl_t impl)
+        : impl_(std::move(impl))
+    {}
 
     array&& push_back_move(std::true_type, value_type value)
-    { impl_.push_back_mut({}, std::move(value)); return std::move(*this); }
+    {
+        impl_.push_back_mut({}, std::move(value));
+        return std::move(*this);
+    }
     array push_back_move(std::false_type, value_type value)
-    { return impl_.push_back(std::move(value)); }
+    {
+        return impl_.push_back(std::move(value));
+    }
 
     array&& set_move(std::true_type, size_type index, value_type value)
-    { impl_.assoc_mut({}, index, std::move(value)); return std::move(*this); }
+    {
+        impl_.assoc_mut({}, index, std::move(value));
+        return std::move(*this);
+    }
     array set_move(std::false_type, size_type index, value_type value)
-    { return impl_.assoc(index, std::move(value)); }
+    {
+        return impl_.assoc(index, std::move(value));
+    }
 
     template <typename Fn>
     array&& update_move(std::true_type, size_type index, Fn&& fn)
-    { impl_.update_mut({}, index, std::forward<Fn>(fn)); return std::move(*this); }
+    {
+        impl_.update_mut({}, index, std::forward<Fn>(fn));
+        return std::move(*this);
+    }
     template <typename Fn>
     array update_move(std::false_type, size_type index, Fn&& fn)
-    { return impl_.update(index, std::forward<Fn>(fn)); }
+    {
+        return impl_.update(index, std::forward<Fn>(fn));
+    }
 
     array&& take_move(std::true_type, size_type elems)
-    { impl_.take_mut({}, elems); return std::move(*this); }
+    {
+        impl_.take_mut({}, elems);
+        return std::move(*this);
+    }
     array take_move(std::false_type, size_type elems)
-    { return impl_.take(elems); }
+    {
+        return impl_.take(elems);
+    }
 
     impl_t impl_ = impl_t::empty();
 };
diff --git a/src/immer/array_transient.hpp b/src/immer/array_transient.hpp
index 0084e47ddd..08000b2d92 100644
--- a/src/immer/array_transient.hpp
+++ b/src/immer/array_transient.hpp
@@ -8,8 +8,10 @@
 
 #pragma once
 
-#include <immer/memory_policy.hpp>
 #include <immer/detail/arrays/with_capacity.hpp>
+#include <immer/memory_policy.hpp>
+
+#include <cstddef>
 
 namespace immer {
 
@@ -27,17 +29,16 @@ class array;
  * @endrst
  */
 template <typename T, typename MemoryPolicy = default_memory_policy>
-class array_transient
-    : MemoryPolicy::transience_t::owner
+class array_transient : MemoryPolicy::transience_t::owner
 {
-    using impl_t = detail::arrays::with_capacity<T, MemoryPolicy>;
+    using impl_t             = detail::arrays::with_capacity<T, MemoryPolicy>;
     using impl_no_capacity_t = detail::arrays::no_capacity<T, MemoryPolicy>;
-    using owner_t = typename MemoryPolicy::transience_t::owner;
+    using owner_t            = typename MemoryPolicy::transience_t::owner;
 
 public:
-    using value_type = T;
-    using reference = const T&;
-    using size_type = std::size_t;
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = std::size_t;
     using difference_type = std::ptrdiff_t;
     using const_reference = const T&;
 
@@ -60,54 +61,65 @@ class array_transient
      * collection. It does not allocate memory and its complexity is
      * @f$ O(1) @f$.
      */
-    iterator begin() const { return impl_.data(); }
+    IMMER_NODISCARD iterator begin() const { return impl_.data(); }
 
     /*!
      * Returns an iterator pointing just after the last element of the
      * collection. It does not allocate and its complexity is @f$ O(1) @f$.
      */
-    iterator end()   const { return impl_.data() + impl_.size; }
+    IMMER_NODISCARD iterator end() const { return impl_.data() + impl_.size; }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing at the first element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rbegin() const { return reverse_iterator{end()}; }
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing after the last element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rend()   const { return reverse_iterator{begin()}; }
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
 
     /*!
      * Returns the number of elements in the container.  It does
      * not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    std::size_t size() const { return impl_.size; }
+    IMMER_NODISCARD std::size_t size() const { return impl_.size; }
 
     /*!
      * Returns `true` if there are no elements in the container.  It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    bool empty() const { return impl_.d->empty(); }
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
 
     /*!
      * Access the raw data.
      */
-    const T* data() const { return impl_.data(); }
+    IMMER_NODISCARD const T* data() const { return impl_.data(); }
+
+    /*!
+     * Provide mutable access to the raw underlaying data.
+     */
+    IMMER_NODISCARD T* data_mut() { return impl_.data_mut(*this); }
 
     /*!
      * Access the last element.
      */
-    const T& back() const { return data()[size() - 1]; }
+    IMMER_NODISCARD const T& back() const { return data()[size() - 1]; }
 
     /*!
      * Access the first element.
      */
-    const T& front() const { return data()[0]; }
+    IMMER_NODISCARD const T& front() const { return data()[0]; }
 
     /*!
      * Returns a `const` reference to the element at position `index`.
@@ -115,8 +127,7 @@ class array_transient
      * allocate memory and its complexity is *effectively* @f$ O(1)
      * @f$.
      */
-    reference operator[] (size_type index) const
-    { return impl_.get(index); }
+    reference operator[](size_type index) const { return impl_.get(index); }
 
     /*!
      * Returns a `const` reference to the element at position
@@ -124,15 +135,16 @@ class array_transient
      * index \geq size() @f$.  It does not allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
-    reference at(size_type index) const
-    { return impl_.get_check(index); }
+    reference at(size_type index) const { return impl_.get_check(index); }
 
     /*!
      * Inserts `value` at the end.  It may allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
     void push_back(value_type value)
-    { impl_.push_back_mut(*this, std::move(value)); }
+    {
+        impl_.push_back_mut(*this, std::move(value));
+    }
 
     /*!
      * Sets to the value `value` at position `idx`.
@@ -141,7 +153,9 @@ class array_transient
      * *effectively* @f$ O(1) @f$.
      */
     void set(size_type index, value_type value)
-    { impl_.assoc_mut(*this, index, std::move(value)); }
+    {
+        impl_.assoc_mut(*this, index, std::move(value));
+    }
 
     /*!
      * Updates the array to contain the result of the expression
@@ -152,27 +166,30 @@ class array_transient
      */
     template <typename FnT>
     void update(size_type index, FnT&& fn)
-    { impl_.update_mut(*this, index, std::forward<FnT>(fn)); }
+    {
+        impl_.update_mut(*this, index, std::forward<FnT>(fn));
+    }
 
     /*!
      * Resizes the array to only contain the first `min(elems, size())`
      * elements. It may allocate memory and its complexity is
      * *effectively* @f$ O(1) @f$.
      */
-    void take(size_type elems)
-    { impl_.take_mut(*this, elems); }
+    void take(size_type elems) { impl_.take_mut(*this, elems); }
 
     /*!
      * Returns an @a immutable form of this container, an
      * `immer::array`.
      */
-    persistent_type persistent() &
+    IMMER_NODISCARD persistent_type persistent() &
     {
         this->owner_t::operator=(owner_t{});
-        return persistent_type{ impl_ };
+        return persistent_type{impl_};
+    }
+    IMMER_NODISCARD persistent_type persistent() &&
+    {
+        return persistent_type{std::move(impl_)};
     }
-    persistent_type persistent() &&
-    { return persistent_type{ std::move(impl_) }; }
 
 private:
     friend persistent_type;
@@ -181,7 +198,7 @@ class array_transient
         : impl_(std::move(impl))
     {}
 
-    impl_t  impl_  = impl_t::empty;
+    impl_t impl_ = impl_t::empty();
 };
 
 } // namespace immer
diff --git a/src/immer/atom.hpp b/src/immer/atom.hpp
index 206f3c497a..2cf409b916 100644
--- a/src/immer/atom.hpp
+++ b/src/immer/atom.hpp
@@ -21,14 +21,14 @@ namespace detail {
 template <typename T, typename MemoryPolicy>
 struct refcount_atom_impl
 {
-    using box_type = box<T, MemoryPolicy>;
-    using value_type = T;
+    using box_type      = box<T, MemoryPolicy>;
+    using value_type    = T;
     using memory_policy = MemoryPolicy;
-    using spinlock_t = typename MemoryPolicy::refcount::spinlock_type;
-    using scoped_lock_t = typename spinlock_t::scoped_lock;
+    using lock_t        = typename MemoryPolicy::lock;
+    using scoped_lock_t = typename lock_t::scoped_lock;
 
     refcount_atom_impl(const refcount_atom_impl&) = delete;
-    refcount_atom_impl(refcount_atom_impl&&) = delete;
+    refcount_atom_impl(refcount_atom_impl&&)      = delete;
     refcount_atom_impl& operator=(const refcount_atom_impl&) = delete;
     refcount_atom_impl& operator=(refcount_atom_impl&&) = delete;
 
@@ -54,7 +54,7 @@ struct refcount_atom_impl
             scoped_lock_t lock{lock_};
             swap(b, impl_);
         }
-        return std::move(b);
+        return b;
     }
 
     template <typename Fn>
@@ -67,31 +67,30 @@ struct refcount_atom_impl
                 scoped_lock_t lock{lock_};
                 if (oldv.impl_ == impl_.impl_) {
                     impl_ = newv;
-                    return { newv };
+                    return {newv};
                 }
             }
         }
     }
 
 private:
-    mutable spinlock_t lock_;
+    mutable lock_t lock_;
     box_type impl_;
 };
 
 template <typename T, typename MemoryPolicy>
 struct gc_atom_impl
 {
-    using box_type = box<T, MemoryPolicy>;
-    using value_type = T;
+    using box_type      = box<T, MemoryPolicy>;
+    using value_type    = T;
     using memory_policy = MemoryPolicy;
 
-    static_assert(
-        std::is_same<typename MemoryPolicy::refcount,
-                     no_refcount_policy>::value,
-        "gc_atom_impl can only be used when there is no refcount!");
+    static_assert(std::is_same<typename MemoryPolicy::refcount,
+                               no_refcount_policy>::value,
+                  "gc_atom_impl can only be used when there is no refcount!");
 
     gc_atom_impl(const gc_atom_impl&) = delete;
-    gc_atom_impl(gc_atom_impl&&) = delete;
+    gc_atom_impl(gc_atom_impl&&)      = delete;
     gc_atom_impl& operator=(const gc_atom_impl&) = delete;
     gc_atom_impl& operator=(gc_atom_impl&&) = delete;
 
@@ -99,14 +98,11 @@ struct gc_atom_impl
         : impl_{b.impl_}
     {}
 
-    box_type load() const
-    { return {impl_.load()}; }
+    box_type load() const { return {impl_.load()}; }
 
-    void store(box_type b)
-    { impl_.store(b.impl_); }
+    void store(box_type b) { impl_.store(b.impl_); }
 
-    box_type exchange(box_type b)
-    { return {impl_.exchange(b.impl_)}; }
+    box_type exchange(box_type b) { return {impl_.exchange(b.impl_)}; }
 
     template <typename Fn>
     box_type update(Fn&& fn)
@@ -115,7 +111,7 @@ struct gc_atom_impl
             auto oldv = box_type{impl_.load()};
             auto newv = oldv.update(fn);
             if (impl_.compare_exchange_weak(oldv.impl_, newv.impl_))
-                return { newv };
+                return {newv};
         }
     }
 
@@ -133,40 +129,39 @@ struct gc_atom_impl
  * @rst
  *
  * .. warning:: If memory policy used includes thread unsafe reference counting,
- *    no no thread safety is assumed, and the atom becomes thread unsafe too!
+ *    no thread safety is assumed, and the atom becomes thread unsafe too!
  *
- * .. note:: ``box<T>`` provides a value based box of type ``T``, this is, we can
- *    think about it as a value-based version of ``std::shared_ptr``.  In a
+ * .. note:: ``box<T>`` provides a value based box of type ``T``, this is, we
+ *    can think about it as a value-based version of ``std::shared_ptr``.  In a
  *    similar fashion, ``atom<T>`` is in spirit the value-based equivalent of
  *    C++20 ``std::atomic_shared_ptr``.  However, the API does not follow
  *    ``std::atomic`` interface closely, since it attempts to be a higher level
  *    construction, most similar to Clojure's ``(atom)``.  It is remarkable in
  *    particular that, since ``box<T>`` underlying object is immutable, using
- *    ``atom<T>`` is fully thread-safe in ways that ``std::atmic_shared_ptr`` is
+ *    ``atom<T>`` is fully thread-safe in ways that ``std::atomic_shared_ptr`` is
  *    not. This is so because dereferencing the underlying pointer in a
- *    ``std::atomic_share_ptr`` may require further synchronization, in particular
- *    when invoking non-const methods.
+ *    ``std::atomic_share_ptr`` may require further synchronization, in
+ *    particular when invoking non-const methods.
  *
  * @endrst
  */
-template <typename T,
-          typename MemoryPolicy = default_memory_policy>
+template <typename T, typename MemoryPolicy = default_memory_policy>
 class atom
 {
 public:
-    using box_type = box<T, MemoryPolicy>;
-    using value_type = T;
+    using box_type      = box<T, MemoryPolicy>;
+    using value_type    = T;
     using memory_policy = MemoryPolicy;
 
     atom(const atom&) = delete;
-    atom(atom&&) = delete;
+    atom(atom&&)      = delete;
     void operator=(const atom&) = delete;
     void operator=(atom&&) = delete;
 
     /*!
      * Constructs an atom holding a value `b`;
      */
-    atom(box_type v={})
+    atom(box_type v = {})
         : impl_{std::move(v)}
     {}
 
@@ -182,32 +177,30 @@ class atom
     /*!
      * Reads the currently stored value in a thread-safe manner.
      */
-    operator box_type() const
-    { return impl_.load(); }
+    operator box_type() const { return impl_.load(); }
 
     /*!
      * Reads the currently stored value in a thread-safe manner.
      */
-    operator value_type() const
-    { return *impl_.load(); }
+    operator value_type() const { return *impl_.load(); }
 
     /*!
      * Reads the currently stored value in a thread-safe manner.
      */
-    box_type load() const
-    { return impl_.load(); }
+    IMMER_NODISCARD box_type load() const { return impl_.load(); }
 
     /*!
      * Stores a new value in a thread-safe manner.
      */
-    void store(box_type b)
-    { impl_.store(std::move(b)); }
+    void store(box_type b) { impl_.store(std::move(b)); }
 
     /*!
      * Stores a new value and returns the old value, in a thread-safe manner.
      */
-    box_type exchange(box_type b)
-    { return impl_.exchange(std::move(b)); }
+    IMMER_NODISCARD box_type exchange(box_type b)
+    {
+        return impl_.exchange(std::move(b));
+    }
 
     /*!
      * Stores the result of applying `fn` to the current value atomically and
@@ -223,7 +216,9 @@ class atom
      */
     template <typename Fn>
     box_type update(Fn&& fn)
-    { return impl_.update(std::forward<Fn>(fn)); }
+    {
+        return impl_.update(std::forward<Fn>(fn));
+    }
 
 private:
     struct get_refcount_atom_impl
@@ -248,12 +243,12 @@ class atom
     // `no_refcount_policy`), we just store the pointer in an atomic.  If we use
     // reference counting, we rely on the reference counting spinlock.
     using impl_t = typename std::conditional_t<
-        std::is_same<typename MemoryPolicy::refcount, no_refcount_policy>::value,
+        std::is_same<typename MemoryPolicy::refcount,
+                     no_refcount_policy>::value,
         get_gc_atom_impl,
-        get_refcount_atom_impl
-    >::template apply<T, MemoryPolicy>::type;
+        get_refcount_atom_impl>::template apply<T, MemoryPolicy>::type;
 
     impl_t impl_;
 };
 
-}
+} // namespace immer
diff --git a/src/immer/box.hpp b/src/immer/box.hpp
index b8ad19e1a4..711ac78f09 100644
--- a/src/immer/box.hpp
+++ b/src/immer/box.hpp
@@ -11,6 +11,8 @@
 #include <immer/detail/util.hpp>
 #include <immer/memory_policy.hpp>
 
+#include <cstddef>
+
 namespace immer {
 
 namespace detail {
@@ -30,8 +32,7 @@ struct refcount_atom_impl;
  * operations are never called.  Since a box is immutable, copying or
  * moving just copy the underlying pointers.
  */
-template <typename T,
-          typename MemoryPolicy = default_memory_policy>
+template <typename T, typename MemoryPolicy = default_memory_policy>
 class box
 {
     friend struct detail::gc_atom_impl<T, MemoryPolicy>;
@@ -42,51 +43,70 @@ class box
         T value;
 
         template <typename... Args>
-        holder(Args&&... args) : value{std::forward<Args>(args)...} {}
+        holder(Args&&... args)
+            : value{std::forward<Args>(args)...}
+        {}
     };
 
     using heap = typename MemoryPolicy::heap::type;
 
     holder* impl_ = nullptr;
 
-    box(holder* impl) : impl_{impl} {}
+    box(holder* impl)
+        : impl_{impl}
+    {}
 
 public:
+    const holder* impl() const { return impl_; };
+
     using value_type    = T;
     using memory_policy = MemoryPolicy;
 
     /*!
      * Constructs a box holding `T{}`.
      */
-    box() : impl_{detail::make<heap, holder>()} {}
+    box()
+        : impl_{detail::make<heap, holder>()}
+    {}
 
     /*!
      * Constructs a box holding `T{arg}`
      */
     template <typename Arg,
-              typename Enable=std::enable_if_t<
+              typename Enable = std::enable_if_t<
                   !std::is_same<box, std::decay_t<Arg>>::value &&
                   std::is_constructible<T, Arg>::value>>
     box(Arg&& arg)
-        : impl_{detail::make<heap, holder>(std::forward<Arg>(arg))} {}
+        : impl_{detail::make<heap, holder>(std::forward<Arg>(arg))}
+    {}
 
     /*!
      * Constructs a box holding `T{arg1, arg2, args...}`
      */
     template <typename Arg1, typename Arg2, typename... Args>
-    box(Arg1&& arg1, Arg2&& arg2, Args&& ...args)
-        : impl_{detail::make<heap, holder>(
-            std::forward<Arg1>(arg1),
-            std::forward<Arg2>(arg2),
-            std::forward<Args>(args)...)}
+    box(Arg1&& arg1, Arg2&& arg2, Args&&... args)
+        : impl_{detail::make<heap, holder>(std::forward<Arg1>(arg1),
+                                           std::forward<Arg2>(arg2),
+                                           std::forward<Args>(args)...)}
     {}
 
     friend void swap(box& a, box& b)
-    { using std::swap; swap(a.impl_, b.impl_); }
+    {
+        using std::swap;
+        swap(a.impl_, b.impl_);
+    }
 
     box(box&& other) { swap(*this, other); }
-    box(const box& other) : impl_(other.impl_) { impl_->inc(); }
-    box& operator=(box&& other) { swap(*this, other); return *this; }
+    box(const box& other)
+        : impl_(other.impl_)
+    {
+        impl_->inc();
+    }
+    box& operator=(box&& other)
+    {
+        swap(*this, other);
+        return *this;
+    }
     box& operator=(const box& other)
     {
         auto aux = other;
@@ -102,28 +122,16 @@ class box
     }
 
     /*! Query the current value. */
-    const T& get() const { return impl_->value; }
+    IMMER_NODISCARD const T& get() const { return impl_->value; }
 
     /*! Conversion to the boxed type. */
     operator const T&() const { return get(); }
 
     /*! Access via dereference */
-    const T& operator* () const { return get(); }
+    const T& operator*() const { return get(); }
 
     /*! Access via pointer member access */
-    const T* operator-> () const { return &get(); }
-
-    /*! Comparison. */
-    bool operator==(detail::exact_t<const box&> other) const
-    { return impl_ == other.value.impl_ || get() == other.value.get(); }
-    // Note that the `exact_t` disambiguates comparisons against `T{}`
-    // directly.  In that case we want to use `operator T&` and
-    // compare directly.  We definitely never want to convert a value
-    // to a box (which causes an allocation) just to compare it.
-    bool operator!=(detail::exact_t<const box&> other) const
-    { return !(*this == other.value); }
-    bool operator<(detail::exact_t<const box&> other) const
-    { return get() < other.value.get(); }
+    const T* operator->() const { return &get(); }
 
     /*!
      * Returns a new box built by applying the `fn` to the underlying
@@ -141,12 +149,12 @@ class box
      * @endrst
      */
     template <typename Fn>
-    box update(Fn&& fn) const&
+    IMMER_NODISCARD box update(Fn&& fn) const&
     {
         return std::forward<Fn>(fn)(get());
     }
     template <typename Fn>
-    box&& update(Fn&& fn) &&
+    IMMER_NODISCARD box&& update(Fn&& fn) &&
     {
         if (impl_->unique())
             impl_->value = std::forward<Fn>(fn)(std::move(impl_->value));
@@ -156,6 +164,66 @@ class box
     }
 };
 
+template <typename T, typename MP>
+IMMER_NODISCARD bool operator==(const box<T, MP>& a, const box<T, MP>& b)
+{
+    return a.impl() == b.impl() || a.get() == b.get();
+}
+template <typename T, typename MP>
+IMMER_NODISCARD bool operator!=(const box<T, MP>& a, const box<T, MP>& b)
+{
+    return a.impl() != b.impl() && a.get() != b.get();
+}
+template <typename T, typename MP>
+IMMER_NODISCARD bool operator<(const box<T, MP>& a, const box<T, MP>& b)
+{
+    return a.impl() != b.impl() && a.get() < b.get();
+}
+
+template <typename T, typename MP, typename T2>
+IMMER_NODISCARD auto operator==(const box<T, MP>& a, T2&& b)
+    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
+                        decltype(a.get() == b)>
+{
+    return a.get() == b;
+}
+template <typename T, typename MP, typename T2>
+IMMER_NODISCARD auto operator!=(const box<T, MP>& a, T2&& b)
+    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
+                        decltype(a.get() != b)>
+{
+    return a.get() != b;
+}
+template <typename T, typename MP, typename T2>
+IMMER_NODISCARD auto operator<(const box<T, MP>& a, T2&& b)
+    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
+                        decltype(a.get() < b)>
+{
+    return a.get() < b;
+}
+
+template <typename T2, typename T, typename MP>
+IMMER_NODISCARD auto operator==(T2&& b, const box<T, MP>& a)
+    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
+                        decltype(a.get() == b)>
+{
+    return a.get() == b;
+}
+template <typename T2, typename T, typename MP>
+IMMER_NODISCARD auto operator!=(T2&& b, const box<T, MP>& a)
+    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
+                        decltype(a.get() != b)>
+{
+    return a.get() != b;
+}
+template <typename T2, typename T, typename MP>
+IMMER_NODISCARD auto operator<(T2&& b, const box<T, MP>& a)
+    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
+                        decltype(b < a.get())>
+{
+    return b < a.get();
+}
+
 } // namespace immer
 
 namespace std {
@@ -163,7 +231,7 @@ namespace std {
 template <typename T, typename MP>
 struct hash<immer::box<T, MP>>
 {
-    std::size_t operator() (const immer::box<T, MP>& x) const
+    std::size_t operator()(const immer::box<T, MP>& x) const
     {
         return std::hash<T>{}(*x);
     }
diff --git a/src/immer/config.hpp b/src/immer/config.hpp
index 67697ef36b..2e8378f6d4 100644
--- a/src/immer/config.hpp
+++ b/src/immer/config.hpp
@@ -8,6 +8,60 @@
 
 #pragma once
 
+#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+#define IMMER_HAS_CPP17 1
+#endif
+
+#if defined(__has_cpp_attribute)
+#if __has_cpp_attribute(nodiscard)
+#define IMMER_NODISCARD [[nodiscard]]
+#endif
+#else
+#if _MSVC_LANG >= 201703L
+#define IMMER_NODISCARD [[nodiscard]]
+#endif
+#endif
+
+#ifdef __has_feature
+#if !__has_feature(cxx_exceptions)
+#define IMMER_NO_EXCEPTIONS
+#endif
+#endif
+
+#ifdef IMMER_NO_EXCEPTIONS
+#define IMMER_TRY if (true)
+#define IMMER_CATCH(expr) else
+#define IMMER_THROW(expr)                                                      \
+    do {                                                                       \
+        assert(!#expr);                                                        \
+        std::terminate();                                                      \
+    } while (false)
+#define IMMER_RETHROW
+#else
+#define IMMER_TRY try
+#define IMMER_CATCH(expr) catch (expr)
+#define IMMER_THROW(expr) throw expr
+#define IMMER_RETHROW throw
+#endif
+
+#ifndef IMMER_NODISCARD
+#define IMMER_NODISCARD
+#endif
+
+#ifndef IMMER_TAGGED_NODE
+#ifdef NDEBUG
+#define IMMER_TAGGED_NODE 0
+#else
+#define IMMER_TAGGED_NODE 1
+#endif
+#endif
+
+#if IMMER_TAGGED_NODE
+#define IMMER_ASSERT_TAGGED(assertion) assert(assertion)
+#else
+#define IMMER_ASSERT_TAGGED(assertion)
+#endif
+
 #ifndef IMMER_DEBUG_TRACES
 #define IMMER_DEBUG_TRACES 0
 #endif
@@ -16,6 +70,10 @@
 #define IMMER_DEBUG_PRINT 0
 #endif
 
+#ifndef IMMER_DEBUG_STATS
+#define IMMER_DEBUG_STATS 0
+#endif
+
 #ifndef IMMER_DEBUG_DEEP_CHECK
 #define IMMER_DEBUG_DEEP_CHECK 0
 #endif
@@ -25,42 +83,47 @@
 #include <prettyprint.hpp>
 #endif
 
+#if IMMER_DEBUG_STATS
+#include <iostream>
+#endif
+
 #if IMMER_DEBUG_TRACES
 #define IMMER_TRACE(...) std::cout << __VA_ARGS__ << std::endl
 #else
 #define IMMER_TRACE(...)
 #endif
-#define IMMER_TRACE_F(...)                                              \
+#define IMMER_TRACE_F(...)                                                     \
     IMMER_TRACE(__FILE__ << ":" << __LINE__ << ": " << __VA_ARGS__)
-#define IMMER_TRACE_E(expr)                             \
-    IMMER_TRACE("    " << #expr << " = " << (expr))
+#define IMMER_TRACE_E(expr) IMMER_TRACE("    " << #expr << " = " << (expr))
 
 #if defined(_MSC_VER)
-#define IMMER_UNREACHABLE    __assume(false)
-#define IMMER_LIKELY(cond)   cond
+#define IMMER_UNREACHABLE __assume(false)
+#define IMMER_LIKELY(cond) cond
 #define IMMER_UNLIKELY(cond) cond
-#define IMMER_FORCEINLINE    __forceinline
+#define IMMER_FORCEINLINE __forceinline
 #define IMMER_PREFETCH(p)
 #else
-#define IMMER_UNREACHABLE    __builtin_unreachable()
-#define IMMER_LIKELY(cond)   __builtin_expect(!!(cond), 1)
+#define IMMER_UNREACHABLE __builtin_unreachable()
+#define IMMER_LIKELY(cond) __builtin_expect(!!(cond), 1)
 #define IMMER_UNLIKELY(cond) __builtin_expect(!!(cond), 0)
-#define IMMER_FORCEINLINE    inline __attribute__ ((always_inline))
+#define IMMER_FORCEINLINE inline __attribute__((always_inline))
 #define IMMER_PREFETCH(p)
 // #define IMMER_PREFETCH(p)    __builtin_prefetch(p)
 #endif
 
 #define IMMER_DESCENT_DEEP 0
 
+#ifndef IMMER_ENABLE_DEBUG_SIZE_HEAP
 #ifdef NDEBUG
 #define IMMER_ENABLE_DEBUG_SIZE_HEAP 0
 #else
 #define IMMER_ENABLE_DEBUG_SIZE_HEAP 1
 #endif
+#endif
 
 namespace immer {
 
-const auto default_bits = 5;
+const auto default_bits           = 5;
 const auto default_free_list_size = 1 << 10;
 
 } // namespace immer
diff --git a/src/immer/detail/arrays/no_capacity.hpp b/src/immer/detail/arrays/no_capacity.hpp
index 33135d0cc3..a24f6da249 100644
--- a/src/immer/detail/arrays/no_capacity.hpp
+++ b/src/immer/detail/arrays/no_capacity.hpp
@@ -9,8 +9,13 @@
 #pragma once
 
 #include <immer/algorithm.hpp>
+#include <immer/config.hpp>
 #include <immer/detail/arrays/node.hpp>
 
+#include <cassert>
+#include <cstddef>
+#include <stdexcept>
+
 namespace immer {
 namespace detail {
 namespace arrays {
@@ -18,16 +23,16 @@ namespace arrays {
 template <typename T, typename MemoryPolicy>
 struct no_capacity
 {
-    using node_t      = node<T, MemoryPolicy>;
-    using edit_t      = typename MemoryPolicy::transience_t::edit;
-    using size_t      = std::size_t;
+    using node_t = node<T, MemoryPolicy>;
+    using edit_t = typename MemoryPolicy::transience_t::edit;
+    using size_t = std::size_t;
 
     node_t* ptr;
-    size_t  size;
+    size_t size;
 
     static const no_capacity& empty()
     {
-        static const no_capacity empty_ {
+        static const no_capacity empty_{
             node_t::make_n(0),
             0,
         };
@@ -35,7 +40,8 @@ struct no_capacity
     }
 
     no_capacity(node_t* p, size_t s)
-        : ptr{p}, size{s}
+        : ptr{p}
+        , size{s}
     {}
 
     no_capacity(const no_capacity& other)
@@ -70,10 +76,7 @@ struct no_capacity
         swap(x.size, y.size);
     }
 
-    ~no_capacity()
-    {
-        dec();
-    }
+    ~no_capacity() { dec(); }
 
     void inc()
     {
@@ -91,22 +94,33 @@ struct no_capacity
     T* data() { return ptr->data(); }
     const T* data() const { return ptr->data(); }
 
-    template <typename Iter, typename Sent,
-              std::enable_if_t
-              <is_forward_iterator_v<Iter> 
-	       && compatible_sentinel_v<Iter, Sent>, bool> = true>
+    T* data_mut(edit_t e)
+    {
+        if (!ptr->can_mutate(e))
+            ptr = node_t::copy_e(e, size, ptr, size);
+        return data();
+    }
+
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<is_forward_iterator_v<Iter> &&
+                                   compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
     static no_capacity from_range(Iter first, Sent last)
     {
         auto count = static_cast<size_t>(distance(first, last));
-        return {
-            node_t::copy_n(count, first, last),
-            count,
-        };
+        if (count == 0)
+            return empty();
+        else
+            return {
+                node_t::copy_n(count, first, last),
+                count,
+            };
     }
 
     static no_capacity from_fill(size_t n, T v)
     {
-        return { node_t::fill_n(n, v), n };
+        return {node_t::fill_n(n, v), n};
     }
 
     template <typename U>
@@ -128,46 +142,45 @@ struct no_capacity
         return std::forward<Fn>(fn)(data(), data() + size);
     }
 
-    const T& get(std::size_t index) const
-    {
-        return data()[index];
-    }
+    const T& get(std::size_t index) const { return data()[index]; }
 
     const T& get_check(std::size_t index) const
     {
         if (index >= size)
-            throw std::out_of_range{"out of range"};
+            IMMER_THROW(std::out_of_range{"out of range"});
         return data()[index];
     }
 
     bool equals(const no_capacity& other) const
     {
         return ptr == other.ptr ||
-            (size == other.size &&
-             std::equal(data(), data() + size, other.data()));
+               (size == other.size &&
+                std::equal(data(), data() + size, other.data()));
     }
 
     no_capacity push_back(T value) const
     {
         auto p = node_t::copy_n(size + 1, ptr, size);
-        try {
+        IMMER_TRY {
             new (p->data() + size) T{std::move(value)};
-            return { p, size + 1 };
-        } catch (...) {
+            return {p, size + 1};
+        }
+        IMMER_CATCH (...) {
             node_t::delete_n(p, size, size + 1);
-            throw;
+            IMMER_RETHROW;
         }
     }
 
     no_capacity assoc(std::size_t idx, T value) const
     {
         auto p = node_t::copy_n(size, ptr, size);
-        try {
+        IMMER_TRY {
             p->data()[idx] = std::move(value);
-            return { p, size };
-        } catch (...) {
+            return {p, size};
+        }
+        IMMER_CATCH (...) {
             node_t::delete_n(p, size, size);
-            throw;
+            IMMER_RETHROW;
         }
     }
 
@@ -175,20 +188,21 @@ struct no_capacity
     no_capacity update(std::size_t idx, Fn&& op) const
     {
         auto p = node_t::copy_n(size, ptr, size);
-        try {
+        IMMER_TRY {
             auto& elem = p->data()[idx];
-            elem = std::forward<Fn>(op)(std::move(elem));
-            return { p, size };
-        } catch (...) {
+            elem       = std::forward<Fn>(op)(std::move(elem));
+            return {p, size};
+        }
+        IMMER_CATCH (...) {
             node_t::delete_n(p, size, size);
-            throw;
+            IMMER_RETHROW;
         }
     }
 
     no_capacity take(std::size_t sz) const
     {
         auto p = node_t::copy_n(sz, ptr, sz);
-        return { p, sz };
+        return {p, sz};
     }
 };
 
diff --git a/src/immer/detail/arrays/node.hpp b/src/immer/detail/arrays/node.hpp
index 7883d167f2..ac521e0335 100644
--- a/src/immer/detail/arrays/node.hpp
+++ b/src/immer/detail/arrays/node.hpp
@@ -8,10 +8,12 @@
 
 #pragma once
 
-#include <immer/detail/util.hpp>
-#include <immer/detail/type_traits.hpp>
+#include <immer/config.hpp>
 #include <immer/detail/combine_standard_layout.hpp>
+#include <immer/detail/type_traits.hpp>
+#include <immer/detail/util.hpp>
 
+#include <cstddef>
 #include <limits>
 
 namespace immer {
@@ -21,54 +23,48 @@ namespace arrays {
 template <typename T, typename MemoryPolicy>
 struct node
 {
-    using memory      = MemoryPolicy;
-    using heap        = typename MemoryPolicy::heap::type;
-    using transience  = typename memory::transience_t;
-    using refs_t      = typename memory::refcount;
-    using ownee_t     = typename transience::ownee;
-    using node_t      = node;
-    using edit_t      = typename transience::edit;
+    using memory     = MemoryPolicy;
+    using heap       = typename MemoryPolicy::heap::type;
+    using transience = typename memory::transience_t;
+    using refs_t     = typename memory::refcount;
+    using ownee_t    = typename transience::ownee;
+    using node_t     = node;
+    using edit_t     = typename transience::edit;
 
     struct data_t
     {
         aligned_storage_for<T> buffer;
     };
 
-    using impl_t = combine_standard_layout_t<data_t,
-                                             refs_t,
-                                             ownee_t>;
+    using impl_t = combine_standard_layout_t<data_t, refs_t, ownee_t>;
 
     impl_t impl;
 
     constexpr static std::size_t sizeof_n(size_t count)
     {
-        return immer_offsetof(impl_t, d.buffer) + sizeof(T) * count;
+        return std::max(immer_offsetof(impl_t, d.buffer) + sizeof(T) * count,
+                        sizeof(node));
     }
 
-    refs_t& refs() const
-    {
-        return auto_const_cast(get<refs_t>(impl));
-    }
+    refs_t& refs() const { return auto_const_cast(get<refs_t>(impl)); }
 
     const ownee_t& ownee() const { return get<ownee_t>(impl); }
-    ownee_t& ownee()             { return get<ownee_t>(impl); }
+    ownee_t& ownee() { return get<ownee_t>(impl); }
 
     const T* data() const { return reinterpret_cast<const T*>(&impl.d.buffer); }
-    T* data()             { return reinterpret_cast<T*>(&impl.d.buffer); }
+    T* data() { return reinterpret_cast<T*>(&impl.d.buffer); }
 
     bool can_mutate(edit_t e) const
     {
-        return refs().unique()
-            || ownee().can_mutate(e);
+        return refs().unique() || ownee().can_mutate(e);
     }
 
     static void delete_n(node_t* p, size_t sz, size_t cap)
     {
-        destroy_n(p->data(), sz);
+        detail::destroy_n(p->data(), sz);
         heap::deallocate(sizeof_n(cap), p);
     }
 
-
     static node_t* make_n(size_t n)
     {
         return new (heap::allocate(sizeof_n(n))) node_t{};
@@ -76,7 +72,7 @@ struct node
 
     static node_t* make_e(edit_t e, size_t n)
     {
-        auto p = make_n(n);
+        auto p     = make_n(n);
         p->ownee() = e;
         return p;
     }
@@ -84,27 +80,30 @@ struct node
     static node_t* fill_n(size_t n, T v)
     {
         auto p = make_n(n);
-        try {
+        IMMER_TRY {
             std::uninitialized_fill_n(p->data(), n, v);
             return p;
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             heap::deallocate(sizeof_n(n), p);
-            throw;
+            IMMER_RETHROW;
         }
     }
 
-    template <typename Iter, typename Sent,
-            std::enable_if_t
-            <detail::compatible_sentinel_v<Iter,Sent>, bool> = true>
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
     static node_t* copy_n(size_t n, Iter first, Sent last)
     {
         auto p = make_n(n);
-        try {
-            uninitialized_copy(first, last, p->data());
+        IMMER_TRY {
+            detail::uninitialized_copy(first, last, p->data());
             return p;
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             heap::deallocate(sizeof_n(n), p);
-            throw;
+            IMMER_RETHROW;
         }
     }
 
@@ -116,7 +115,7 @@ struct node
     template <typename Iter>
     static node_t* copy_e(edit_t e, size_t n, Iter first, Iter last)
     {
-        auto p = copy_n(n, first, last);
+        auto p     = copy_n(n, first, last);
         p->ownee() = e;
         return p;
     }
diff --git a/src/immer/detail/arrays/with_capacity.hpp b/src/immer/detail/arrays/with_capacity.hpp
index d408b38aa3..c80f706821 100644
--- a/src/immer/detail/arrays/with_capacity.hpp
+++ b/src/immer/detail/arrays/with_capacity.hpp
@@ -8,8 +8,13 @@
 
 #pragma once
 
+#include <immer/config.hpp>
 #include <immer/detail/arrays/no_capacity.hpp>
 
+#include <cassert>
+#include <cstddef>
+#include <stdexcept>
+
 namespace immer {
 namespace detail {
 namespace arrays {
@@ -19,26 +24,24 @@ struct with_capacity
 {
     using no_capacity_t = no_capacity<T, MemoryPolicy>;
 
-    using node_t      = node<T, MemoryPolicy>;
-    using edit_t      = typename MemoryPolicy::transience_t::edit;
-    using size_t      = std::size_t;
+    using node_t = node<T, MemoryPolicy>;
+    using edit_t = typename MemoryPolicy::transience_t::edit;
+    using size_t = std::size_t;
 
     node_t* ptr;
-    size_t  size;
-    size_t  capacity;
+    size_t size;
+    size_t capacity;
 
     static const with_capacity& empty()
     {
-        static const with_capacity empty_ {
-            node_t::make_n(1),
-            0,
-            1
-        };
+        static const with_capacity empty_{node_t::make_n(1), 0, 1};
         return empty_;
     }
 
     with_capacity(node_t* p, size_t s, size_t c)
-        : ptr{p}, size{s}, capacity{c}
+        : ptr{p}
+        , size{s}
+        , capacity{c}
     {}
 
     with_capacity(const with_capacity& other)
@@ -80,10 +83,7 @@ struct with_capacity
         swap(x.capacity, y.capacity);
     }
 
-    ~with_capacity()
-    {
-        dec();
-    }
+    ~with_capacity() { dec(); }
 
     void inc()
     {
@@ -99,30 +99,40 @@ struct with_capacity
     }
 
     const T* data() const { return ptr->data(); }
-    T* data()             { return ptr->data(); }
+    T* data() { return ptr->data(); }
+
+    T* data_mut(edit_t e)
+    {
+        if (!ptr->can_mutate(e)) {
+            auto p = node_t::copy_e(e, capacity, ptr, size);
+            dec();
+            ptr = p;
+        }
+        return data();
+    }
 
     operator no_capacity_t() const
     {
         if (size == capacity) {
             ptr->refs().inc();
-            return { ptr, size };
+            return {ptr, size};
         } else {
-            return { node_t::copy_n(size, ptr, size), size };
+            return {node_t::copy_n(size, ptr, size), size};
         }
     }
 
-    template <typename Iter, typename Sent,
-              std::enable_if_t
-              <is_forward_iterator_v<Iter>
-               && compatible_sentinel_v<Iter, Sent>, bool> = true>
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<is_forward_iterator_v<Iter> &&
+                                   compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
     static with_capacity from_range(Iter first, Sent last)
     {
         auto count = static_cast<size_t>(distance(first, last));
-        return {
-            node_t::copy_n(count, first, last),
-            count,
-            count
-        };
+        if (count == 0)
+            return empty();
+        else
+            return {node_t::copy_n(count, first, last), count, count};
     }
 
     template <typename U>
@@ -134,7 +144,7 @@ struct with_capacity
 
     static with_capacity from_fill(size_t n, T v)
     {
-        return { node_t::fill_n(n, v), n, n };
+        return {node_t::fill_n(n, v), n, n};
     }
 
     template <typename Fn>
@@ -149,51 +159,50 @@ struct with_capacity
         return std::forward<Fn>(fn)(data(), data() + size);
     }
 
-    const T& get(std::size_t index) const
-    {
-        return data()[index];
-    }
+    const T& get(std::size_t index) const { return data()[index]; }
 
     const T& get_check(std::size_t index) const
     {
         if (index >= size)
-            throw std::out_of_range{"out of range"};
+            IMMER_THROW(std::out_of_range{"out of range"});
         return data()[index];
     }
 
     bool equals(const with_capacity& other) const
     {
         return ptr == other.ptr ||
-            (size == other.size &&
-             std::equal(data(), data() + size, other.data()));
+               (size == other.size &&
+                std::equal(data(), data() + size, other.data()));
     }
 
     static size_t recommend_up(size_t sz, size_t cap)
     {
         auto max = std::numeric_limits<size_t>::max();
-        return
-            sz <= cap       ? cap :
-            cap >= max / 2  ? max
-            /* otherwise */ : std::max(2 * cap, sz);
+        return sz <= cap        ? cap
+               : cap >= max / 2 ? max
+                                /* otherwise */
+                                : std::max(2 * cap, sz);
     }
 
     static size_t recommend_down(size_t sz, size_t cap)
     {
-        return sz == 0      ? 1 :
-            sz < cap / 2    ? sz * 2 :
-            /* otherwise */   cap;
+        return sz == 0        ? 1
+               : sz < cap / 2 ? sz * 2
+                              :
+                              /* otherwise */ cap;
     }
 
     with_capacity push_back(T value) const
     {
         auto cap = recommend_up(size + 1, capacity);
-        auto p = node_t::copy_n(cap, ptr, size);
-        try {
+        auto p   = node_t::copy_n(cap, ptr, size);
+        IMMER_TRY {
             new (p->data() + size) T{std::move(value)};
-            return { p, size + 1, cap };
-        } catch (...) {
+            return {p, size + 1, cap};
+        }
+        IMMER_CATCH (...) {
             node_t::delete_n(p, size, cap);
-            throw;
+            IMMER_RETHROW;
         }
     }
 
@@ -204,13 +213,14 @@ struct with_capacity
             ++size;
         } else {
             auto cap = recommend_up(size + 1, capacity);
-            auto p = node_t::copy_e(e, cap, ptr, size);
-            try {
+            auto p   = node_t::copy_e(e, cap, ptr, size);
+            IMMER_TRY {
                 new (p->data() + size) T{std::move(value)};
-                *this = { p, size + 1, cap };
-            } catch (...) {
+                *this = {p, size + 1, cap};
+            }
+            IMMER_CATCH (...) {
                 node_t::delete_n(p, size, cap);
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
@@ -218,12 +228,13 @@ struct with_capacity
     with_capacity assoc(std::size_t idx, T value) const
     {
         auto p = node_t::copy_n(capacity, ptr, size);
-        try {
+        IMMER_TRY {
             p->data()[idx] = std::move(value);
-            return { p, size, capacity };
-        } catch (...) {
+            return {p, size, capacity};
+        }
+        IMMER_CATCH (...) {
             node_t::delete_n(p, size, capacity);
-            throw;
+            IMMER_RETHROW;
         }
     }
 
@@ -233,12 +244,13 @@ struct with_capacity
             data()[idx] = std::move(value);
         } else {
             auto p = node_t::copy_n(capacity, ptr, size);
-            try {
+            IMMER_TRY {
                 p->data()[idx] = std::move(value);
-                *this = { p, size, capacity };
-            } catch (...) {
+                *this          = {p, size, capacity};
+            }
+            IMMER_CATCH (...) {
                 node_t::delete_n(p, size, capacity);
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
@@ -247,13 +259,14 @@ struct with_capacity
     with_capacity update(std::size_t idx, Fn&& op) const
     {
         auto p = node_t::copy_n(capacity, ptr, size);
-        try {
+        IMMER_TRY {
             auto& elem = p->data()[idx];
-            elem = std::forward<Fn>(op)(std::move(elem));
-            return { p, size, capacity };
-        } catch (...) {
+            elem       = std::forward<Fn>(op)(std::move(elem));
+            return {p, size, capacity};
+        }
+        IMMER_CATCH (...) {
             node_t::delete_n(p, size, capacity);
-            throw;
+            IMMER_RETHROW;
         }
     }
 
@@ -262,36 +275,39 @@ struct with_capacity
     {
         if (ptr->can_mutate(e)) {
             auto& elem = data()[idx];
-            elem = std::forward<Fn>(op)(std::move(elem));
+            elem       = std::forward<Fn>(op)(std::move(elem));
         } else {
             auto p = node_t::copy_e(e, capacity, ptr, size);
-            try {
+            IMMER_TRY {
                 auto& elem = p->data()[idx];
-                elem = std::forward<Fn>(op)(std::move(elem));
-                *this = { p, size, capacity };
-            } catch (...) {
+                elem       = std::forward<Fn>(op)(std::move(elem));
+                *this      = {p, size, capacity};
+            }
+            IMMER_CATCH (...) {
                 node_t::delete_n(p, size, capacity);
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
 
     with_capacity take(std::size_t sz) const
     {
+        assert(sz <= size);
         auto cap = recommend_down(sz, capacity);
-        auto p = node_t::copy_n(cap, ptr, sz);
-        return { p, sz, cap };
+        auto p   = node_t::copy_n(cap, ptr, sz);
+        return {p, sz, cap};
     }
 
     void take_mut(edit_t e, std::size_t sz)
     {
+        assert(sz <= size);
         if (ptr->can_mutate(e)) {
-            destroy_n(data() + size, size - sz);
+            detail::destroy_n(data() + size, size - sz);
             size = sz;
         } else {
             auto cap = recommend_down(sz, capacity);
-            auto p = node_t::copy_e(e, cap, ptr, sz);
-            *this = { p, sz, cap };
+            auto p   = node_t::copy_e(e, cap, ptr, sz);
+            *this    = {p, sz, cap};
         }
     }
 };
diff --git a/src/immer/detail/combine_standard_layout.hpp b/src/immer/detail/combine_standard_layout.hpp
index be8e698acc..68de2a8a8c 100644
--- a/src/immer/detail/combine_standard_layout.hpp
+++ b/src/immer/detail/combine_standard_layout.hpp
@@ -8,11 +8,12 @@
 
 #pragma once
 
+#include <cstddef>
 #include <type_traits>
 
-#if __GNUC__ == 7 || __GNUC_MINOR__ == 1
+#if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ == 1
 #define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 1
-#define immer_offsetof(st, m) ((std::size_t) &(((st*)0)->m))
+#define immer_offsetof(st, m) ((std::size_t) & (((st*) 0)->m))
 #else
 #define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 0
 #define immer_offsetof offsetof
@@ -48,7 +49,8 @@ using combine_standard_layout_t = typename combine_standard_layout<Ts...>::type;
 namespace csl {
 
 template <typename T>
-struct type_t {};
+struct type_t
+{};
 
 template <typename U, typename T>
 U& get(T& x);
@@ -56,17 +58,25 @@ U& get(T& x);
 template <typename U, typename T>
 const U& get(const T& x);
 
-template <typename T, typename Next=void>
+template <typename T, typename Next = void>
 struct inherit
 {
-    struct type : T, Next
+    struct type
+        : T
+        , Next
     {
         using Next::get_;
 
         template <typename U>
-        friend decltype(auto) get(type& x) { return x.get_(type_t<U>{}); }
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
         template <typename U>
-        friend decltype(auto) get(const type& x) { return x.get_(type_t<U>{}); }
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
 
         T& get_(type_t<T>) { return *this; }
         const T& get_(type_t<T>) const { return *this; }
@@ -79,16 +89,22 @@ struct inherit<T, void>
     struct type : T
     {
         template <typename U>
-        friend decltype(auto) get(type& x) { return x.get_(type_t<U>{}); }
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
         template <typename U>
-        friend decltype(auto) get(const type& x) { return x.get_(type_t<U>{}); }
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
 
         T& get_(type_t<T>) { return *this; }
         const T& get_(type_t<T>) const { return *this; }
     };
 };
 
-template <typename T, typename Next=void>
+template <typename T, typename Next = void>
 struct member
 {
     struct type : Next
@@ -98,9 +114,15 @@ struct member
         using Next::get_;
 
         template <typename U>
-        friend decltype(auto) get(type& x) { return x.get_(type_t<U>{}); }
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
         template <typename U>
-        friend decltype(auto) get(const type& x) { return x.get_(type_t<U>{}); }
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
 
         T& get_(type_t<T>) { return d; }
         const T& get_(type_t<T>) const { return d; }
@@ -115,9 +137,15 @@ struct member<T, void>
         T d;
 
         template <typename U>
-        friend decltype(auto) get(type& x) { return x.get_(type_t<U>{}); }
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
         template <typename U>
-        friend decltype(auto) get(const type& x) { return x.get_(type_t<U>{}); }
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
 
         T& get_(type_t<T>) { return d; }
         const T& get_(type_t<T>) const { return d; }
@@ -133,17 +161,29 @@ struct member_two
         T d;
 
         template <typename U>
-        friend decltype(auto) get(type& x) { return x.get_(type_t<U>{}); }
+        friend decltype(auto) get(type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
         template <typename U>
-        friend decltype(auto) get(const type& x) { return x.get_(type_t<U>{}); }
+        friend decltype(auto) get(const type& x)
+        {
+            return x.get_(type_t<U>{});
+        }
 
         T& get_(type_t<T>) { return d; }
         const T& get_(type_t<T>) const { return d; }
 
         template <typename U>
-        auto get_(type_t<U> t) -> decltype(auto) { return n.get_(t); }
+        auto get_(type_t<U> t) -> decltype(auto)
+        {
+            return n.get_(t);
+        }
         template <typename U>
-        auto get_(type_t<U> t) const -> decltype(auto) { return n.get_(t); }
+        auto get_(type_t<U> t) const -> decltype(auto)
+        {
+            return n.get_(t);
+        }
     };
 };
 
@@ -155,10 +195,9 @@ struct combine_standard_layout_aux<T>
 {
     static_assert(std::is_standard_layout<T>::value, "");
 
-    using type = typename std::conditional_t<
-        std::is_empty<T>::value,
-        csl::inherit<T>,
-        csl::member<T>>::type;
+    using type = typename std::conditional_t<std::is_empty<T>::value,
+                                             csl::inherit<T>,
+                                             csl::member<T>>::type;
 };
 
 template <typename T, typename... Ts>
@@ -173,10 +212,11 @@ struct combine_standard_layout_aux<T, Ts...>
     static constexpr auto empty_next = std::is_empty<next_t>::value;
 
     using type = typename std::conditional_t<
-        empty_this, inherit<this_t, next_t>,
-        std::conditional_t<
-            empty_next, member<this_t, next_t>,
-            member_two<this_t, next_t>>>::type;
+        empty_this,
+        inherit<this_t, next_t>,
+        std::conditional_t<empty_next,
+                           member<this_t, next_t>,
+                           member_two<this_t, next_t>>>::type;
 };
 
 } // namespace csl
diff --git a/src/immer/detail/hamts/bits.hpp b/src/immer/detail/hamts/bits.hpp
index 92c7e45cf2..0b4b923784 100644
--- a/src/immer/detail/hamts/bits.hpp
+++ b/src/immer/detail/hamts/bits.hpp
@@ -8,6 +8,7 @@
 
 #pragma once
 
+#include <cstddef>
 #include <cstdint>
 
 #if defined(_MSC_VER)
@@ -18,18 +19,17 @@ namespace immer {
 namespace detail {
 namespace hamts {
 
-using size_t   = std::size_t;
-using hash_t   = std::size_t;
-using bits_t   = std::uint32_t;
-using count_t  = std::uint32_t;
-using shift_t  = std::uint32_t;
+using size_t  = std::size_t;
+using hash_t  = std::size_t;
+using bits_t  = std::uint32_t;
+using count_t = std::uint32_t;
+using shift_t = std::uint32_t;
 
 template <bits_t B>
 struct get_bitmap_type
 {
     static_assert(B < 6u, "B > 6 is not supported.");
-
-    using type = std::uint32_t;
+    using type = std::uint8_t;
 };
 
 template <>
@@ -38,17 +38,29 @@ struct get_bitmap_type<6u>
     using type = std::uint64_t;
 };
 
-template <bits_t B, typename T=count_t>
+template <>
+struct get_bitmap_type<5u>
+{
+    using type = std::uint32_t;
+};
+
+template <>
+struct get_bitmap_type<4u>
+{
+    using type = std::uint16_t;
+};
+
+template <bits_t B, typename T = count_t>
 constexpr T branches = T{1u} << B;
 
-template <bits_t B, typename T=size_t>
+template <bits_t B, typename T = size_t>
 constexpr T mask = branches<B, T> - 1u;
 
-template <bits_t B, typename T=count_t>
+template <bits_t B, typename T = count_t>
 constexpr T max_depth = (sizeof(hash_t) * 8u + B - 1u) / B;
 
-template <bits_t B, typename T=count_t>
-constexpr T max_shift = max_depth<B, count_t> * B;
+template <bits_t B, typename T = count_t>
+constexpr T max_shift = max_depth<B, count_t>* B;
 
 #define IMMER_HAS_BUILTIN_POPCOUNT 1
 
@@ -67,17 +79,18 @@ inline auto popcount_fallback(std::uint64_t x)
 {
     x = x - ((x >> 1) & 0x5555555555555555u);
     x = (x & 0x3333333333333333u) + ((x >> 2u) & 0x3333333333333333u);
-    return (((x + (x >> 4)) & 0x0F0F0F0F0F0F0F0Fu) * 0x0101010101010101u) >> 56u;
+    return (((x + (x >> 4)) & 0x0F0F0F0F0F0F0F0Fu) * 0x0101010101010101u) >>
+           56u;
 }
 
 inline count_t popcount(std::uint32_t x)
 {
 #if IMMER_HAS_BUILTIN_POPCOUNT
-#  if defined(_MSC_VER)
-    return  __popcnt(x);
-#  else
+#if defined(_MSC_VER)
+    return __popcnt(x);
+#else
     return __builtin_popcount(x);
-#  endif
+#endif
 #else
     return popcount_fallback(x);
 #endif
@@ -86,16 +99,77 @@ inline count_t popcount(std::uint32_t x)
 inline count_t popcount(std::uint64_t x)
 {
 #if IMMER_HAS_BUILTIN_POPCOUNT
-#  if defined(_MSC_VER)
-    return  __popcnt64(x);
-#  else
+#if defined(_MSC_VER)
+#if defined(_WIN64)
+    return static_cast<count_t>(__popcnt64(x));
+#else
+    // TODO: benchmark against popcount_fallback(std::uint64_t x)
+    return popcount(static_cast<std::uint32_t>(x >> 32)) +
+           popcount(static_cast<std::uint32_t>(x));
+#endif
+#else
     return __builtin_popcountll(x);
-#  endif
+#endif
 #else
     return popcount_fallback(x);
 #endif
 }
 
+inline count_t popcount(std::uint16_t x)
+{
+    return popcount(static_cast<std::uint32_t>(x));
+}
+
+inline count_t popcount(std::uint8_t x)
+{
+    return popcount(static_cast<std::uint32_t>(x));
+}
+
+template <typename bitmap_t>
+class set_bits_range
+{
+    bitmap_t bitmap;
+
+    class set_bits_iterator
+    {
+        bitmap_t bitmap;
+
+        inline static bitmap_t clearlsbit(bitmap_t bitmap)
+        {
+            return bitmap & (bitmap - 1);
+        }
+
+        inline static bitmap_t lsbit(bitmap_t bitmap)
+        {
+            return bitmap ^ clearlsbit(bitmap);
+        }
+
+    public:
+        set_bits_iterator(bitmap_t bitmap)
+            : bitmap(bitmap){};
+
+        set_bits_iterator operator++()
+        {
+            bitmap = clearlsbit(bitmap);
+            return *this;
+        }
+
+        bool operator!=(set_bits_iterator const& other) const
+        {
+            return bitmap != other.bitmap;
+        }
+
+        bitmap_t operator*() const { return lsbit(bitmap); }
+    };
+
+public:
+    set_bits_range(bitmap_t bitmap)
+        : bitmap(bitmap)
+    {}
+    set_bits_iterator begin() const { return set_bits_iterator(bitmap); }
+    set_bits_iterator end() const { return set_bits_iterator(0); }
+};
+
 } // namespace hamts
 } // namespace detail
 } // namespace immer
diff --git a/src/immer/detail/hamts/champ.hpp b/src/immer/detail/hamts/champ.hpp
index d3687ffbee..64a8cb8248 100644
--- a/src/immer/detail/hamts/champ.hpp
+++ b/src/immer/detail/hamts/champ.hpp
@@ -17,6 +17,107 @@ namespace immer {
 namespace detail {
 namespace hamts {
 
+#if IMMER_DEBUG_STATS
+struct champ_debug_stats
+{
+    std::size_t bits       = {};
+    std::size_t value_size = {};
+    std::size_t child_size = sizeof(void*);
+
+    std::size_t inner_node_count         = {};
+    std::size_t inner_node_w_value_count = {};
+    std::size_t inner_node_w_child_count = {};
+    std::size_t collision_node_count     = {};
+
+    std::size_t child_count     = {};
+    std::size_t value_count     = {};
+    std::size_t collision_count = {};
+
+    friend champ_debug_stats operator+(champ_debug_stats a, champ_debug_stats b)
+    {
+        if (a.bits != b.bits || a.value_size != b.value_size ||
+            a.child_size != b.child_size)
+            throw std::runtime_error{"accumulating incompatible stats"};
+        return {
+            a.bits,
+            a.value_size,
+            a.child_size,
+            a.inner_node_count + b.inner_node_count,
+            a.inner_node_w_value_count + b.inner_node_w_value_count,
+            a.inner_node_w_child_count + b.inner_node_w_child_count,
+            a.collision_node_count + b.collision_node_count,
+            a.child_count + b.child_count,
+            a.value_count + b.value_count,
+            a.collision_count + b.collision_count,
+        };
+    }
+
+    struct summary
+    {
+        double collision_ratio;
+
+        double utilization;
+        double child_utilization;
+        double value_utilization;
+
+        double dense_utilization;
+        double dense_value_utilization;
+        double dense_child_utilization;
+
+        friend std::ostream& operator<<(std::ostream& os, const summary& s)
+        {
+            os << "---\n";
+            os << "collisions\n"
+               << "  ratio = " << s.collision_ratio << " %\n";
+            os << "utilization\n"
+               << "  total    = " << s.utilization << " %\n"
+               << "  children = " << s.child_utilization << " %\n"
+               << "  values   = " << s.value_utilization << " %\n";
+            os << "utilization (dense)\n"
+               << "  total    = " << s.dense_utilization << " %\n"
+               << "  children = " << s.dense_child_utilization << " %\n"
+               << "  values   = " << s.dense_value_utilization << " %\n";
+            return os;
+        }
+    };
+
+    summary get_summary() const
+    {
+        auto m = std::size_t{1} << bits;
+
+        auto collision_ratio = 100. * collision_count / value_count;
+
+        auto capacity          = m * inner_node_count;
+        auto child_utilization = 100. * child_count / capacity;
+        auto value_utilization = 100. * value_count / capacity;
+        auto utilization =
+            100. * (value_count * value_size + child_count * child_size) /
+            (capacity * value_size + capacity * child_size);
+
+        auto value_capacity = m * inner_node_w_value_count;
+        auto child_capacity = m * inner_node_w_child_count;
+        auto dense_child_utilization =
+            child_capacity == 0 ? 100. : 100. * child_count / child_capacity;
+        auto dense_value_utilization =
+            value_capacity == 0 ? 100. : 100. * value_count / value_capacity;
+        auto dense_utilization =
+            value_capacity + child_capacity == 0
+                ? 100.
+                : 100. * (value_count * value_size + child_count * child_size) /
+                      (value_capacity * value_size +
+                       child_capacity * child_size);
+
+        return {collision_ratio,
+                utilization,
+                child_utilization,
+                value_utilization,
+                dense_utilization,
+                dense_value_utilization,
+                dense_child_utilization};
+    }
+};
+#endif
+
 template <typename T,
           typename Hash,
           typename Equal,
@@ -26,27 +127,26 @@ struct champ
 {
     static constexpr auto bits = B;
 
-    using node_t = node<T, Hash, Equal, MemoryPolicy, B>;
+    using node_t   = node<T, Hash, Equal, MemoryPolicy, B>;
+    using edit_t   = typename MemoryPolicy::transience_t::edit;
+    using owner_t  = typename MemoryPolicy::transience_t::owner;
     using bitmap_t = typename get_bitmap_type<B>::type;
 
     static_assert(branches<B> <= sizeof(bitmap_t) * 8, "");
 
     node_t* root;
-    size_t  size;
+    size_t size;
 
-    static const champ& empty()
+    static node_t* empty()
     {
-        static const champ empty_ {
-            node_t::make_inner_n(0),
-            0,
-        };
-        return empty_;
+        static const auto node = node_t::make_inner_n(0);
+        return node->inc();
     }
 
-    champ(node_t* r, size_t sz)
-        : root{r}, size{sz}
-    {
-    }
+    champ(node_t* r, size_t sz = 0)
+        : root{r}
+        , size{sz}
+    {}
 
     champ(const champ& other)
         : champ{other.root, other.size}
@@ -80,20 +180,125 @@ struct champ
         swap(x.size, y.size);
     }
 
-    ~champ()
+    ~champ() { dec(); }
+
+    void inc() const { root->inc(); }
+
+    void dec() const
     {
-        dec();
+        if (root->dec())
+            node_t::delete_deep(root, 0);
     }
 
-    void inc() const
+    std::size_t do_check_champ(node_t* node,
+                               count_t depth,
+                               size_t path_hash,
+                               size_t hash_mask) const
     {
-        root->inc();
+        auto result = std::size_t{};
+        if (depth < max_depth<B>) {
+            auto nodemap = node->nodemap();
+            if (nodemap) {
+                auto fst = node->children();
+                for (auto idx = std::size_t{}; idx < branches<B>; ++idx) {
+                    if (nodemap & (1 << idx)) {
+                        auto child = *fst++;
+                        result +=
+                            do_check_champ(child,
+                                           depth + 1,
+                                           path_hash | (idx << (B * depth)),
+                                           (hash_mask << B) | mask<B>);
+                    }
+                }
+            }
+            auto datamap = node->datamap();
+            if (datamap) {
+                auto fst = node->values();
+                for (auto idx = std::size_t{}; idx < branches<B>; ++idx) {
+                    if (datamap & (1 << idx)) {
+                        auto hash  = Hash{}(*fst++);
+                        auto check = (hash & hash_mask) ==
+                                     (path_hash | (idx << (B * depth)));
+                        // assert(check);
+                        result += !!check;
+                    }
+                }
+            }
+        } else {
+            auto fst = node->collisions();
+            auto lst = fst + node->collision_count();
+            for (; fst != lst; ++fst) {
+                auto hash  = Hash{}(*fst);
+                auto check = hash == path_hash;
+                // assert(check);
+                result += !!check;
+            }
+        }
+        return result;
     }
 
-    void dec() const
+    // Checks that the hashes of the values correspond with what has actually
+    // been inserted.  If it doesn't it can mean that corruption has happened
+    // due some value being moved out of the champ when it should have not.
+    bool check_champ() const
     {
-        if (root->dec())
-            node_t::delete_deep(root, 0);
+        auto r = do_check_champ(root, 0, 0, mask<B>);
+        // assert(r == size);
+        return r == size;
+    }
+
+#if IMMER_DEBUG_STATS
+    void do_get_debug_stats(champ_debug_stats& stats,
+                            node_t* node,
+                            count_t depth) const
+    {
+        if (depth < max_depth<B>) {
+            ++stats.inner_node_count;
+            stats.inner_node_w_value_count += node->data_count() > 0;
+            stats.inner_node_w_child_count += node->children_count() > 0;
+            stats.value_count += node->data_count();
+            stats.child_count += node->children_count();
+            auto nodemap = node->nodemap();
+            if (nodemap) {
+                auto fst = node->children();
+                auto lst = fst + node->children_count();
+                for (; fst != lst; ++fst)
+                    do_get_debug_stats(stats, *fst, depth + 1);
+            }
+        } else {
+            ++stats.collision_node_count;
+            stats.collision_count += node->collision_count();
+        }
+    }
+
+    champ_debug_stats get_debug_stats() const
+    {
+        auto stats = champ_debug_stats{B, sizeof(T)};
+        do_get_debug_stats(stats, root, 0);
+        return stats;
+    }
+#endif
+
+    template <typename U>
+    static auto from_initializer_list(std::initializer_list<U> values)
+    {
+        auto e      = owner_t{};
+        auto result = champ{empty()};
+        for (auto&& v : values)
+            result.add_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 = champ{empty()};
+        for (; first != last; ++first)
+            result.add_mut(e, *first);
+        return result;
     }
 
     template <typename Fn>
@@ -103,21 +308,221 @@ struct champ
     }
 
     template <typename Fn>
-    void for_each_chunk_traversal(node_t* node, count_t depth, Fn&& fn) const
+    void
+    for_each_chunk_traversal(const node_t* node, count_t depth, Fn&& fn) const
     {
         if (depth < max_depth<B>) {
             auto datamap = node->datamap();
             if (datamap)
-                fn(node->values(), node->values() + popcount(datamap));
+                fn(node->values(), node->values() + node->data_count());
             auto nodemap = node->nodemap();
             if (nodemap) {
                 auto fst = node->children();
-                auto lst = fst + popcount(nodemap);
+                auto lst = fst + node->children_count();
                 for (; fst != lst; ++fst)
                     for_each_chunk_traversal(*fst, depth + 1, fn);
             }
         } else {
-            fn(node->collisions(), node->collisions() + node->collision_count());
+            fn(node->collisions(),
+               node->collisions() + node->collision_count());
+        }
+    }
+
+    template <typename EqualValue, typename Differ>
+    void diff(const champ& new_champ, Differ&& differ) const
+    {
+        diff<EqualValue>(root, new_champ.root, 0, std::forward<Differ>(differ));
+    }
+
+    template <typename EqualValue, typename Differ>
+    void diff(const node_t* old_node,
+              const node_t* new_node,
+              count_t depth,
+              Differ&& differ) const
+    {
+        if (old_node == new_node)
+            return;
+        if (depth < max_depth<B>) {
+            auto old_nodemap = old_node->nodemap();
+            auto new_nodemap = new_node->nodemap();
+            auto old_datamap = old_node->datamap();
+            auto new_datamap = new_node->datamap();
+            auto old_bits    = old_nodemap | old_datamap;
+            auto new_bits    = new_nodemap | new_datamap;
+            auto changes     = old_bits ^ new_bits;
+
+            // added bits
+            for (auto bit : set_bits_range<bitmap_t>(new_bits & changes)) {
+                if (new_nodemap & bit) {
+                    auto offset = new_node->children_count(bit);
+                    auto child  = new_node->children()[offset];
+                    for_each_chunk_traversal(
+                        child,
+                        depth + 1,
+                        [&](auto const& begin, auto const& end) {
+                            for (auto it = begin; it != end; it++)
+                                differ.added(*it);
+                        });
+                } else if (new_datamap & bit) {
+                    auto offset       = new_node->data_count(bit);
+                    auto const& value = new_node->values()[offset];
+                    differ.added(value);
+                }
+            }
+
+            // removed bits
+            for (auto bit : set_bits_range<bitmap_t>(old_bits & changes)) {
+                if (old_nodemap & bit) {
+                    auto offset = old_node->children_count(bit);
+                    auto child  = old_node->children()[offset];
+                    for_each_chunk_traversal(
+                        child,
+                        depth + 1,
+                        [&](auto const& begin, auto const& end) {
+                            for (auto it = begin; it != end; it++)
+                                differ.removed(*it);
+                        });
+                } else if (old_datamap & bit) {
+                    auto offset       = old_node->data_count(bit);
+                    auto const& value = old_node->values()[offset];
+                    differ.removed(value);
+                }
+            }
+
+            // bits in both nodes
+            for (auto bit : set_bits_range<bitmap_t>(old_bits & new_bits)) {
+                if ((old_nodemap & bit) && (new_nodemap & bit)) {
+                    auto old_offset = old_node->children_count(bit);
+                    auto new_offset = new_node->children_count(bit);
+                    auto old_child  = old_node->children()[old_offset];
+                    auto new_child  = new_node->children()[new_offset];
+                    diff<EqualValue>(old_child, new_child, depth + 1, differ);
+                } else if ((old_datamap & bit) && (new_nodemap & bit)) {
+                    diff_data_node<EqualValue>(
+                        old_node, new_node, bit, depth, differ);
+                } else if ((old_nodemap & bit) && (new_datamap & bit)) {
+                    diff_node_data<EqualValue>(
+                        old_node, new_node, bit, depth, differ);
+                } else if ((old_datamap & bit) && (new_datamap & bit)) {
+                    diff_data_data<EqualValue>(old_node, new_node, bit, differ);
+                }
+            }
+        } else {
+            diff_collisions<EqualValue>(old_node, new_node, differ);
+        }
+    }
+
+    template <typename EqualValue, typename Differ>
+    void diff_data_node(const node_t* old_node,
+                        const node_t* new_node,
+                        bitmap_t bit,
+                        count_t depth,
+                        Differ&& differ) const
+    {
+        auto old_offset       = old_node->data_count(bit);
+        auto const& old_value = old_node->values()[old_offset];
+        auto new_offset       = new_node->children_count(bit);
+        auto new_child        = new_node->children()[new_offset];
+
+        bool found = false;
+        for_each_chunk_traversal(
+            new_child, depth + 1, [&](auto const& begin, auto const& end) {
+                for (auto it = begin; it != end; it++) {
+                    if (Equal{}(old_value, *it)) {
+                        if (!EqualValue{}(old_value, *it))
+                            differ.changed(old_value, *it);
+                        found = true;
+                    } else {
+                        differ.added(*it);
+                    }
+                }
+            });
+        if (!found)
+            differ.removed(old_value);
+    }
+
+    template <typename EqualValue, typename Differ>
+    void diff_node_data(const node_t* old_node,
+                        const node_t* const new_node,
+                        bitmap_t bit,
+                        count_t depth,
+                        Differ&& differ) const
+    {
+        auto old_offset       = old_node->children_count(bit);
+        auto old_child        = old_node->children()[old_offset];
+        auto new_offset       = new_node->data_count(bit);
+        auto const& new_value = new_node->values()[new_offset];
+
+        bool found = false;
+        for_each_chunk_traversal(
+            old_child, depth + 1, [&](auto const& begin, auto const& end) {
+                for (auto it = begin; it != end; it++) {
+                    if (Equal{}(*it, new_value)) {
+                        if (!EqualValue{}(*it, new_value))
+                            differ.changed(*it, new_value);
+                        found = true;
+                    } else {
+                        differ.removed(*it);
+                    }
+                }
+            });
+        if (!found)
+            differ.added(new_value);
+    }
+
+    template <typename EqualValue, typename Differ>
+    void diff_data_data(const node_t* old_node,
+                        const node_t* new_node,
+                        bitmap_t bit,
+                        Differ&& differ) const
+    {
+        auto old_offset       = old_node->data_count(bit);
+        auto new_offset       = new_node->data_count(bit);
+        auto const& old_value = old_node->values()[old_offset];
+        auto const& new_value = new_node->values()[new_offset];
+        if (!Equal{}(old_value, new_value)) {
+            differ.removed(old_value);
+            differ.added(new_value);
+        } else {
+            if (!EqualValue{}(old_value, new_value))
+                differ.changed(old_value, new_value);
+        }
+    }
+
+    template <typename EqualValue, typename Differ>
+    void diff_collisions(const node_t* old_node,
+                         const node_t* new_node,
+                         Differ&& differ) const
+    {
+        auto old_begin = old_node->collisions();
+        auto old_end   = old_node->collisions() + old_node->collision_count();
+        auto new_begin = new_node->collisions();
+        auto new_end   = new_node->collisions() + new_node->collision_count();
+        // search changes and removals
+        for (auto old_it = old_begin; old_it != old_end; old_it++) {
+            bool found = false;
+            for (auto new_it = new_begin; new_it != new_end; new_it++) {
+                if (Equal{}(*old_it, *new_it)) {
+                    if (!EqualValue{}(*old_it, *new_it))
+                        differ.changed(*old_it, *new_it);
+                    found = true;
+                    break;
+                }
+            }
+            if (!found)
+                differ.removed(*old_it);
+        }
+        // search new entries
+        for (auto new_it = new_begin; new_it != new_end; new_it++) {
+            bool found = false;
+            for (auto old_it = old_begin; old_it != old_end; old_it++) {
+                if (Equal{}(*old_it, *new_it)) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found)
+                differ.added(*new_it);
         }
     }
 
@@ -129,11 +534,11 @@ struct champ
         for (auto i = count_t{}; i < max_depth<B>; ++i) {
             auto bit = bitmap_t{1u} << (hash & mask<B>);
             if (node->nodemap() & bit) {
-                auto offset = popcount(node->nodemap() & (bit - 1));
-                node = node->children() [offset];
-                hash = hash >> B;
+                auto offset = node->children_count(bit);
+                node        = node->children()[offset];
+                hash        = hash >> B;
             } else if (node->datamap() & bit) {
-                auto offset = popcount(node->datamap() & (bit - 1));
+                auto offset = node->data_count(bit);
                 auto val    = node->values() + offset;
                 if (Equal{}(*val, k))
                     return Project{}(*val);
@@ -151,9 +556,15 @@ struct champ
         return Default{}();
     }
 
-    std::pair<node_t*, bool>
-    do_add(node_t* node, T v, hash_t hash, shift_t shift) const
+    struct add_result
+    {
+        node_t* node;
+        bool added;
+    };
+
+    add_result do_add(node_t* node, T v, hash_t hash, shift_t shift) const
     {
+        assert(node);
         if (shift == max_shift<B>) {
             auto fst = node->collisions();
             auto lst = fst + node->collision_count();
@@ -161,159 +572,591 @@ struct champ
                 if (Equal{}(*fst, v))
                     return {
                         node_t::copy_collision_replace(node, fst, std::move(v)),
-                        false
-                    };
-            return {
-                node_t::copy_collision_insert(node, std::move(v)),
-                true
-            };
+                        false};
+            return {node_t::copy_collision_insert(node, std::move(v)), true};
         } else {
             auto idx = (hash & (mask<B> << shift)) >> shift;
             auto bit = bitmap_t{1u} << idx;
             if (node->nodemap() & bit) {
-                auto offset = popcount(node->nodemap() & (bit - 1));
-                auto result = do_add(node->children() [offset],
-                                     std::move(v), hash,
-                                     shift + B);
-                try {
-                    result.first = node_t::copy_inner_replace(
-                        node, offset, result.first);
+                auto offset = node->children_count(bit);
+                assert(node->children()[offset]);
+                auto result = do_add(
+                    node->children()[offset], std::move(v), hash, shift + B);
+                IMMER_TRY {
+                    result.node =
+                        node_t::copy_inner_replace(node, offset, result.node);
                     return result;
-                } catch (...) {
-                    node_t::delete_deep_shift(result.first, shift + B);
-                    throw;
+                }
+                IMMER_CATCH (...) {
+                    node_t::delete_deep_shift(result.node, shift + B);
+                    IMMER_RETHROW;
                 }
             } else if (node->datamap() & bit) {
-                auto offset = popcount(node->datamap() & (bit - 1));
+                auto offset = node->data_count(bit);
                 auto val    = node->values() + offset;
                 if (Equal{}(*val, v))
-                    return {
-                        node_t::copy_inner_replace_value(
-                            node, offset, std::move(v)),
-                        false
-                    };
+                    return {node_t::copy_inner_replace_value(
+                                node, offset, std::move(v)),
+                            false};
                 else {
-                    auto child = node_t::make_merged(shift + B,
-                                                    std::move(v), hash,
-                                                    *val, Hash{}(*val));
-                    try {
-                        return {
-                            node_t::copy_inner_replace_merged(
-                                node, bit, offset, child),
-                            true
-                        };
-                    } catch (...) {
+                    auto child = node_t::make_merged(
+                        shift + B, std::move(v), hash, *val, Hash{}(*val));
+                    IMMER_TRY {
+                        return {node_t::copy_inner_replace_merged(
+                                    node, bit, offset, child),
+                                true};
+                    }
+                    IMMER_CATCH (...) {
                         node_t::delete_deep_shift(child, shift + B);
-                        throw;
+                        IMMER_RETHROW;
                     }
                 }
             } else {
                 return {
                     node_t::copy_inner_insert_value(node, bit, std::move(v)),
-                    true
-                };
+                    true};
             }
         }
     }
 
     champ add(T v) const
+    {
+        auto hash     = Hash{}(v);
+        auto res      = do_add(root, std::move(v), hash, 0);
+        auto new_size = size + (res.added ? 1 : 0);
+        return {res.node, new_size};
+    }
+
+    struct add_mut_result
+    {
+        node_t* node;
+        bool added;
+        bool mutated;
+    };
+
+    add_mut_result
+    do_add_mut(edit_t e, node_t* node, T v, hash_t hash, shift_t shift) const
+    {
+        assert(node);
+        if (shift == max_shift<B>) {
+            auto fst = node->collisions();
+            auto lst = fst + node->collision_count();
+            for (; fst != lst; ++fst)
+                if (Equal{}(*fst, v)) {
+                    if (node->can_mutate(e)) {
+                        *fst = std::move(v);
+                        return {node, false, true};
+                    } else {
+                        auto r = node_t::copy_collision_replace(
+                            node, fst, std::move(v));
+                        return {node_t::owned(r, e), false, false};
+                    }
+                }
+            auto mutate = node->can_mutate(e);
+            auto r = mutate ? node_t::move_collision_insert(node, std::move(v))
+                            : node_t::copy_collision_insert(node, std::move(v));
+            return {node_t::owned(r, e), true, mutate};
+        } else {
+            auto idx = (hash & (mask<B> << shift)) >> shift;
+            auto bit = bitmap_t{1u} << idx;
+            if (node->nodemap() & bit) {
+                auto offset = node->children_count(bit);
+                auto child  = node->children()[offset];
+                if (node->can_mutate(e)) {
+                    auto result =
+                        do_add_mut(e, child, std::move(v), hash, shift + B);
+                    node->children()[offset] = result.node;
+                    if (!result.mutated && child->dec())
+                        node_t::delete_deep_shift(child, shift + B);
+                    return {node, result.added, true};
+                } else {
+                    assert(node->children()[offset]);
+                    auto result = do_add(child, std::move(v), hash, shift + B);
+                    IMMER_TRY {
+                        result.node = node_t::copy_inner_replace(
+                            node, offset, result.node);
+                        node_t::owned(result.node, e);
+                        return {result.node, result.added, false};
+                    }
+                    IMMER_CATCH (...) {
+                        node_t::delete_deep_shift(result.node, shift + B);
+                        IMMER_RETHROW;
+                    }
+                }
+            } else if (node->datamap() & bit) {
+                auto offset = node->data_count(bit);
+                auto val    = node->values() + offset;
+                if (Equal{}(*val, v)) {
+                    if (node->can_mutate(e)) {
+                        auto vals    = node->ensure_mutable_values(e);
+                        vals[offset] = std::move(v);
+                        return {node, false, true};
+                    } else {
+                        auto r = node_t::copy_inner_replace_value(
+                            node, offset, std::move(v));
+                        return {node_t::owned_values(r, e), false, false};
+                    }
+                } else {
+                    auto mutate        = node->can_mutate(e);
+                    auto mutate_values = mutate && node->can_mutate_values(e);
+                    auto hash2         = Hash{}(*val);
+                    auto child         = node_t::make_merged_e(
+                        e,
+                        shift + B,
+                        std::move(v),
+                        hash,
+                        mutate_values ? std::move(*val) : *val,
+                        hash2);
+                    IMMER_TRY {
+                        auto r = mutate ? node_t::move_inner_replace_merged(
+                                              e, node, bit, offset, child)
+                                        : node_t::copy_inner_replace_merged(
+                                              node, bit, offset, child);
+                        return {node_t::owned_values_safe(r, e), true, mutate};
+                    }
+                    IMMER_CATCH (...) {
+                        node_t::delete_deep_shift(child, shift + B);
+                        IMMER_RETHROW;
+                    }
+                }
+            } else {
+                auto mutate = node->can_mutate(e);
+                auto r      = mutate ? node_t::move_inner_insert_value(
+                                      e, node, bit, std::move(v))
+                                     : node_t::copy_inner_insert_value(
+                                      node, bit, std::move(v));
+                return {node_t::owned_values(r, e), true, mutate};
+            }
+        }
+    }
+
+    void add_mut(edit_t e, T v)
     {
         auto hash = Hash{}(v);
-        auto res = do_add(root, std::move(v), hash, 0);
-        auto new_size = size + (res.second ? 1 : 0);
-        return { res.first, new_size };
+        auto res  = do_add_mut(e, root, std::move(v), hash, 0);
+        if (!res.mutated && root->dec())
+            node_t::delete_deep(root, 0);
+        root = res.node;
+        size += res.added ? 1 : 0;
     }
 
-    template <typename Project, typename Default, typename Combine,
-              typename K, typename Fn>
-    std::pair<node_t*, bool>
-    do_update(node_t* node, K&& k, Fn&& fn,
-              hash_t hash, shift_t shift) const
+    using update_result = add_result;
+
+    template <typename Project,
+              typename Default,
+              typename Combine,
+              typename K,
+              typename Fn>
+    update_result
+    do_update(node_t* node, K&& k, Fn&& fn, hash_t hash, shift_t shift) const
     {
         if (shift == max_shift<B>) {
             auto fst = node->collisions();
             auto lst = fst + node->collision_count();
             for (; fst != lst; ++fst)
                 if (Equal{}(*fst, k))
-                    return {
-                        node_t::copy_collision_replace(
-                            node, fst, Combine{}(std::forward<K>(k),
-                                                 std::forward<Fn>(fn)(
-                                                     Project{}(*fst)))),
-                        false
-                    };
-            return {
-                node_t::copy_collision_insert(
-                    node, Combine{}(std::forward<K>(k),
-                                    std::forward<Fn>(fn)(
-                                        Default{}()))),
-                true
-            };
+                    return {node_t::copy_collision_replace(
+                                node,
+                                fst,
+                                Combine{}(std::forward<K>(k),
+                                          std::forward<Fn>(fn)(Project{}(
+                                              detail::as_const(*fst))))),
+                            false};
+            return {node_t::copy_collision_insert(
+                        node,
+                        Combine{}(std::forward<K>(k),
+                                  std::forward<Fn>(fn)(Default{}()))),
+                    true};
         } else {
             auto idx = (hash & (mask<B> << shift)) >> shift;
             auto bit = bitmap_t{1u} << idx;
             if (node->nodemap() & bit) {
-                auto offset = popcount(node->nodemap() & (bit - 1));
+                auto offset = node->children_count(bit);
                 auto result = do_update<Project, Default, Combine>(
-                    node->children() [offset], k, std::forward<Fn>(fn),
-                    hash, shift + B);
-                try {
-                    result.first = node_t::copy_inner_replace(
-                        node, offset, result.first);
+                    node->children()[offset],
+                    k,
+                    std::forward<Fn>(fn),
+                    hash,
+                    shift + B);
+                IMMER_TRY {
+                    result.node =
+                        node_t::copy_inner_replace(node, offset, result.node);
                     return result;
-                } catch (...) {
-                    node_t::delete_deep_shift(result.first, shift + B);
-                    throw;
+                }
+                IMMER_CATCH (...) {
+                    node_t::delete_deep_shift(result.node, shift + B);
+                    IMMER_RETHROW;
                 }
             } else if (node->datamap() & bit) {
-                auto offset = popcount(node->datamap() & (bit - 1));
+                auto offset = node->data_count(bit);
                 auto val    = node->values() + offset;
                 if (Equal{}(*val, k))
-                    return {
-                        node_t::copy_inner_replace_value(
-                            node, offset, Combine{}(std::forward<K>(k),
-                                                    std::forward<Fn>(fn)(
-                                                        Project{}(*val)))),
-                        false
-                    };
+                    return {node_t::copy_inner_replace_value(
+                                node,
+                                offset,
+                                Combine{}(std::forward<K>(k),
+                                          std::forward<Fn>(fn)(Project{}(
+                                              detail::as_const(*val))))),
+                            false};
                 else {
                     auto child = node_t::make_merged(
-                        shift + B, Combine{}(std::forward<K>(k),
-                                             std::forward<Fn>(fn)(
-                                                 Default{}())),
-                        hash, *val, Hash{}(*val));
-                    try {
-                        return {
-                            node_t::copy_inner_replace_merged(
-                                node, bit, offset, child),
-                            true
-                        };
-                    } catch (...) {
+                        shift + B,
+                        Combine{}(std::forward<K>(k),
+                                  std::forward<Fn>(fn)(Default{}())),
+                        hash,
+                        *val,
+                        Hash{}(*val));
+                    IMMER_TRY {
+                        return {node_t::copy_inner_replace_merged(
+                                    node, bit, offset, child),
+                                true};
+                    }
+                    IMMER_CATCH (...) {
                         node_t::delete_deep_shift(child, shift + B);
-                        throw;
+                        IMMER_RETHROW;
                     }
                 }
             } else {
-                return {
-                    node_t::copy_inner_insert_value(
-                        node, bit, Combine{}(std::forward<K>(k),
-                                             std::forward<Fn>(fn)(
-                                                 Default{}()))),
-                    true
-                };
+                return {node_t::copy_inner_insert_value(
+                            node,
+                            bit,
+                            Combine{}(std::forward<K>(k),
+                                      std::forward<Fn>(fn)(Default{}()))),
+                        true};
             }
         }
     }
 
-    template <typename Project, typename Default, typename Combine,
-              typename K, typename Fn>
+    template <typename Project,
+              typename Default,
+              typename Combine,
+              typename K,
+              typename Fn>
     champ update(const K& k, Fn&& fn) const
     {
         auto hash = Hash{}(k);
-        auto res = do_update<Project, Default, Combine>(
+        auto res  = do_update<Project, Default, Combine>(
+            root, k, std::forward<Fn>(fn), hash, 0);
+        auto new_size = size + (res.added ? 1 : 0);
+        return {res.node, new_size};
+    }
+
+    template <typename Project, typename Combine, typename K, typename Fn>
+    node_t* do_update_if_exists(
+        node_t* node, K&& k, Fn&& fn, hash_t hash, shift_t shift) const
+    {
+        if (shift == max_shift<B>) {
+            auto fst = node->collisions();
+            auto lst = fst + node->collision_count();
+            for (; fst != lst; ++fst)
+                if (Equal{}(*fst, k))
+                    return node_t::copy_collision_replace(
+                        node,
+                        fst,
+                        Combine{}(std::forward<K>(k),
+                                  std::forward<Fn>(fn)(
+                                      Project{}(detail::as_const(*fst)))));
+            return nullptr;
+        } else {
+            auto idx = (hash & (mask<B> << shift)) >> shift;
+            auto bit = bitmap_t{1u} << idx;
+            if (node->nodemap() & bit) {
+                auto offset = node->children_count(bit);
+                auto result = do_update_if_exists<Project, Combine>(
+                    node->children()[offset],
+                    k,
+                    std::forward<Fn>(fn),
+                    hash,
+                    shift + B);
+                IMMER_TRY {
+                    return result ? node_t::copy_inner_replace(
+                                        node, offset, result)
+                                  : nullptr;
+                }
+                IMMER_CATCH (...) {
+                    node_t::delete_deep_shift(result, shift + B);
+                    IMMER_RETHROW;
+                }
+            } else if (node->datamap() & bit) {
+                auto offset = node->data_count(bit);
+                auto val    = node->values() + offset;
+                if (Equal{}(*val, k))
+                    return node_t::copy_inner_replace_value(
+                        node,
+                        offset,
+                        Combine{}(std::forward<K>(k),
+                                  std::forward<Fn>(fn)(
+                                      Project{}(detail::as_const(*val)))));
+                else {
+                    return nullptr;
+                }
+            } else {
+                return nullptr;
+            }
+        }
+    }
+
+    template <typename Project, typename Combine, typename K, typename Fn>
+    champ update_if_exists(const K& k, Fn&& fn) const
+    {
+        auto hash = Hash{}(k);
+        auto res  = do_update_if_exists<Project, Combine>(
             root, k, std::forward<Fn>(fn), hash, 0);
-        auto new_size = size + (res.second ? 1 : 0);
-        return { res.first, new_size };
+        if (res) {
+            return {res, size};
+        } else {
+            return {root->inc(), size};
+        };
+    }
+
+    using update_mut_result = add_mut_result;
+
+    template <typename Project,
+              typename Default,
+              typename Combine,
+              typename K,
+              typename Fn>
+    update_mut_result do_update_mut(edit_t e,
+                                    node_t* node,
+                                    K&& k,
+                                    Fn&& fn,
+                                    hash_t hash,
+                                    shift_t shift) const
+    {
+        if (shift == max_shift<B>) {
+            auto fst = node->collisions();
+            auto lst = fst + node->collision_count();
+            for (; fst != lst; ++fst)
+                if (Equal{}(*fst, k)) {
+                    if (node->can_mutate(e)) {
+                        *fst = Combine{}(
+                            std::forward<K>(k),
+                            std::forward<Fn>(fn)(Project{}(std::move(*fst))));
+                        return {node, false, true};
+                    } else {
+                        auto r = node_t::copy_collision_replace(
+                            node,
+                            fst,
+                            Combine{}(std::forward<K>(k),
+                                      std::forward<Fn>(fn)(
+                                          Project{}(detail::as_const(*fst)))));
+                        return {node_t::owned(r, e), false, false};
+                    }
+                }
+            auto v      = Combine{}(std::forward<K>(k),
+                               std::forward<Fn>(fn)(Default{}()));
+            auto mutate = node->can_mutate(e);
+            auto r = mutate ? node_t::move_collision_insert(node, std::move(v))
+                            : node_t::copy_collision_insert(node, std::move(v));
+            return {node_t::owned(r, e), true, mutate};
+        } else {
+            auto idx = (hash & (mask<B> << shift)) >> shift;
+            auto bit = bitmap_t{1u} << idx;
+            if (node->nodemap() & bit) {
+                auto offset = node->children_count(bit);
+                auto child  = node->children()[offset];
+                if (node->can_mutate(e)) {
+                    auto result = do_update_mut<Project, Default, Combine>(
+                        e, child, k, std::forward<Fn>(fn), hash, shift + B);
+                    node->children()[offset] = result.node;
+                    if (!result.mutated && child->dec())
+                        node_t::delete_deep_shift(child, shift + B);
+                    return {node, result.added, true};
+                } else {
+                    auto result = do_update<Project, Default, Combine>(
+                        child, k, std::forward<Fn>(fn), hash, shift + B);
+                    IMMER_TRY {
+                        result.node = node_t::copy_inner_replace(
+                            node, offset, result.node);
+                        node_t::owned(result.node, e);
+                        return {result.node, result.added, false};
+                    }
+                    IMMER_CATCH (...) {
+                        node_t::delete_deep_shift(result.node, shift + B);
+                        IMMER_RETHROW;
+                    }
+                }
+            } else if (node->datamap() & bit) {
+                auto offset = node->data_count(bit);
+                auto val    = node->values() + offset;
+                if (Equal{}(*val, k)) {
+                    if (node->can_mutate(e)) {
+                        auto vals    = node->ensure_mutable_values(e);
+                        vals[offset] = Combine{}(std::forward<K>(k),
+                                                 std::forward<Fn>(fn)(Project{}(
+                                                     std::move(vals[offset]))));
+                        return {node, false, true};
+                    } else {
+                        auto r = node_t::copy_inner_replace_value(
+                            node,
+                            offset,
+                            Combine{}(std::forward<K>(k),
+                                      std::forward<Fn>(fn)(
+                                          Project{}(detail::as_const(*val)))));
+                        return {node_t::owned_values(r, e), false, false};
+                    }
+                } else {
+                    auto mutate        = node->can_mutate(e);
+                    auto mutate_values = mutate && node->can_mutate_values(e);
+                    auto hash2         = Hash{}(*val);
+                    auto child         = node_t::make_merged_e(
+                        e,
+                        shift + B,
+                        Combine{}(std::forward<K>(k),
+                                  std::forward<Fn>(fn)(Default{}())),
+                        hash,
+                        mutate_values ? std::move(*val) : *val,
+                        hash2);
+                    IMMER_TRY {
+                        auto r = mutate ? node_t::move_inner_replace_merged(
+                                              e, node, bit, offset, child)
+                                        : node_t::copy_inner_replace_merged(
+                                              node, bit, offset, child);
+                        return {node_t::owned_values_safe(r, e), true, mutate};
+                    }
+                    IMMER_CATCH (...) {
+                        node_t::delete_deep_shift(child, shift + B);
+                        IMMER_RETHROW;
+                    }
+                }
+            } else {
+                auto mutate = node->can_mutate(e);
+                auto v      = Combine{}(std::forward<K>(k),
+                                   std::forward<Fn>(fn)(Default{}()));
+                auto r      = mutate ? node_t::move_inner_insert_value(
+                                      e, node, bit, std::move(v))
+                                     : node_t::copy_inner_insert_value(
+                                      node, bit, std::move(v));
+                return {node_t::owned_values(r, e), true, mutate};
+            }
+        }
+    }
+
+    template <typename Project,
+              typename Default,
+              typename Combine,
+              typename K,
+              typename Fn>
+    void update_mut(edit_t e, const K& k, Fn&& fn)
+    {
+        auto hash = Hash{}(k);
+        auto res  = do_update_mut<Project, Default, Combine>(
+            e, root, k, std::forward<Fn>(fn), hash, 0);
+        if (!res.mutated && root->dec())
+            node_t::delete_deep(root, 0);
+        root = res.node;
+        size += res.added ? 1 : 0;
+    }
+
+    struct update_if_exists_mut_result
+    {
+        node_t* node;
+        bool mutated;
+    };
+
+    template <typename Project, typename Combine, typename K, typename Fn>
+    update_if_exists_mut_result do_update_if_exists_mut(edit_t e,
+                                                        node_t* node,
+                                                        K&& k,
+                                                        Fn&& fn,
+                                                        hash_t hash,
+                                                        shift_t shift) const
+    {
+        if (shift == max_shift<B>) {
+            auto fst = node->collisions();
+            auto lst = fst + node->collision_count();
+            for (; fst != lst; ++fst)
+                if (Equal{}(*fst, k)) {
+                    if (node->can_mutate(e)) {
+                        *fst = Combine{}(
+                            std::forward<K>(k),
+                            std::forward<Fn>(fn)(Project{}(std::move(*fst))));
+                        return {node, true};
+                    } else {
+                        auto r = node_t::copy_collision_replace(
+                            node,
+                            fst,
+                            Combine{}(std::forward<K>(k),
+                                      std::forward<Fn>(fn)(
+                                          Project{}(detail::as_const(*fst)))));
+                        return {node_t::owned(r, e), false};
+                    }
+                }
+            return {nullptr, false};
+        } else {
+            auto idx = (hash & (mask<B> << shift)) >> shift;
+            auto bit = bitmap_t{1u} << idx;
+            if (node->nodemap() & bit) {
+                auto offset = node->children_count(bit);
+                auto child  = node->children()[offset];
+                if (node->can_mutate(e)) {
+                    auto result = do_update_if_exists_mut<Project, Combine>(
+                        e, child, k, std::forward<Fn>(fn), hash, shift + B);
+                    if (result.node) {
+                        node->children()[offset] = result.node;
+                        if (!result.mutated && child->dec())
+                            node_t::delete_deep_shift(child, shift + B);
+                        return {node, true};
+                    } else {
+                        return {nullptr, false};
+                    }
+                } else {
+                    auto result = do_update_if_exists<Project, Combine>(
+                        child, k, std::forward<Fn>(fn), hash, shift + B);
+                    IMMER_TRY {
+                        if (result) {
+                            result = node_t::copy_inner_replace(
+                                node, offset, result);
+                            node_t::owned(result, e);
+                            return {result, false};
+                        } else {
+                            return {nullptr, false};
+                        }
+                    }
+                    IMMER_CATCH (...) {
+                        node_t::delete_deep_shift(result, shift + B);
+                        IMMER_RETHROW;
+                    }
+                }
+            } else if (node->datamap() & bit) {
+                auto offset = node->data_count(bit);
+                auto val    = node->values() + offset;
+                if (Equal{}(*val, k)) {
+                    if (node->can_mutate(e)) {
+                        auto vals    = node->ensure_mutable_values(e);
+                        vals[offset] = Combine{}(std::forward<K>(k),
+                                                 std::forward<Fn>(fn)(Project{}(
+                                                     std::move(vals[offset]))));
+                        return {node, true};
+                    } else {
+                        auto r = node_t::copy_inner_replace_value(
+                            node,
+                            offset,
+                            Combine{}(std::forward<K>(k),
+                                      std::forward<Fn>(fn)(
+                                          Project{}(detail::as_const(*val)))));
+                        return {node_t::owned_values(r, e), false};
+                    }
+                } else {
+                    return {nullptr, false};
+                }
+            } else {
+                return {nullptr, false};
+            }
+        }
+    }
+
+    template <typename Project, typename Combine, typename K, typename Fn>
+    void update_if_exists_mut(edit_t e, const K& k, Fn&& fn)
+    {
+        auto hash = Hash{}(k);
+        auto res  = do_update_if_exists_mut<Project, Combine>(
+            e, root, k, std::forward<Fn>(fn), hash, 0);
+        if (res.node) {
+            if (!res.mutated && root->dec())
+                node_t::delete_deep(root, 0);
+            root = res.node;
+        }
     }
 
     // basically:
@@ -330,20 +1173,30 @@ struct champ
 
         union data_t
         {
-            T*      singleton;
+            T* singleton;
             node_t* tree;
         };
 
         kind_t kind;
         data_t data;
 
-        sub_result()          : kind{nothing}   {};
-        sub_result(T* x)      : kind{singleton} { data.singleton = x; };
-        sub_result(node_t* x) : kind{tree}      { data.tree = x; };
+        sub_result()
+            : kind{nothing} {};
+        sub_result(T* x)
+            : kind{singleton}
+        {
+            data.singleton = x;
+        };
+        sub_result(node_t* x)
+            : kind{tree}
+        {
+            data.tree = x;
+        };
     };
 
     template <typename K>
-    sub_result do_sub(node_t* node, const K& k, hash_t hash, shift_t shift) const
+    sub_result
+    do_sub(node_t* node, const K& k, hash_t hash, shift_t shift) const
     {
         if (shift == max_shift<B>) {
             auto fst = node->collisions();
@@ -351,51 +1204,65 @@ struct champ
             for (auto cur = fst; cur != lst; ++cur)
                 if (Equal{}(*cur, k))
                     return node->collision_count() > 2
-                        ? node_t::copy_collision_remove(node, cur)
-                        : sub_result{fst + (cur == fst)};
+                               ? node_t::copy_collision_remove(node, cur)
+                               : sub_result{fst + (cur == fst)};
+#if !defined(_MSC_VER)
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+#endif
+            // Apparently GCC is generating this warning sometimes when
+            // compiling the benchmarks. It makes however no sense at all.
             return {};
+#if !defined(_MSC_VER)
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+#endif
         } else {
             auto idx = (hash & (mask<B> << shift)) >> shift;
             auto bit = bitmap_t{1u} << idx;
             if (node->nodemap() & bit) {
-                auto offset = popcount(node->nodemap() & (bit - 1));
-                auto result = do_sub(node->children() [offset],
-                                     k, hash, shift + B);
+                auto offset = node->children_count(bit);
+                auto result =
+                    do_sub(node->children()[offset], k, hash, shift + B);
                 switch (result.kind) {
                 case sub_result::nothing:
                     return {};
                 case sub_result::singleton:
                     return node->datamap() == 0 &&
-                           popcount(node->nodemap()) == 1 &&
-                           shift > 0
-                        ? result
-                        : node_t::copy_inner_replace_inline(
-                            node, bit, offset, *result.data.singleton);
+                                   node->children_count() == 1 && shift > 0
+                               ? result
+                               : node_t::copy_inner_replace_inline(
+                                     node, bit, offset, *result.data.singleton);
                 case sub_result::tree:
-                    try {
-                        return node_t::copy_inner_replace(node, offset,
-                                                          result.data.tree);
-                    } catch (...) {
+                    IMMER_TRY {
+                        return node_t::copy_inner_replace(
+                            node, offset, result.data.tree);
+                    }
+                    IMMER_CATCH (...) {
                         node_t::delete_deep_shift(result.data.tree, shift + B);
-                        throw;
+                        IMMER_RETHROW;
                     }
                 }
             } else if (node->datamap() & bit) {
-                auto offset = popcount(node->datamap() & (bit - 1));
+                auto offset = node->data_count(bit);
                 auto val    = node->values() + offset;
                 if (Equal{}(*val, k)) {
-                    auto nv = popcount(node->datamap());
+                    auto nv = node->data_count();
                     if (node->nodemap() || nv > 2)
-                        return node_t::copy_inner_remove_value(node, bit, offset);
+                        return node_t::copy_inner_remove_value(
+                            node, bit, offset);
                     else if (nv == 2) {
-                        return shift > 0
-                            ? sub_result{node->values() + !offset}
-                            : node_t::make_inner_n(0,
-                                                  node->datamap() & ~bit,
-                                                  node->values()[!offset]);
+                        return shift > 0 ? sub_result{node->values() + !offset}
+                                         : node_t::make_inner_n(
+                                               0,
+                                               node->datamap() & ~bit,
+                                               node->values()[!offset]);
                     } else {
                         assert(shift == 0);
-                        return empty().root->inc();
+                        return empty();
                     }
                 }
             }
@@ -407,21 +1274,225 @@ struct champ
     champ sub(const K& k) const
     {
         auto hash = Hash{}(k);
-        auto res = do_sub(root, k, hash, 0);
+        auto res  = do_sub(root, k, hash, 0);
         switch (res.kind) {
         case sub_result::nothing:
             return *this;
         case sub_result::tree:
-            return {
-                res.data.tree,
-                size - 1
-            };
+            return {res.data.tree, size - 1};
+        default:
+            IMMER_UNREACHABLE;
+        }
+    }
+
+    struct sub_result_mut
+    {
+        using kind_t = typename sub_result::kind_t;
+        using data_t = typename sub_result::data_t;
+
+        kind_t kind;
+        data_t data;
+        bool owned;
+        bool mutated;
+
+        sub_result_mut(sub_result a)
+            : kind{a.kind}
+            , data{a.data}
+            , owned{false}
+            , mutated{false}
+        {}
+        sub_result_mut(sub_result a, bool m)
+            : kind{a.kind}
+            , data{a.data}
+            , owned{false}
+            , mutated{m}
+        {}
+        sub_result_mut()
+            : kind{kind_t::nothing}
+            , mutated{false} {};
+        sub_result_mut(T* x, bool m)
+            : kind{kind_t::singleton}
+            , owned{m}
+            , mutated{m}
+        {
+            data.singleton = x;
+        };
+        sub_result_mut(T* x, bool o, bool m)
+            : kind{kind_t::singleton}
+            , owned{o}
+            , mutated{m}
+        {
+            data.singleton = x;
+        };
+        sub_result_mut(node_t* x, bool m)
+            : kind{kind_t::tree}
+            , owned{false}
+            , mutated{m}
+        {
+            data.tree = x;
+        };
+    };
+
+    template <typename K>
+    sub_result_mut do_sub_mut(edit_t e,
+                              node_t* node,
+                              const K& k,
+                              hash_t hash,
+                              shift_t shift,
+                              void* store) const
+    {
+        auto mutate = node->can_mutate(e);
+        if (shift == max_shift<B>) {
+            auto fst = node->collisions();
+            auto lst = fst + node->collision_count();
+            for (auto cur = fst; cur != lst; ++cur) {
+                if (Equal{}(*cur, k)) {
+                    if (node->collision_count() <= 2) {
+                        if (mutate) {
+                            auto r = new (store)
+                                T{std::move(node->collisions()[cur == fst])};
+                            node_t::delete_collision(node);
+                            return sub_result_mut{r, true};
+                        } else {
+                            return sub_result_mut{fst + (cur == fst), false};
+                        }
+                    } else {
+                        auto r = mutate
+                                     ? node_t::move_collision_remove(node, cur)
+                                     : node_t::copy_collision_remove(node, cur);
+                        return {node_t::owned(r, e), mutate};
+                    }
+                }
+            }
+            return {};
+        } else {
+            auto idx = (hash & (mask<B> << shift)) >> shift;
+            auto bit = bitmap_t{1u} << idx;
+            if (node->nodemap() & bit) {
+                auto offset   = node->children_count(bit);
+                auto children = node->children();
+                auto child    = children[offset];
+                auto result =
+                    mutate ? do_sub_mut(e, child, k, hash, shift + B, store)
+                           : do_sub(child, k, hash, shift + B);
+                switch (result.kind) {
+                case sub_result::nothing:
+                    return {};
+                case sub_result::singleton:
+                    if (node->datamap() == 0 && node->children_count() == 1 &&
+                        shift > 0) {
+                        if (mutate) {
+                            node_t::delete_inner(node);
+                            if (!result.mutated && child->dec())
+                                node_t::delete_deep_shift(child, shift + B);
+                        }
+                        return {result.data.singleton, result.owned, mutate};
+                    } else {
+                        auto r =
+                            mutate ? node_t::move_inner_replace_inline(
+                                         e,
+                                         node,
+                                         bit,
+                                         offset,
+                                         result.owned
+                                             ? std::move(*result.data.singleton)
+                                             : *result.data.singleton)
+                                   : node_t::copy_inner_replace_inline(
+                                         node,
+                                         bit,
+                                         offset,
+                                         *result.data.singleton);
+                        if (result.owned)
+                            detail::destroy_at(result.data.singleton);
+                        if (!result.mutated && mutate && child->dec())
+                            node_t::delete_deep_shift(child, shift + B);
+                        return {node_t::owned_values(r, e), mutate};
+                    }
+                case sub_result::tree:
+                    if (mutate) {
+                        children[offset] = result.data.tree;
+                        if (!result.mutated && child->dec())
+                            node_t::delete_deep_shift(child, shift + B);
+                        return {node, true};
+                    } else {
+                        IMMER_TRY {
+                            auto r = node_t::copy_inner_replace(
+                                node, offset, result.data.tree);
+                            return {node_t::owned(r, e), false};
+                        }
+                        IMMER_CATCH (...) {
+                            node_t::delete_deep_shift(result.data.tree,
+                                                      shift + B);
+                            IMMER_RETHROW;
+                        }
+                    }
+                }
+            } else if (node->datamap() & bit) {
+                auto offset        = node->data_count(bit);
+                auto val           = node->values() + offset;
+                auto mutate_values = mutate && node->can_mutate_values(e);
+                if (Equal{}(*val, k)) {
+                    auto nv = node->data_count();
+                    if (node->nodemap() || nv > 2) {
+                        auto r = mutate ? node_t::move_inner_remove_value(
+                                              e, node, bit, offset)
+                                        : node_t::copy_inner_remove_value(
+                                              node, bit, offset);
+                        return {node_t::owned_values_safe(r, e), mutate};
+                    } else if (nv == 2) {
+                        if (shift > 0) {
+                            if (mutate_values) {
+                                auto r = new (store)
+                                    T{std::move(node->values()[!offset])};
+                                node_t::delete_inner(node);
+                                return {r, true};
+                            } else {
+                                return {node->values() + !offset, false};
+                            }
+                        } else {
+                            auto& v = node->values()[!offset];
+                            auto r  = node_t::make_inner_n(
+                                0,
+                                node->datamap() & ~bit,
+                                mutate_values ? std::move(v) : v);
+                            assert(!node->nodemap());
+                            if (mutate)
+                                node_t::delete_inner(node);
+                            return {node_t::owned_values(r, e), mutate};
+                        }
+                    } else {
+                        assert(shift == 0);
+                        if (mutate)
+                            node_t::delete_inner(node);
+                        return {empty(), mutate};
+                    }
+                }
+            }
+            return {};
+        }
+    }
+
+    template <typename K>
+    void sub_mut(edit_t e, const K& k)
+    {
+        auto store = aligned_storage_for<T>{};
+        auto hash  = Hash{}(k);
+        auto res   = do_sub_mut(e, root, k, hash, 0, &store);
+        switch (res.kind) {
+        case sub_result::nothing:
+            break;
+        case sub_result::tree:
+            if (!res.mutated && root->dec())
+                node_t::delete_deep(root, 0);
+            root = res.data.tree;
+            --size;
+            break;
         default:
             IMMER_UNREACHABLE;
         }
     }
 
-    template <typename Eq=Equal>
+    template <typename Eq = Equal>
     bool equals(const champ& other) const
     {
         return size == other.size && equals_tree<Eq>(root, other.root, 0);
@@ -435,16 +1506,16 @@ struct champ
         else if (depth == max_depth<B>) {
             auto nv = a->collision_count();
             return nv == b->collision_count() &&
-                equals_collisions<Eq>(a->collisions(), b->collisions(), nv);
+                   equals_collisions<Eq>(a->collisions(), b->collisions(), nv);
         } else {
-            if (a->nodemap() != b->nodemap() ||
-                a->datamap() != b->datamap())
+            if (a->nodemap() != b->nodemap() || a->datamap() != b->datamap())
                 return false;
-            auto n = popcount(a->nodemap());
+            auto n = a->children_count();
             for (auto i = count_t{}; i < n; ++i)
-                if (!equals_tree<Eq>(a->children()[i], b->children()[i], depth + 1))
+                if (!equals_tree<Eq>(
+                        a->children()[i], b->children()[i], depth + 1))
                     return false;
-            auto nv = popcount(a->datamap());
+            auto nv = a->data_count();
             return !nv || equals_values<Eq>(a->values(), b->values(), nv);
         }
     }
@@ -465,7 +1536,8 @@ struct champ
                 if (Eq{}(*a, *fst))
                     goto good;
             return false;
-        good: continue;
+        good:
+            continue;
         }
         return true;
     }
diff --git a/src/immer/detail/hamts/champ_iterator.hpp b/src/immer/detail/hamts/champ_iterator.hpp
index 07d552daca..5ff3106024 100644
--- a/src/immer/detail/hamts/champ_iterator.hpp
+++ b/src/immer/detail/hamts/champ_iterator.hpp
@@ -27,16 +27,17 @@ struct champ_iterator
     using tree_t = champ<T, Hash, Eq, MP, B>;
     using node_t = typename tree_t::node_t;
 
-    struct end_t {};
-
     champ_iterator() = default;
 
+    struct end_t
+    {};
+
     champ_iterator(const tree_t& v)
-        : depth_ { 0 }
+        : depth_{0}
     {
         if (v.root->datamap()) {
             cur_ = v.root->values();
-            end_ = v.root->values() + popcount(v.root->datamap());
+            end_ = v.root->values() + v.root->data_count();
         } else {
             cur_ = end_ = nullptr;
         }
@@ -45,17 +46,17 @@ struct champ_iterator
     }
 
     champ_iterator(const tree_t& v, end_t)
-        : cur_   { nullptr }
-        , end_   { nullptr }
-        , depth_ { 0 }
+        : cur_{nullptr}
+        , end_{nullptr}
+        , depth_{0}
     {
         path_[0] = &v.root;
     }
 
     champ_iterator(const champ_iterator& other)
-        : cur_   { other.cur_ }
-        , end_   { other.end_ }
-        , depth_ { other.depth_ }
+        : cur_{other.cur_}
+        , end_{other.end_}
+        , depth_{other.depth_}
     {
         std::copy(other.path_, other.path_ + depth_ + 1, path_);
     }
@@ -66,7 +67,9 @@ struct champ_iterator
     T* cur_;
     T* end_;
     count_t depth_;
-    node_t* const* path_[max_depth<B> + 1];
+    node_t* const* path_[max_depth<B> + 1] = {
+        0,
+    };
 
     void increment()
     {
@@ -78,14 +81,16 @@ struct champ_iterator
     {
         if (depth_ < max_depth<B>) {
             auto parent = *path_[depth_];
+            assert(parent);
             if (parent->nodemap()) {
                 ++depth_;
                 path_[depth_] = parent->children();
-                auto child = *path_[depth_];
+                auto child    = *path_[depth_];
+                assert(child);
                 if (depth_ < max_depth<B>) {
                     if (child->datamap()) {
                         cur_ = child->values();
-                        end_ = cur_ + popcount(child->datamap());
+                        end_ = cur_ + child->data_count();
                     }
                 } else {
                     cur_ = child->collisions();
@@ -101,15 +106,16 @@ struct champ_iterator
     {
         while (depth_ > 0) {
             auto parent = *path_[depth_ - 1];
-            auto last   = parent->children() + popcount(parent->nodemap());
+            auto last   = parent->children() + parent->children_count();
             auto next   = path_[depth_] + 1;
             if (next < last) {
                 path_[depth_] = next;
-                auto child = *path_[depth_];
+                auto child    = *path_[depth_];
+                assert(child);
                 if (depth_ < max_depth<B>) {
                     if (child->datamap()) {
                         cur_ = child->values();
-                        end_ = cur_ + popcount(child->datamap());
+                        end_ = cur_ + child->data_count();
                     }
                 } else {
                     cur_ = child->collisions();
@@ -117,7 +123,7 @@ struct champ_iterator
                 }
                 return true;
             }
-            -- depth_;
+            --depth_;
         }
         return false;
     }
@@ -137,15 +143,9 @@ struct champ_iterator
         }
     }
 
-    bool equal(const champ_iterator& other) const
-    {
-        return cur_ == other.cur_;
-    }
+    bool equal(const champ_iterator& other) const { return cur_ == other.cur_; }
 
-    const T& dereference() const
-    {
-        return *cur_;
-    }
+    const T& dereference() const { return *cur_; }
 };
 
 } // namespace hamts
diff --git a/src/immer/detail/hamts/node.hpp b/src/immer/detail/hamts/node.hpp
index 61aa381336..7f7dc8b1fe 100644
--- a/src/immer/detail/hamts/node.hpp
+++ b/src/immer/detail/hamts/node.hpp
@@ -8,22 +8,23 @@
 
 #pragma once
 
+#include <immer/config.hpp>
 #include <immer/detail/combine_standard_layout.hpp>
-#include <immer/detail/util.hpp>
 #include <immer/detail/hamts/bits.hpp>
+#include <immer/detail/util.hpp>
 
 #include <cassert>
-
-#ifdef NDEBUG
-#define IMMER_HAMTS_TAGGED_NODE 0
-#else
-#define IMMER_HAMTS_TAGGED_NODE 1
-#endif
+#include <cstddef>
 
 namespace immer {
 namespace detail {
 namespace hamts {
 
+// For C++14 support.
+// Calling the destructor inline breaks MSVC in some obscure
+// corner cases.
+template <typename T> constexpr void destroy_at(T* p) { p->~T(); }
+
 template <typename T,
           typename Hash,
           typename Equal,
@@ -31,7 +32,7 @@ template <typename T,
           bits_t B>
 struct node
 {
-    using node_t      = node;
+    using node_t = node;
 
     using memory      = MemoryPolicy;
     using heap_policy = typename memory::heap;
@@ -60,13 +61,12 @@ struct node
         aligned_storage_for<T> buffer;
     };
 
-    using values_t = combine_standard_layout_t<
-        values_data_t, refs_t>;
+    using values_t = combine_standard_layout_t<values_data_t, refs_t, ownee_t>;
 
     struct inner_t
     {
-        bitmap_t  nodemap;
-        bitmap_t  datamap;
+        bitmap_t nodemap;
+        bitmap_t datamap;
         values_t* values;
         aligned_storage_for<node_t*> buffer;
     };
@@ -79,112 +79,156 @@ struct node
 
     struct impl_data_t
     {
-#if IMMER_HAMTS_TAGGED_NODE
+#if IMMER_TAGGED_NODE
         kind_t kind;
 #endif
         data_t data;
     };
 
-    using impl_t = combine_standard_layout_t<
-        impl_data_t, refs_t>;
+    using impl_t = combine_standard_layout_t<impl_data_t, refs_t, ownee_t>;
 
     impl_t impl;
 
     constexpr static std::size_t sizeof_values_n(count_t count)
     {
-        return immer_offsetof(values_t, d.buffer)
-            + sizeof(values_data_t::buffer) * count;
+        return std::max(sizeof(values_t),
+                        immer_offsetof(values_t, d.buffer) +
+                            sizeof(values_data_t::buffer) * count);
     }
 
     constexpr static std::size_t sizeof_collision_n(count_t count)
     {
-        return immer_offsetof(impl_t, d.data.collision.buffer)
-            + sizeof(collision_t::buffer) * count;
+        return immer_offsetof(impl_t, d.data.collision.buffer) +
+               sizeof(collision_t::buffer) * count;
     }
 
     constexpr static std::size_t sizeof_inner_n(count_t count)
     {
-        return immer_offsetof(impl_t, d.data.inner.buffer)
-            + sizeof(inner_t::buffer) * count;
+        return immer_offsetof(impl_t, d.data.inner.buffer) +
+               sizeof(inner_t::buffer) * count;
     }
 
-#if IMMER_HAMTS_TAGGED_NODE
-    kind_t kind() const
-    {
-        return impl.d.kind;
-    }
+#if IMMER_TAGGED_NODE
+    kind_t kind() const { return impl.d.kind; }
 #endif
 
     auto values()
     {
-        assert(kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
         assert(impl.d.data.inner.values);
         return (T*) &impl.d.data.inner.values->d.buffer;
     }
 
     auto values() const
     {
-        assert(kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
         assert(impl.d.data.inner.values);
         return (const T*) &impl.d.data.inner.values->d.buffer;
     }
 
     auto children()
     {
-        assert(kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
         return (node_t**) &impl.d.data.inner.buffer;
     }
 
     auto children() const
     {
-        assert(kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
         return (const node_t* const*) &impl.d.data.inner.buffer;
     }
 
     auto datamap() const
     {
-        assert(kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
         return impl.d.data.inner.datamap;
     }
 
     auto nodemap() const
     {
-        assert(kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
         return impl.d.data.inner.nodemap;
     }
 
+    auto data_count() const
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        return popcount(datamap());
+    }
+
+    auto data_count(bitmap_t bit) const
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        return popcount(static_cast<bitmap_t>(datamap() & (bit - 1)));
+    }
+
+    auto children_count() const
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        return popcount(nodemap());
+    }
+
+    auto children_count(bitmap_t bit) const
+    {
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
+        return popcount(static_cast<bitmap_t>(nodemap() & (bit - 1)));
+    }
+
     auto collision_count() const
     {
-        assert(kind() == kind_t::collision);
+        IMMER_ASSERT_TAGGED(kind() == kind_t::collision);
         return impl.d.data.collision.count;
     }
 
     T* collisions()
     {
-        assert(kind() == kind_t::collision);
-        return (T*)&impl.d.data.collision.buffer;
+        IMMER_ASSERT_TAGGED(kind() == kind_t::collision);
+        return (T*) &impl.d.data.collision.buffer;
     }
 
     const T* collisions() const
     {
-        assert(kind() == kind_t::collision);
-        return (const T*)&impl.d.data.collision.buffer;
+        IMMER_ASSERT_TAGGED(kind() == kind_t::collision);
+        return (const T*) &impl.d.data.collision.buffer;
     }
 
-    static refs_t& refs(const values_t* x) { return auto_const_cast(get<refs_t>(*x)); }
+    static refs_t& refs(const values_t* x)
+    {
+        return auto_const_cast(get<refs_t>(*x));
+    }
     static const ownee_t& ownee(const values_t* x) { return get<ownee_t>(*x); }
     static ownee_t& ownee(values_t* x) { return get<ownee_t>(*x); }
+    static bool can_mutate(values_t* x, edit_t e)
+    {
+        return refs(x).unique() || ownee(x).can_mutate(e);
+    }
 
-    static refs_t& refs(const node_t* x) { return auto_const_cast(get<refs_t>(x->impl)); }
-    static const ownee_t& ownee(const node_t* x) { return get<ownee_t>(x->impl); }
+    static refs_t& refs(const node_t* x)
+    {
+        return auto_const_cast(get<refs_t>(x->impl));
+    }
+    static const ownee_t& ownee(const node_t* x)
+    {
+        return get<ownee_t>(x->impl);
+    }
     static ownee_t& ownee(node_t* x) { return get<ownee_t>(x->impl); }
 
+    bool can_mutate(edit_t e) const
+    {
+        return refs(this).unique() || ownee(this).can_mutate(e);
+    }
+    bool can_mutate_values(edit_t e) const
+    {
+        return can_mutate(impl.d.data.inner.values, e);
+    }
+
     static node_t* make_inner_n(count_t n)
     {
         assert(n <= branches<B>);
         auto m = heap::allocate(sizeof_inner_n(n));
         auto p = new (m) node_t;
-#if IMMER_HAMTS_TAGGED_NODE
+        assert(p == (node_t*) m);
+#if IMMER_TAGGED_NODE
         p->impl.d.kind = node_t::kind_t::inner;
 #endif
         p->impl.d.data.inner.nodemap = 0;
@@ -208,12 +252,13 @@ struct node
         assert(nv <= branches<B>);
         auto p = make_inner_n(n);
         if (nv) {
-            try {
+            IMMER_TRY {
                 p->impl.d.data.inner.values =
                     new (heap::allocate(sizeof_values_n(nv))) values_t{};
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 deallocate_inner(p, n);
-                throw;
+                IMMER_RETHROW;
             }
         }
         return p;
@@ -222,47 +267,48 @@ struct node
     static node_t* make_inner_n(count_t n, count_t idx, node_t* child)
     {
         assert(n >= 1);
-        auto p = make_inner_n(n);
+        auto p                       = make_inner_n(n);
         p->impl.d.data.inner.nodemap = bitmap_t{1u} << idx;
-        p->children()[0] = child;
+        p->children()[0]             = child;
         return p;
     }
 
-    static node_t* make_inner_n(count_t n,
-                                bitmap_t bitmap,
-                                T x)
+    static node_t* make_inner_n(count_t n, bitmap_t bitmap, T x)
     {
-        auto p = make_inner_n(n, 1);
+        auto p                       = make_inner_n(n, 1);
         p->impl.d.data.inner.datamap = bitmap;
-        try {
+        IMMER_TRY {
             new (p->values()) T{std::move(x)};
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             deallocate_inner(p, n, 1);
-            throw;
+            IMMER_RETHROW;
         }
         return p;
     }
 
-    static node_t* make_inner_n(count_t n,
-                                count_t idx1, T x1,
-                                count_t idx2, T x2)
+    static node_t*
+    make_inner_n(count_t n, count_t idx1, T x1, count_t idx2, T x2)
     {
         assert(idx1 != idx2);
         auto p = make_inner_n(n, 2);
-        p->impl.d.data.inner.datamap = (bitmap_t{1u} << idx1) | (bitmap_t{1u} << idx2);
-        auto assign = [&] (auto&& x1, auto&& x2) {
+        p->impl.d.data.inner.datamap =
+            (bitmap_t{1u} << idx1) | (bitmap_t{1u} << idx2);
+        auto assign = [&](auto&& x1, auto&& x2) {
             auto vp = p->values();
-            try {
+            IMMER_TRY {
                 new (vp) T{std::move(x1)};
-                try {
+                IMMER_TRY {
                     new (vp + 1) T{std::move(x2)};
-                } catch (...) {
-                    vp->~T();
-                    throw;
                 }
-            } catch (...) {
+                IMMER_CATCH (...) {
+                    immer::detail::hamts::destroy_at(vp);
+                    IMMER_RETHROW;
+                }
+            }
+            IMMER_CATCH (...) {
                 deallocate_inner(p, n, 2);
-                throw;
+                IMMER_RETHROW;
             }
         };
         if (idx1 < idx2)
@@ -274,10 +320,9 @@ struct node
 
     static node_t* make_collision_n(count_t n)
     {
-        assert(n <= branches<B>);
         auto m = heap::allocate(sizeof_collision_n(n));
         auto p = new (m) node_t;
-#if IMMER_HAMTS_TAGGED_NODE
+#if IMMER_TAGGED_NODE
         p->impl.d.kind = node_t::kind_t::collision;
 #endif
         p->impl.d.data.collision.count = n;
@@ -288,337 +333,712 @@ struct node
     {
         auto m = heap::allocate(sizeof_collision_n(2));
         auto p = new (m) node_t;
-#if IMMER_HAMTS_TAGGED_NODE
+#if IMMER_TAGGED_NODE
         p->impl.d.kind = node_t::kind_t::collision;
 #endif
         p->impl.d.data.collision.count = 2;
-        auto cols = p->collisions();
-        try {
+        auto cols                      = p->collisions();
+        IMMER_TRY {
             new (cols) T{std::move(v1)};
-            try {
+            IMMER_TRY {
                 new (cols + 1) T{std::move(v2)};
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 cols->~T();
-                throw;
+                IMMER_RETHROW;
             }
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             deallocate_collision(p, 2);
-            throw;
+            IMMER_RETHROW;
         }
         return p;
     }
 
+    T* ensure_mutable_values(edit_t e)
+    {
+        assert(can_mutate(e));
+        auto old = impl.d.data.inner.values;
+        if (node_t::can_mutate(old, e))
+            return values();
+        else {
+            auto nv    = data_count();
+            auto nxt   = new (heap::allocate(sizeof_values_n(nv))) values_t{};
+            auto dst   = (T*) &nxt->d.buffer;
+            auto src   = values();
+            ownee(nxt) = e;
+            IMMER_TRY {
+                detail::uninitialized_copy(src, src + nv, dst);
+            }
+            IMMER_CATCH (...) {
+                deallocate_values(nxt, nv);
+                IMMER_RETHROW;
+            }
+            impl.d.data.inner.values = nxt;
+            if (refs(old).dec())
+                delete_values(old, nv);
+            return dst;
+        }
+    }
+
     static node_t* copy_collision_insert(node_t* src, T v)
     {
-        assert(src->kind() == kind_t::collision);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
         auto n    = src->collision_count();
         auto dst  = make_collision_n(n + 1);
         auto srcp = src->collisions();
         auto dstp = dst->collisions();
-        try {
+        IMMER_TRY {
             new (dstp) T{std::move(v)};
-            try {
-                std::uninitialized_copy(srcp, srcp + n, dstp + 1);
-            } catch (...) {
+            IMMER_TRY {
+                detail::uninitialized_copy(srcp, srcp + n, dstp + 1);
+            }
+            IMMER_CATCH (...) {
+                dstp->~T();
+                IMMER_RETHROW;
+            }
+        }
+        IMMER_CATCH (...) {
+            deallocate_collision(dst, n + 1);
+            IMMER_RETHROW;
+        }
+        return dst;
+    }
+
+    static node_t* move_collision_insert(node_t* src, T v)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
+        auto n    = src->collision_count();
+        auto dst  = make_collision_n(n + 1);
+        auto srcp = src->collisions();
+        auto dstp = dst->collisions();
+        IMMER_TRY {
+            new (dstp) T{std::move(v)};
+            IMMER_TRY {
+                detail::uninitialized_move(srcp, srcp + n, dstp + 1);
+            }
+            IMMER_CATCH (...) {
                 dstp->~T();
-                throw;
+                IMMER_RETHROW;
             }
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             deallocate_collision(dst, n + 1);
-            throw;
+            IMMER_RETHROW;
         }
+        delete_collision(src);
         return dst;
     }
 
     static node_t* copy_collision_remove(node_t* src, T* v)
     {
-        assert(src->kind() == kind_t::collision);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
         assert(src->collision_count() > 1);
         auto n    = src->collision_count();
         auto dst  = make_collision_n(n - 1);
         auto srcp = src->collisions();
         auto dstp = dst->collisions();
-        try {
-            dstp = std::uninitialized_copy(srcp, v, dstp);
-            try {
-                std::uninitialized_copy(v + 1, srcp + n, dstp);
-            } catch (...) {
-                destroy(dst->collisions(), dstp);
-                throw;
-            }
-        } catch (...) {
+        IMMER_TRY {
+            dstp = detail::uninitialized_copy(srcp, v, dstp);
+            IMMER_TRY {
+                detail::uninitialized_copy(v + 1, srcp + n, dstp);
+            }
+            IMMER_CATCH (...) {
+                detail::destroy(dst->collisions(), dstp);
+                IMMER_RETHROW;
+            }
+        }
+        IMMER_CATCH (...) {
             deallocate_collision(dst, n - 1);
-            throw;
+            IMMER_RETHROW;
         }
         return dst;
     }
 
+    static node_t* move_collision_remove(node_t* src, T* v)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
+        assert(src->collision_count() > 1);
+        auto n    = src->collision_count();
+        auto dst  = make_collision_n(n - 1);
+        auto srcp = src->collisions();
+        auto dstp = dst->collisions();
+        IMMER_TRY {
+            dstp = detail::uninitialized_move(srcp, v, dstp);
+            IMMER_TRY {
+                detail::uninitialized_move(v + 1, srcp + n, dstp);
+            }
+            IMMER_CATCH (...) {
+                detail::destroy(dst->collisions(), dstp);
+                IMMER_RETHROW;
+            }
+        }
+        IMMER_CATCH (...) {
+            deallocate_collision(dst, n - 1);
+            IMMER_RETHROW;
+        }
+        delete_collision(src);
+        return dst;
+    }
+
     static node_t* copy_collision_replace(node_t* src, T* pos, T v)
     {
-        assert(src->kind() == kind_t::collision);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::collision);
         auto n    = src->collision_count();
         auto dst  = make_collision_n(n);
         auto srcp = src->collisions();
         auto dstp = dst->collisions();
         assert(pos >= srcp && pos < srcp + n);
-        try {
+        IMMER_TRY {
             new (dstp) T{std::move(v)};
-            try {
-                dstp = std::uninitialized_copy(srcp, pos, dstp + 1);
-                try {
-                    std::uninitialized_copy(pos + 1, srcp + n, dstp);
-                } catch (...) {
-                    destroy(dst->collisions(), dstp);
-                    throw;
+            IMMER_TRY {
+                dstp = detail::uninitialized_copy(srcp, pos, dstp + 1);
+                IMMER_TRY {
+                    detail::uninitialized_copy(pos + 1, srcp + n, dstp);
+                }
+                IMMER_CATCH (...) {
+                    detail::destroy(dst->collisions(), dstp);
+                    IMMER_RETHROW;
                 }
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 dst->collisions()->~T();
-                throw;
+                IMMER_RETHROW;
             }
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             deallocate_collision(dst, n);
-            throw;
+            IMMER_RETHROW;
         }
         return dst;
     }
 
-    static node_t* copy_inner_replace(node_t* src,
-                                      count_t offset, node_t* child)
+    static node_t*
+    copy_inner_replace(node_t* src, count_t offset, node_t* child)
     {
-        assert(src->kind() == kind_t::inner);
-        auto n    = popcount(src->nodemap());
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto n    = src->children_count();
         auto dst  = make_inner_n(n, src->impl.d.data.inner.values);
         auto srcp = src->children();
         auto dstp = dst->children();
         dst->impl.d.data.inner.datamap = src->datamap();
         dst->impl.d.data.inner.nodemap = src->nodemap();
-        std::uninitialized_copy(srcp, srcp + n, dstp);
-        inc_nodes(srcp, n);
-        srcp[offset]->dec_unsafe();
+        std::copy(srcp, srcp + n, dstp);
+        inc_nodes(srcp, offset);
+        inc_nodes(srcp + offset + 1, n - offset - 1);
         dstp[offset] = child;
         return dst;
     }
 
-    static node_t* copy_inner_replace_value(node_t* src,
-                                            count_t offset, T v)
+    static node_t* owned(node_t* n, edit_t e)
+    {
+        ownee(n) = e;
+        return n;
+    }
+
+    static node_t* owned_values(node_t* n, edit_t e)
     {
-        assert(src->kind() == kind_t::inner);
-        assert(offset < popcount(src->datamap()));
-        auto n    = popcount(src->nodemap());
-        auto nv   = popcount(src->datamap());
-        auto dst  = make_inner_n(n, nv);
+        ownee(n)                           = e;
+        ownee(n->impl.d.data.inner.values) = e;
+        return n;
+    }
+
+    static node_t* owned_values_safe(node_t* n, edit_t e)
+    {
+        ownee(n) = e;
+        if (n->impl.d.data.inner.values)
+            ownee(n->impl.d.data.inner.values) = e;
+        return n;
+    }
+
+    static node_t* copy_inner_replace_value(node_t* src, count_t offset, T v)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        assert(offset < src->data_count());
+        auto n                         = src->children_count();
+        auto nv                        = src->data_count();
+        auto dst                       = make_inner_n(n, nv);
         dst->impl.d.data.inner.datamap = src->datamap();
         dst->impl.d.data.inner.nodemap = src->nodemap();
-        try {
-            std::uninitialized_copy(
+        IMMER_TRY {
+            detail::uninitialized_copy(
                 src->values(), src->values() + nv, dst->values());
-            try {
+            IMMER_TRY {
                 dst->values()[offset] = std::move(v);
-            } catch (...) {
-                destroy_n(dst->values(), nv);
-                throw;
             }
-        } catch (...) {
+            IMMER_CATCH (...) {
+                detail::destroy_n(dst->values(), nv);
+                IMMER_RETHROW;
+            }
+        }
+        IMMER_CATCH (...) {
             deallocate_inner(dst, n, nv);
-            throw;
+            IMMER_RETHROW;
         }
         inc_nodes(src->children(), n);
-        std::uninitialized_copy(
-            src->children(), src->children() + n, dst->children());
+        std::copy(src->children(), src->children() + n, dst->children());
         return dst;
     }
 
-    static node_t* copy_inner_replace_merged(
-        node_t* src, bitmap_t bit, count_t voffset, node_t* node)
+    static node_t* copy_inner_replace_merged(node_t* src,
+                                             bitmap_t bit,
+                                             count_t voffset,
+                                             node_t* node)
     {
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         assert(!(src->nodemap() & bit));
         assert(src->datamap() & bit);
-        assert(voffset == popcount(src->datamap() & (bit - 1)));
-        auto n       = popcount(src->nodemap());
-        auto nv      = popcount(src->datamap());
-        auto dst     = make_inner_n(n + 1, nv - 1);
-        auto noffset = popcount(src->nodemap() & (bit - 1));
+        assert(voffset == src->data_count(bit));
+        auto n                         = src->children_count();
+        auto nv                        = src->data_count();
+        auto dst                       = make_inner_n(n + 1, nv - 1);
+        auto noffset                   = src->children_count(bit);
         dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
         dst->impl.d.data.inner.nodemap = src->nodemap() | bit;
         if (nv > 1) {
-            try {
-                std::uninitialized_copy(
-                    src->values(), src->values() + voffset,
-                    dst->values());
-                try {
-                    std::uninitialized_copy(
-                        src->values() + voffset + 1, src->values() + nv,
-                        dst->values() + voffset);
-                } catch (...) {
-                    destroy_n(dst->values(), voffset);
-                    throw;
+            IMMER_TRY {
+                detail::uninitialized_copy(
+                    src->values(), src->values() + voffset, dst->values());
+                IMMER_TRY {
+                    detail::uninitialized_copy(src->values() + voffset + 1,
+                                               src->values() + nv,
+                                               dst->values() + voffset);
+                }
+                IMMER_CATCH (...) {
+                    detail::destroy_n(dst->values(), voffset);
+                    IMMER_RETHROW;
                 }
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 deallocate_inner(dst, n + 1, nv - 1);
-                throw;
+                IMMER_RETHROW;
             }
         }
         inc_nodes(src->children(), n);
-        std::uninitialized_copy(
-            src->children(), src->children() + noffset,
-            dst->children());
-        std::uninitialized_copy(
-            src->children() + noffset, src->children() + n,
-            dst->children() + noffset + 1);
+        std::copy(src->children(), src->children() + noffset, dst->children());
+        std::copy(src->children() + noffset,
+                  src->children() + n,
+                  dst->children() + noffset + 1);
+        dst->children()[noffset] = node;
+        return dst;
+    }
+
+    static node_t* move_inner_replace_merged(
+        edit_t e, node_t* src, bitmap_t bit, count_t voffset, node_t* node)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        assert(!(src->nodemap() & bit));
+        assert(src->datamap() & bit);
+        assert(voffset == src->data_count(bit));
+        auto n                         = src->children_count();
+        auto nv                        = src->data_count();
+        auto dst                       = make_inner_n(n + 1, nv - 1);
+        auto noffset                   = src->children_count(bit);
+        dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
+        dst->impl.d.data.inner.nodemap = src->nodemap() | bit;
+        if (nv > 1) {
+            auto mutate_values = can_mutate(src->impl.d.data.inner.values, e);
+            IMMER_TRY {
+                if (mutate_values)
+                    detail::uninitialized_move(
+                        src->values(), src->values() + voffset, dst->values());
+                else
+                    detail::uninitialized_copy(
+                        src->values(), src->values() + voffset, dst->values());
+                IMMER_TRY {
+                    if (mutate_values)
+                        detail::uninitialized_move(src->values() + voffset + 1,
+                                                   src->values() + nv,
+                                                   dst->values() + voffset);
+                    else
+                        detail::uninitialized_copy(src->values() + voffset + 1,
+                                                   src->values() + nv,
+                                                   dst->values() + voffset);
+                }
+                IMMER_CATCH (...) {
+                    detail::destroy_n(dst->values(), voffset);
+                    IMMER_RETHROW;
+                }
+            }
+            IMMER_CATCH (...) {
+                deallocate_inner(dst, n + 1, nv - 1);
+                IMMER_RETHROW;
+            }
+        }
+        // inc_nodes(src->children(), n);
+        std::copy(src->children(), src->children() + noffset, dst->children());
+        std::copy(src->children() + noffset,
+                  src->children() + n,
+                  dst->children() + noffset + 1);
         dst->children()[noffset] = node;
+        delete_inner(src);
         return dst;
     }
 
-    static node_t* copy_inner_replace_inline(
-        node_t* src, bitmap_t bit, count_t noffset, T value)
+    static node_t* copy_inner_replace_inline(node_t* src,
+                                             bitmap_t bit,
+                                             count_t noffset,
+                                             T value)
     {
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         assert(!(src->datamap() & bit));
         assert(src->nodemap() & bit);
-        assert(noffset == popcount(src->nodemap() & (bit - 1)));
-        auto n       = popcount(src->nodemap());
-        auto nv      = popcount(src->datamap());
-        auto dst     = make_inner_n(n - 1, nv + 1);
-        auto voffset = popcount(src->datamap() & (bit - 1));
+        assert(noffset == src->children_count(bit));
+        auto n                         = src->children_count();
+        auto nv                        = src->data_count();
+        auto dst                       = make_inner_n(n - 1, nv + 1);
+        auto voffset                   = src->data_count(bit);
         dst->impl.d.data.inner.nodemap = src->nodemap() & ~bit;
         dst->impl.d.data.inner.datamap = src->datamap() | bit;
-        try {
+        IMMER_TRY {
             if (nv)
-                std::uninitialized_copy(
-                    src->values(), src->values() + voffset,
-                    dst->values());
-            try {
+                detail::uninitialized_copy(
+                    src->values(), src->values() + voffset, dst->values());
+            IMMER_TRY {
                 new (dst->values() + voffset) T{std::move(value)};
-                try {
+                IMMER_TRY {
                     if (nv)
-                        std::uninitialized_copy(
-                            src->values() + voffset, src->values() + nv,
-                            dst->values() + voffset + 1);
-                } catch (...) {
+                        detail::uninitialized_copy(src->values() + voffset,
+                                                   src->values() + nv,
+                                                   dst->values() + voffset + 1);
+                }
+                IMMER_CATCH (...) {
                     dst->values()[voffset].~T();
-                    throw;
+                    IMMER_RETHROW;
                 }
-            } catch (...) {
-                destroy_n(dst->values(), voffset);
-                throw;
             }
-        } catch (...) {
+            IMMER_CATCH (...) {
+                detail::destroy_n(dst->values(), voffset);
+                IMMER_RETHROW;
+            }
+        }
+        IMMER_CATCH (...) {
             deallocate_inner(dst, n - 1, nv + 1);
-            throw;
+            IMMER_RETHROW;
         }
-        inc_nodes(src->children(), n);
-        src->children()[noffset]->dec_unsafe();
-        std::uninitialized_copy(
-            src->children(), src->children() + noffset,
-            dst->children());
-        std::uninitialized_copy(
-            src->children() + noffset + 1, src->children() + n,
-            dst->children() + noffset);
+        inc_nodes(src->children(), noffset);
+        inc_nodes(src->children() + noffset + 1, n - noffset - 1);
+        std::copy(src->children(), src->children() + noffset, dst->children());
+        std::copy(src->children() + noffset + 1,
+                  src->children() + n,
+                  dst->children() + noffset);
         return dst;
     }
 
-    static node_t* copy_inner_remove_value(
-        node_t* src, bitmap_t bit, count_t voffset)
+    static node_t* move_inner_replace_inline(
+        edit_t e, node_t* src, bitmap_t bit, count_t noffset, T value)
     {
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        assert(!(src->datamap() & bit));
+        assert(src->nodemap() & bit);
+        assert(noffset == src->children_count(bit));
+        auto n                         = src->children_count();
+        auto nv                        = src->data_count();
+        auto dst                       = make_inner_n(n - 1, nv + 1);
+        auto voffset                   = src->data_count(bit);
+        dst->impl.d.data.inner.nodemap = src->nodemap() & ~bit;
+        dst->impl.d.data.inner.datamap = src->datamap() | bit;
+        IMMER_TRY {
+            auto mutate_values =
+                nv && can_mutate(src->impl.d.data.inner.values, e);
+            if (nv) {
+                if (mutate_values)
+                    detail::uninitialized_move(
+                        src->values(), src->values() + voffset, dst->values());
+                else
+                    detail::uninitialized_copy(
+                        src->values(), src->values() + voffset, dst->values());
+            }
+            IMMER_TRY {
+                new (dst->values() + voffset) T{std::move(value)};
+                IMMER_TRY {
+                    if (nv) {
+                        if (mutate_values)
+                            detail::uninitialized_move(src->values() + voffset,
+                                                       src->values() + nv,
+                                                       dst->values() + voffset +
+                                                           1);
+                        else
+                            detail::uninitialized_copy(src->values() + voffset,
+                                                       src->values() + nv,
+                                                       dst->values() + voffset +
+                                                           1);
+                    }
+                }
+                IMMER_CATCH (...) {
+                    dst->values()[voffset].~T();
+                    IMMER_RETHROW;
+                }
+            }
+            IMMER_CATCH (...) {
+                detail::destroy_n(dst->values(), voffset);
+                IMMER_RETHROW;
+            }
+        }
+        IMMER_CATCH (...) {
+            deallocate_inner(dst, n - 1, nv + 1);
+            IMMER_RETHROW;
+        }
+        std::copy(src->children(), src->children() + noffset, dst->children());
+        std::copy(src->children() + noffset + 1,
+                  src->children() + n,
+                  dst->children() + noffset);
+        delete_inner(src);
+        return dst;
+    }
+
+    static node_t*
+    copy_inner_remove_value(node_t* src, bitmap_t bit, count_t voffset)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         assert(!(src->nodemap() & bit));
         assert(src->datamap() & bit);
-        assert(voffset == popcount(src->datamap() & (bit - 1)));
-        auto n       = popcount(src->nodemap());
-        auto nv      = popcount(src->datamap());
-        auto dst     = make_inner_n(n, nv - 1);
+        assert(voffset == src->data_count(bit));
+        auto n                         = src->children_count();
+        auto nv                        = src->data_count();
+        auto dst                       = make_inner_n(n, nv - 1);
         dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
         dst->impl.d.data.inner.nodemap = src->nodemap();
         if (nv > 1) {
-            try {
-                std::uninitialized_copy(
-                    src->values(), src->values() + voffset,
-                    dst->values());
-                try {
-                    std::uninitialized_copy(
-                        src->values() + voffset + 1, src->values() + nv,
-                        dst->values() + voffset);
-                } catch (...) {
-                    destroy_n(dst->values(), voffset);
-                    throw;
+            IMMER_TRY {
+                detail::uninitialized_copy(
+                    src->values(), src->values() + voffset, dst->values());
+                IMMER_TRY {
+                    detail::uninitialized_copy(src->values() + voffset + 1,
+                                               src->values() + nv,
+                                               dst->values() + voffset);
+                }
+                IMMER_CATCH (...) {
+                    detail::destroy_n(dst->values(), voffset);
+                    IMMER_RETHROW;
                 }
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 deallocate_inner(dst, n, nv - 1);
-                throw;
+                IMMER_RETHROW;
             }
         }
         inc_nodes(src->children(), n);
-        std::uninitialized_copy(
-            src->children(), src->children() + n, dst->children());
+        std::copy(src->children(), src->children() + n, dst->children());
+        return dst;
+    }
+
+    static node_t* move_inner_remove_value(edit_t e,
+                                           node_t* src,
+                                           bitmap_t bit,
+                                           count_t voffset)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        assert(!(src->nodemap() & bit));
+        assert(src->datamap() & bit);
+        assert(voffset == src->data_count(bit));
+        auto n                         = src->children_count();
+        auto nv                        = src->data_count();
+        auto dst                       = make_inner_n(n, nv - 1);
+        dst->impl.d.data.inner.datamap = src->datamap() & ~bit;
+        dst->impl.d.data.inner.nodemap = src->nodemap();
+        if (nv > 1) {
+            auto mutate_values = can_mutate(src->impl.d.data.inner.values, e);
+            if (mutate_values) {
+                IMMER_TRY {
+                    detail::uninitialized_move(
+                        src->values(), src->values() + voffset, dst->values());
+                    IMMER_TRY {
+                        detail::uninitialized_move(src->values() + voffset + 1,
+                                                   src->values() + nv,
+                                                   dst->values() + voffset);
+                    }
+                    IMMER_CATCH (...) {
+                        detail::destroy_n(dst->values(), voffset);
+                        IMMER_RETHROW;
+                    }
+                }
+                IMMER_CATCH (...) {
+                    deallocate_inner(dst, n, nv - 1);
+                    IMMER_RETHROW;
+                }
+            } else {
+                IMMER_TRY {
+                    detail::uninitialized_copy(
+                        src->values(), src->values() + voffset, dst->values());
+                    IMMER_TRY {
+                        detail::uninitialized_copy(src->values() + voffset + 1,
+                                                   src->values() + nv,
+                                                   dst->values() + voffset);
+                    }
+                    IMMER_CATCH (...) {
+                        detail::destroy_n(dst->values(), voffset);
+                        IMMER_RETHROW;
+                    }
+                }
+                IMMER_CATCH (...) {
+                    deallocate_inner(dst, n, nv - 1);
+                    IMMER_RETHROW;
+                }
+            }
+        }
+        std::copy(src->children(), src->children() + n, dst->children());
+        delete_inner(src);
         return dst;
     }
 
     static node_t* copy_inner_insert_value(node_t* src, bitmap_t bit, T v)
     {
-        assert(src->kind() == kind_t::inner);
-        auto n      = popcount(src->nodemap());
-        auto nv     = popcount(src->datamap());
-        auto offset = popcount(src->datamap() & (bit - 1));
-        auto dst    = make_inner_n(n, nv + 1);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto n                         = src->children_count();
+        auto nv                        = src->data_count();
+        auto offset                    = src->data_count(bit);
+        auto dst                       = make_inner_n(n, nv + 1);
         dst->impl.d.data.inner.datamap = src->datamap() | bit;
         dst->impl.d.data.inner.nodemap = src->nodemap();
-        try {
+        IMMER_TRY {
             if (nv)
-                std::uninitialized_copy(
+                detail::uninitialized_copy(
                     src->values(), src->values() + offset, dst->values());
-            try {
+            IMMER_TRY {
                 new (dst->values() + offset) T{std::move(v)};
-                try {
+                IMMER_TRY {
                     if (nv)
-                        std::uninitialized_copy(
-                            src->values() + offset, src->values() + nv,
-                            dst->values() + offset + 1);
-                } catch (...) {
+                        detail::uninitialized_copy(src->values() + offset,
+                                                   src->values() + nv,
+                                                   dst->values() + offset + 1);
+                }
+                IMMER_CATCH (...) {
                     dst->values()[offset].~T();
-                    throw;
+                    IMMER_RETHROW;
                 }
-            } catch (...) {
-                destroy_n(dst->values(), offset);
-                throw;
             }
-        } catch (...) {
+            IMMER_CATCH (...) {
+                detail::destroy_n(dst->values(), offset);
+                IMMER_RETHROW;
+            }
+        }
+        IMMER_CATCH (...) {
             deallocate_inner(dst, n, nv + 1);
-            throw;
+            IMMER_RETHROW;
         }
         inc_nodes(src->children(), n);
-        std::uninitialized_copy(
-            src->children(), src->children() + n, dst->children());
+        std::copy(src->children(), src->children() + n, dst->children());
+        return dst;
+    }
+
+    static node_t*
+    move_inner_insert_value(edit_t e, node_t* src, bitmap_t bit, T v)
+    {
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto n                         = src->children_count();
+        auto nv                        = src->data_count();
+        auto offset                    = src->data_count(bit);
+        auto dst                       = make_inner_n(n, nv + 1);
+        dst->impl.d.data.inner.datamap = src->datamap() | bit;
+        dst->impl.d.data.inner.nodemap = src->nodemap();
+        IMMER_TRY {
+            auto mutate_values =
+                nv && can_mutate(src->impl.d.data.inner.values, e);
+            if (nv) {
+                if (mutate_values)
+                    detail::uninitialized_move(
+                        src->values(), src->values() + offset, dst->values());
+                else
+                    detail::uninitialized_copy(
+                        src->values(), src->values() + offset, dst->values());
+            }
+            IMMER_TRY {
+                new (dst->values() + offset) T{std::move(v)};
+                IMMER_TRY {
+                    if (nv) {
+                        if (mutate_values)
+                            detail::uninitialized_move(src->values() + offset,
+                                                       src->values() + nv,
+                                                       dst->values() + offset +
+                                                           1);
+                        else
+                            detail::uninitialized_copy(src->values() + offset,
+                                                       src->values() + nv,
+                                                       dst->values() + offset +
+                                                           1);
+                    }
+                }
+                IMMER_CATCH (...) {
+                    dst->values()[offset].~T();
+                    IMMER_RETHROW;
+                }
+            }
+            IMMER_CATCH (...) {
+                detail::destroy_n(dst->values(), offset);
+                IMMER_RETHROW;
+            }
+        }
+        IMMER_CATCH (...) {
+            deallocate_inner(dst, n, nv + 1);
+            IMMER_RETHROW;
+        }
+        std::copy(src->children(), src->children() + n, dst->children());
+        delete_inner(src);
         return dst;
     }
 
-    static node_t* make_merged(shift_t shift,
-                               T v1, hash_t hash1,
-                               T v2, hash_t hash2)
+    static node_t*
+    make_merged(shift_t shift, T v1, hash_t hash1, T v2, hash_t hash2)
     {
         if (shift < max_shift<B>) {
             auto idx1 = hash1 & (mask<B> << shift);
             auto idx2 = hash2 & (mask<B> << shift);
             if (idx1 == idx2) {
-                auto merged = make_merged(shift + B,
-                                          std::move(v1), hash1,
-                                          std::move(v2), hash2);
-                try {
-                    return make_inner_n(1, idx1 >> shift, merged);
-                } catch (...) {
+                auto merged = make_merged(
+                    shift + B, std::move(v1), hash1, std::move(v2), hash2);
+                IMMER_TRY {
+                    return make_inner_n(
+                        1, static_cast<count_t>(idx1 >> shift), merged);
+                }
+                IMMER_CATCH (...) {
                     delete_deep_shift(merged, shift + B);
-                    throw;
+                    IMMER_RETHROW;
                 }
             } else {
                 return make_inner_n(0,
-                                    idx1 >> shift, std::move(v1),
-                                    idx2 >> shift, std::move(v2));
+                                    static_cast<count_t>(idx1 >> shift),
+                                    std::move(v1),
+                                    static_cast<count_t>(idx2 >> shift),
+                                    std::move(v2));
             }
         } else {
             return make_collision(std::move(v1), std::move(v2));
         }
     }
 
+    static node_t* make_merged_e(
+        edit_t e, shift_t shift, T v1, hash_t hash1, T v2, hash_t hash2)
+    {
+        if (shift < max_shift<B>) {
+            auto idx1 = hash1 & (mask<B> << shift);
+            auto idx2 = hash2 & (mask<B> << shift);
+            if (idx1 == idx2) {
+                auto merged = make_merged_e(
+                    e, shift + B, std::move(v1), hash1, std::move(v2), hash2);
+                IMMER_TRY {
+                    return owned(
+                        make_inner_n(
+                            1, static_cast<count_t>(idx1 >> shift), merged),
+                        e);
+                }
+                IMMER_CATCH (...) {
+                    delete_deep_shift(merged, shift + B);
+                    IMMER_RETHROW;
+                }
+            } else {
+                auto r = make_inner_n(0,
+                                      static_cast<count_t>(idx1 >> shift),
+                                      std::move(v1),
+                                      static_cast<count_t>(idx2 >> shift),
+                                      std::move(v2));
+                return owned_values(r, e);
+            }
+        } else {
+            return owned(make_collision(std::move(v1), std::move(v2)), e);
+        }
+    }
+
     node_t* inc()
     {
         refs(this).inc();
@@ -632,7 +1052,6 @@ struct node
     }
 
     bool dec() const { return refs(this).dec(); }
-    void dec_unsafe() const { refs(this).dec_unsafe(); }
 
     static void inc_nodes(node_t** p, count_t n)
     {
@@ -643,24 +1062,26 @@ struct node
     static void delete_values(values_t* p, count_t n)
     {
         assert(p);
+        detail::destroy_n((T*) &p->d.buffer, n);
         deallocate_values(p, n);
     }
 
     static void delete_inner(node_t* p)
     {
         assert(p);
-        assert(p->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
         auto vp = p->impl.d.data.inner.values;
         if (vp && refs(vp).dec())
-            delete_values(vp, popcount(p->datamap()));
-        deallocate_inner(p, popcount(p->nodemap()));
+            delete_values(vp, p->data_count());
+        deallocate_inner(p, p->children_count());
     }
 
     static void delete_collision(node_t* p)
     {
         assert(p);
-        assert(p->kind() == kind_t::collision);
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::collision);
         auto n = p->collision_count();
+        detail::destroy_n(p->collisions(), n);
         deallocate_collision(p, n);
     }
 
@@ -670,7 +1091,7 @@ struct node
             delete_collision(p);
         else {
             auto fst = p->children();
-            auto lst = fst + popcount(p->nodemap());
+            auto lst = fst + p->children_count();
             for (; fst != lst; ++fst)
                 if ((*fst)->dec())
                     delete_deep(*fst, s + 1);
@@ -684,7 +1105,7 @@ struct node
             delete_collision(p);
         else {
             auto fst = p->children();
-            auto lst = fst + popcount(p->nodemap());
+            auto lst = fst + p->children_count();
             for (; fst != lst; ++fst)
                 if ((*fst)->dec())
                     delete_deep_shift(*fst, s + B);
@@ -694,13 +1115,11 @@ struct node
 
     static void deallocate_values(values_t* p, count_t n)
     {
-        destroy_n((T*) &p->d.buffer, n);
         heap::deallocate(node_t::sizeof_values_n(n), p);
     }
 
     static void deallocate_collision(node_t* p, count_t n)
     {
-        destroy_n(p->collisions(), n);
         heap::deallocate(node_t::sizeof_collision_n(n), p);
     }
 
@@ -712,7 +1131,8 @@ struct node
     static void deallocate_inner(node_t* p, count_t n, count_t nv)
     {
         assert(nv);
-        heap::deallocate(node_t::sizeof_values_n(nv), p->impl.d.data.inner.values);
+        heap::deallocate(node_t::sizeof_values_n(nv),
+                         p->impl.d.data.inner.values);
         heap::deallocate(node_t::sizeof_inner_n(n), p);
     }
 };
diff --git a/src/immer/detail/iterator_facade.hpp b/src/immer/detail/iterator_facade.hpp
index 985b2f17ae..1d5578c875 100644
--- a/src/immer/detail/iterator_facade.hpp
+++ b/src/immer/detail/iterator_facade.hpp
@@ -19,27 +19,39 @@ struct iterator_core_access
 {
     template <typename T>
     static decltype(auto) dereference(T&& x)
-    { return x.dereference(); }
+    {
+        return x.dereference();
+    }
 
     template <typename T>
     static decltype(auto) increment(T&& x)
-    { return x.increment(); }
+    {
+        return x.increment();
+    }
 
     template <typename T>
     static decltype(auto) decrement(T&& x)
-    { return x.decrement(); }
+    {
+        return x.decrement();
+    }
 
     template <typename T1, typename T2>
     static decltype(auto) equal(T1&& x1, T2&& x2)
-    { return x1.equal(x2); }
+    {
+        return x1.equal(x2);
+    }
 
     template <typename T, typename D>
     static decltype(auto) advance(T&& x, D d)
-    { return x.advance(d); }
+    {
+        return x.advance(d);
+    }
 
     template <typename T1, typename T2>
     static decltype(auto) distance_to(T1&& x1, T2&& x2)
-    { return x1.distance_to(x2); }
+    {
+        return x1.distance_to(x2);
+    }
 };
 
 /*!
@@ -48,16 +60,18 @@ struct iterator_core_access
 template <typename DerivedT,
           typename IteratorCategoryT,
           typename T,
-          typename ReferenceT = T&,
+          typename ReferenceT      = T&,
           typename DifferenceTypeT = std::ptrdiff_t,
-          typename PointerT = T*>
+          typename PointerT        = T*>
 class iterator_facade
-    : public std::iterator<IteratorCategoryT,
-                           T,
-                           DifferenceTypeT,
-                           PointerT,
-                           ReferenceT>
 {
+public:
+    using iterator_category = IteratorCategoryT;
+    using value_type        = T;
+    using difference_type   = DifferenceTypeT;
+    using pointer           = PointerT;
+    using reference         = ReferenceT;
+
 protected:
     using access_t = iterator_core_access;
 
@@ -68,17 +82,6 @@ class iterator_facade
         std::is_base_of<std::bidirectional_iterator_tag,
                         IteratorCategoryT>::value;
 
-    class reference_proxy
-    {
-        friend iterator_facade;
-        DerivedT iter_;
-
-        reference_proxy(DerivedT iter)
-            : iter_{std::move(iter)} {}
-    public:
-        operator ReferenceT() const { return *iter_; }
-    };
-
     const DerivedT& derived() const
     {
         static_assert(std::is_base_of<iterator_facade, DerivedT>::value,
@@ -93,27 +96,21 @@ class iterator_facade
     }
 
 public:
-    ReferenceT operator*() const
-    {
-        return access_t::dereference(derived());
-    }
-    PointerT operator->() const
-    {
-        return &access_t::dereference(derived());
-    }
-    reference_proxy operator[](DifferenceTypeT n) const
+    ReferenceT operator*() const { return access_t::dereference(derived()); }
+    PointerT operator->() const { return &access_t::dereference(derived()); }
+    ReferenceT operator[](DifferenceTypeT n) const
     {
         static_assert(is_random_access, "");
-        return derived() + n;
+        return *(derived() + n);
     }
 
-    bool operator==(const DerivedT& rhs) const
+    friend bool operator==(const DerivedT& a, const DerivedT& b)
     {
-        return access_t::equal(derived(), rhs);
+        return access_t::equal(a, b);
     }
-    bool operator!=(const DerivedT& rhs) const
+    friend bool operator!=(const DerivedT& a, const DerivedT& b)
     {
-        return !access_t::equal(derived(), rhs);
+        return !access_t::equal(a, b);
     }
 
     DerivedT& operator++()
@@ -170,31 +167,31 @@ class iterator_facade
         auto tmp = derived();
         return tmp -= n;
     }
-    DifferenceTypeT operator-(const DerivedT& rhs) const
+    friend DifferenceTypeT operator-(const DerivedT& a, const DerivedT& b)
     {
         static_assert(is_random_access, "");
-        return access_t::distance_to(rhs, derived());
+        return access_t::distance_to(b, a);
     }
 
-    bool operator<(const DerivedT& rhs) const
+    friend bool operator<(const DerivedT& a, const DerivedT& b)
     {
         static_assert(is_random_access, "");
-        return access_t::distance_to(derived(), rhs) > 0;
+        return access_t::distance_to(a, b) > 0;
     }
-    bool operator<=(const DerivedT& rhs) const
+    friend bool operator<=(const DerivedT& a, const DerivedT& b)
     {
         static_assert(is_random_access, "");
-        return access_t::distance_to(derived(), rhs) >= 0;
+        return access_t::distance_to(a, b) >= 0;
     }
-    bool operator>(const DerivedT& rhs) const
+    friend bool operator>(const DerivedT& a, const DerivedT& b)
     {
         static_assert(is_random_access, "");
-        return access_t::distance_to(derived(), rhs) < 0;
+        return access_t::distance_to(a, b) < 0;
     }
-    bool operator>=(const DerivedT& rhs) const
+    friend bool operator>=(const DerivedT& a, const DerivedT& b)
     {
         static_assert(is_random_access, "");
-        return access_t::distance_to(derived(), rhs) <= 0;
+        return access_t::distance_to(a, b) <= 0;
     }
 };
 
diff --git a/src/immer/detail/rbts/bits.hpp b/src/immer/detail/rbts/bits.hpp
index 549319ae79..f86b44fb01 100644
--- a/src/immer/detail/rbts/bits.hpp
+++ b/src/immer/detail/rbts/bits.hpp
@@ -8,6 +8,7 @@
 
 #pragma once
 
+#include <cstddef>
 #include <cstdint>
 
 namespace immer {
@@ -19,10 +20,10 @@ using shift_t = std::uint32_t;
 using count_t = std::uint32_t;
 using size_t  = std::size_t;
 
-template <bits_t B, typename T=count_t>
+template <bits_t B, typename T = count_t>
 constexpr T branches = T{1} << B;
 
-template <bits_t B, typename T=size_t>
+template <bits_t B, typename T = size_t>
 constexpr T mask = branches<B, T> - 1;
 
 template <bits_t B, bits_t BL>
diff --git a/src/immer/detail/rbts/node.hpp b/src/immer/detail/rbts/node.hpp
index 229b1d9d48..55a512b03d 100644
--- a/src/immer/detail/rbts/node.hpp
+++ b/src/immer/detail/rbts/node.hpp
@@ -8,30 +8,22 @@
 
 #pragma once
 
-#include <immer/heap/tags.hpp>
+#include <immer/config.hpp>
 #include <immer/detail/combine_standard_layout.hpp>
-#include <immer/detail/util.hpp>
 #include <immer/detail/rbts/bits.hpp>
+#include <immer/detail/util.hpp>
+#include <immer/heap/tags.hpp>
 
 #include <cassert>
 #include <cstddef>
 #include <memory>
 #include <type_traits>
 
-#ifdef NDEBUG
-#define IMMER_RBTS_TAGGED_NODE 0
-#else
-#define IMMER_RBTS_TAGGED_NODE 1
-#endif
-
 namespace immer {
 namespace detail {
 namespace rbts {
 
-template <typename T,
-          typename MemoryPolicy,
-          bits_t   B,
-          bits_t   BL>
+template <typename T, typename MemoryPolicy, bits_t B, bits_t BL>
 struct node
 {
     static constexpr auto bits      = B;
@@ -57,16 +49,13 @@ struct node
     struct relaxed_data_t
     {
         count_t count;
-        size_t  sizes[branches<B>];
+        size_t sizes[branches<B>];
     };
 
     using relaxed_data_with_meta_t =
-        combine_standard_layout_t<relaxed_data_t,
-                                  refs_t,
-                                  ownee_t>;
+        combine_standard_layout_t<relaxed_data_t, refs_t, ownee_t>;
 
-    using relaxed_data_no_meta_t =
-        combine_standard_layout_t<relaxed_data_t>;
+    using relaxed_data_no_meta_t = combine_standard_layout_t<relaxed_data_t>;
 
     using relaxed_t = std::conditional_t<embed_relaxed,
                                          relaxed_data_no_meta_t,
@@ -79,26 +68,25 @@ struct node
 
     struct inner_t
     {
-        relaxed_t*  relaxed;
+        relaxed_t* relaxed;
         aligned_storage_for<node_t*> buffer;
     };
 
     union data_t
     {
         inner_t inner;
-        leaf_t  leaf;
+        leaf_t leaf;
     };
 
     struct impl_data_t
     {
-#if IMMER_RBTS_TAGGED_NODE
+#if IMMER_TAGGED_NODE
         kind_t kind;
 #endif
         data_t data;
     };
 
-    using impl_t = combine_standard_layout_t<
-        impl_data_t, refs_t, ownee_t>;
+    using impl_t = combine_standard_layout_t<impl_data_t, refs_t, ownee_t>;
 
     impl_t impl;
 
@@ -109,30 +97,29 @@ struct node
 
     constexpr static std::size_t sizeof_packed_leaf_n(count_t count)
     {
-        return immer_offsetof(impl_t, d.data.leaf.buffer)
-            +  sizeof(leaf_t::buffer) * count;
+        return immer_offsetof(impl_t, d.data.leaf.buffer) +
+               sizeof(leaf_t::buffer) * count;
     }
 
     constexpr static std::size_t sizeof_packed_inner_n(count_t count)
     {
-        return immer_offsetof(impl_t, d.data.inner.buffer)
-            +  sizeof(inner_t::buffer) * count;
+        return immer_offsetof(impl_t, d.data.inner.buffer) +
+               sizeof(inner_t::buffer) * count;
     }
 
     constexpr static std::size_t sizeof_packed_relaxed_n(count_t count)
     {
-        return immer_offsetof(relaxed_t, d.sizes)
-            +  sizeof(size_t) * count;
+        return immer_offsetof(relaxed_t, d.sizes) + sizeof(size_t) * count;
     }
 
     constexpr static std::size_t sizeof_packed_inner_r_n(count_t count)
     {
-        return embed_relaxed
-            ? sizeof_packed_inner_n(count) + sizeof_packed_relaxed_n(count)
-            : sizeof_packed_inner_n(count);
+        return embed_relaxed ? sizeof_packed_inner_n(count) +
+                                   sizeof_packed_relaxed_n(count)
+                             : sizeof_packed_inner_n(count);
     }
 
-    constexpr static std::size_t max_sizeof_leaf  =
+    constexpr static std::size_t max_sizeof_leaf =
         sizeof_packed_leaf_n(branches<BL>);
 
     constexpr static std::size_t max_sizeof_inner =
@@ -145,78 +132,98 @@ struct node
         sizeof_packed_inner_r_n(branches<B>);
 
     constexpr static std::size_t sizeof_inner_n(count_t n)
-    { return keep_headroom ? max_sizeof_inner : sizeof_packed_inner_n(n); }
+    {
+        return keep_headroom ? max_sizeof_inner : sizeof_packed_inner_n(n);
+    }
 
     constexpr static std::size_t sizeof_inner_r_n(count_t n)
-    { return keep_headroom ? max_sizeof_inner_r : sizeof_packed_inner_r_n(n); }
+    {
+        return keep_headroom ? max_sizeof_inner_r : sizeof_packed_inner_r_n(n);
+    }
 
     constexpr static std::size_t sizeof_relaxed_n(count_t n)
-    { return keep_headroom ? max_sizeof_relaxed : sizeof_packed_relaxed_n(n); }
+    {
+        return keep_headroom ? max_sizeof_relaxed : sizeof_packed_relaxed_n(n);
+    }
 
     constexpr static std::size_t sizeof_leaf_n(count_t n)
-    { return keep_headroom ? max_sizeof_leaf : sizeof_packed_leaf_n(n); }
-
-    using heap = typename heap_policy::template
-        optimized<max_sizeof_inner>::type;
-
-#if IMMER_RBTS_TAGGED_NODE
-    kind_t kind() const
     {
-        return impl.d.kind;
+        return keep_headroom ? max_sizeof_leaf : sizeof_packed_leaf_n(n);
     }
+
+    using heap =
+        typename heap_policy::template optimized<max_sizeof_inner>::type;
+
+#if IMMER_TAGGED_NODE
+    kind_t kind() const { return impl.d.kind; }
 #endif
 
     relaxed_t* relaxed()
     {
-        assert(kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
         return impl.d.data.inner.relaxed;
     }
 
     const relaxed_t* relaxed() const
     {
-        assert(kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
         return impl.d.data.inner.relaxed;
     }
 
     node_t** inner()
     {
-        assert(kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
         return reinterpret_cast<node_t**>(&impl.d.data.inner.buffer);
     }
 
     T* leaf()
     {
-        assert(kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(kind() == kind_t::leaf);
         return reinterpret_cast<T*>(&impl.d.data.leaf.buffer);
     }
 
-    static refs_t& refs(const relaxed_t* x) { return auto_const_cast(get<refs_t>(*x)); }
+    static refs_t& refs(const relaxed_t* x)
+    {
+        return auto_const_cast(get<refs_t>(*x));
+    }
     static const ownee_t& ownee(const relaxed_t* x) { return get<ownee_t>(*x); }
     static ownee_t& ownee(relaxed_t* x) { return get<ownee_t>(*x); }
 
-    static refs_t& refs(const node_t* x) { return auto_const_cast(get<refs_t>(x->impl)); }
-    static const ownee_t& ownee(const node_t* x) { return get<ownee_t>(x->impl); }
+    static refs_t& refs(const node_t* x)
+    {
+        return auto_const_cast(get<refs_t>(x->impl));
+    }
+    static const ownee_t& ownee(const node_t* x)
+    {
+        return get<ownee_t>(x->impl);
+    }
     static ownee_t& ownee(node_t* x) { return get<ownee_t>(x->impl); }
 
-    static node_t* make_inner_n(count_t n)
+    static node_t* make_inner_n_into(void* buffer, std::size_t size, count_t n)
     {
         assert(n <= branches<B>);
-        auto m = heap::allocate(sizeof_inner_n(n));
-        auto p = new (m) node_t;
+        assert(size >= sizeof_inner_n(n));
+        auto p                       = new (buffer) node_t;
         p->impl.d.data.inner.relaxed = nullptr;
-#if IMMER_RBTS_TAGGED_NODE
+#if IMMER_TAGGED_NODE
         p->impl.d.kind = node_t::kind_t::inner;
 #endif
         return p;
     }
+    static node_t* make_inner_n(count_t n)
+    {
+        assert(n <= branches<B>);
+        auto m = heap::allocate(sizeof_inner_n(n));
+        return make_inner_n_into(m, sizeof_inner_n(n), n);
+    }
 
     static node_t* make_inner_e(edit_t e)
     {
-        auto m = heap::allocate(max_sizeof_inner);
-        auto p = new (m) node_t;
-        ownee(p) = e;
+        auto m                       = heap::allocate(max_sizeof_inner);
+        auto p                       = new (m) node_t;
+        ownee(p)                     = e;
         p->impl.d.data.inner.relaxed = nullptr;
-#if IMMER_RBTS_TAGGED_NODE
+#if IMMER_TAGGED_NODE
         p->impl.d.kind = node_t::kind_t::inner;
 #endif
         return p;
@@ -230,18 +237,19 @@ struct node
         if (embed_relaxed) {
             mr = reinterpret_cast<unsigned char*>(mp) + sizeof_inner_n(n);
         } else {
-            try {
+            IMMER_TRY {
                 mr = heap::allocate(sizeof_relaxed_n(n), norefs_tag{});
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 heap::deallocate(sizeof_inner_r_n(n), mp);
-                throw;
+                IMMER_RETHROW;
             }
         }
-        auto p = new (mp) node_t;
-        auto r = new (mr) relaxed_t;
-        r->d.count = 0;
+        auto p                       = new (mp) node_t;
+        auto r                       = new (mr) relaxed_t;
+        r->d.count                   = 0;
         p->impl.d.data.inner.relaxed = r;
-#if IMMER_RBTS_TAGGED_NODE
+#if IMMER_TAGGED_NODE
         p->impl.d.kind = node_t::kind_t::inner;
 #endif
         return p;
@@ -250,15 +258,14 @@ struct node
     static node_t* make_inner_sr_n(count_t n, relaxed_t* r)
     {
         return static_if<embed_relaxed, node_t*>(
-            [&] (auto) {
-                return node_t::make_inner_r_n(n);
-            },
-            [&] (auto) {
-                auto p = new (heap::allocate(node_t::sizeof_inner_r_n(n))) node_t;
+            [&](auto) { return node_t::make_inner_r_n(n); },
+            [&](auto) {
+                auto p =
+                    new (heap::allocate(node_t::sizeof_inner_r_n(n))) node_t;
                 assert(r->d.count >= n);
                 node_t::refs(r).inc();
                 p->impl.d.data.inner.relaxed = r;
-#if IMMER_RBTS_TAGGED_NODE
+#if IMMER_TAGGED_NODE
                 p->impl.d.kind = node_t::kind_t::inner;
 #endif
                 return p;
@@ -272,20 +279,21 @@ struct node
         if (embed_relaxed) {
             mr = reinterpret_cast<unsigned char*>(mp) + max_sizeof_inner;
         } else {
-            try {
+            IMMER_TRY {
                 mr = heap::allocate(max_sizeof_relaxed, norefs_tag{});
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 heap::deallocate(max_sizeof_inner_r, mp);
-                throw;
+                IMMER_RETHROW;
             }
         }
-        auto p = new (mp) node_t;
-        auto r = new (mr) relaxed_t;
+        auto p   = new (mp) node_t;
+        auto r   = new (mr) relaxed_t;
         ownee(p) = e;
-        static_if<!embed_relaxed>([&](auto){ node_t::ownee(r) = e; });
-        r->d.count = 0;
+        static_if<!embed_relaxed>([&](auto) { node_t::ownee(r) = e; });
+        r->d.count                   = 0;
         p->impl.d.data.inner.relaxed = r;
-#if IMMER_RBTS_TAGGED_NODE
+#if IMMER_TAGGED_NODE
         p->impl.d.kind = node_t::kind_t::inner;
 #endif
         return p;
@@ -294,36 +302,43 @@ struct node
     static node_t* make_inner_sr_e(edit_t e, relaxed_t* r)
     {
         return static_if<embed_relaxed, node_t*>(
-            [&] (auto) {
-                return node_t::make_inner_r_e(e);
-            },
-            [&] (auto) {
-                auto p = new (heap::allocate(node_t::max_sizeof_inner_r)) node_t;
+            [&](auto) { return node_t::make_inner_r_e(e); },
+            [&](auto) {
+                auto p =
+                    new (heap::allocate(node_t::max_sizeof_inner_r)) node_t;
                 node_t::refs(r).inc();
                 p->impl.d.data.inner.relaxed = r;
-                node_t::ownee(p) = e;
-#if IMMER_RBTS_TAGGED_NODE
+                node_t::ownee(p)             = e;
+#if IMMER_TAGGED_NODE
                 p->impl.d.kind = node_t::kind_t::inner;
 #endif
                 return p;
             });
     }
 
-    static node_t* make_leaf_n(count_t n)
+    static node_t* make_leaf_n_into(void* buffer, std::size_t size, count_t n)
     {
         assert(n <= branches<BL>);
-        auto p = new (heap::allocate(sizeof_leaf_n(n))) node_t;
-#if IMMER_RBTS_TAGGED_NODE
+        assert(size >= sizeof_leaf_n(n));
+        auto p = new (buffer) node_t;
+#if IMMER_TAGGED_NODE
         p->impl.d.kind = node_t::kind_t::leaf;
 #endif
         return p;
     }
 
+    static node_t* make_leaf_n(count_t n)
+    {
+        assert(n <= branches<BL>);
+        auto m = heap::allocate(sizeof_leaf_n(n));
+        return make_leaf_n_into(m, sizeof_leaf_n(n), n);
+    }
+
     static node_t* make_leaf_e(edit_t e)
     {
-        auto p = new (heap::allocate(max_sizeof_leaf)) node_t;
+        auto p   = new (heap::allocate(max_sizeof_leaf)) node_t;
         ownee(p) = e;
-#if IMMER_RBTS_TAGGED_NODE
+#if IMMER_TAGGED_NODE
         p->impl.d.kind = node_t::kind_t::leaf;
 #endif
         return p;
@@ -332,104 +347,111 @@ struct node
     static node_t* make_inner_n(count_t n, node_t* x)
     {
         assert(n >= 1);
-        auto p = make_inner_n(n);
-        p->inner() [0] = x;
+        auto p        = make_inner_n(n);
+        p->inner()[0] = x;
         return p;
     }
 
     static node_t* make_inner_n(edit_t n, node_t* x)
     {
         assert(n >= 1);
-        auto p = make_inner_n(n);
-        p->inner() [0] = x;
+        auto p        = make_inner_n(n);
+        p->inner()[0] = x;
         return p;
     }
 
     static node_t* make_inner_n(count_t n, node_t* x, node_t* y)
     {
         assert(n >= 2);
-        auto p = make_inner_n(n);
-        p->inner() [0] = x;
-        p->inner() [1] = y;
+        auto p        = make_inner_n(n);
+        p->inner()[0] = x;
+        p->inner()[1] = y;
         return p;
     }
 
     static node_t* make_inner_r_n(count_t n, node_t* x)
     {
         assert(n >= 1);
-        auto p = make_inner_r_n(n);
-        auto r = p->relaxed();
-        p->inner() [0] = x;
-        r->d.count = 1;
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        r->d.count    = 1;
         return p;
     }
 
     static node_t* make_inner_r_n(count_t n, node_t* x, size_t xs)
     {
         assert(n >= 1);
-        auto p = make_inner_r_n(n);
-        auto r = p->relaxed();
-        p->inner() [0] = x;
-        r->d.sizes [0] = xs;
-        r->d.count = 1;
+        assert(xs);
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        r->d.sizes[0] = xs;
+        r->d.count    = 1;
         return p;
     }
 
     static node_t* make_inner_r_n(count_t n, node_t* x, node_t* y)
     {
         assert(n >= 2);
-        auto p = make_inner_r_n(n);
-        auto r = p->relaxed();
-        p->inner() [0] = x;
-        p->inner() [1] = y;
-        r->d.count = 2;
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        p->inner()[1] = y;
+        r->d.count    = 2;
         return p;
     }
 
-    static node_t* make_inner_r_n(count_t n,
-                                  node_t* x, size_t xs,
-                                  node_t* y)
+    static node_t* make_inner_r_n(count_t n, node_t* x, size_t xs, node_t* y)
     {
         assert(n >= 2);
-        auto p = make_inner_r_n(n);
-        auto r = p->relaxed();
-        p->inner() [0] = x;
-        p->inner() [1] = y;
-        r->d.sizes [0] = xs;
-        r->d.count = 2;
+        assert(xs);
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        p->inner()[1] = y;
+        r->d.sizes[0] = xs;
+        r->d.count    = 2;
         return p;
     }
 
-    static node_t* make_inner_r_n(count_t n,
-                                  node_t* x, size_t xs,
-                                  node_t* y, size_t ys)
+    static node_t*
+    make_inner_r_n(count_t n, node_t* x, size_t xs, node_t* y, size_t ys)
     {
         assert(n >= 2);
-        auto p = make_inner_r_n(n);
-        auto r = p->relaxed();
-        p->inner() [0] = x;
-        p->inner() [1] = y;
-        r->d.sizes [0] = xs;
-        r->d.sizes [1] = xs + ys;
-        r->d.count = 2;
+        assert(xs);
+        assert(ys);
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        p->inner()[1] = y;
+        r->d.sizes[0] = xs;
+        r->d.sizes[1] = xs + ys;
+        r->d.count    = 2;
         return p;
     }
 
     static node_t* make_inner_r_n(count_t n,
-                                  node_t* x, size_t xs,
-                                  node_t* y, size_t ys,
-                                  node_t* z, size_t zs)
+                                  node_t* x,
+                                  size_t xs,
+                                  node_t* y,
+                                  size_t ys,
+                                  node_t* z,
+                                  size_t zs)
     {
         assert(n >= 3);
-        auto p = make_inner_r_n(n);
-        auto r = p->relaxed();
-        p->inner() [0] = x;
-        p->inner() [1] = y;
-        p->inner() [2] = z;
-        r->d.sizes [0] = xs;
-        r->d.sizes [1] = xs + ys;
-        r->d.sizes [2] = xs + ys + zs;
-        r->d.count = 3;
+        assert(xs);
+        assert(ys);
+        assert(zs);
+        auto p        = make_inner_r_n(n);
+        auto r        = p->relaxed();
+        p->inner()[0] = x;
+        p->inner()[1] = y;
+        p->inner()[2] = z;
+        r->d.sizes[0] = xs;
+        r->d.sizes[1] = xs + ys;
+        r->d.sizes[2] = xs + ys + zs;
+        r->d.count    = 3;
         return p;
     }
 
@@ -438,11 +460,12 @@ struct node
     {
         assert(n >= 1);
         auto p = make_leaf_n(n);
-        try {
-            new (p->leaf()) T{ std::forward<U>(x) };
-        } catch (...) {
+        IMMER_TRY {
+            new (p->leaf()) T{std::forward<U>(x)};
+        }
+        IMMER_CATCH (...) {
             heap::deallocate(node_t::sizeof_leaf_n(n), p);
-            throw;
+            IMMER_RETHROW;
         }
         return p;
     }
@@ -451,27 +474,29 @@ struct node
     static node_t* make_leaf_e(edit_t e, U&& x)
     {
         auto p = make_leaf_e(e);
-        try {
-            new (p->leaf()) T{ std::forward<U>(x) };
-        } catch (...) {
+        IMMER_TRY {
+            new (p->leaf()) T{std::forward<U>(x)};
+        }
+        IMMER_CATCH (...) {
             heap::deallocate(node_t::max_sizeof_leaf, p);
-            throw;
+            IMMER_RETHROW;
         }
         return p;
     }
 
     static node_t* make_path(shift_t shift, node_t* node)
     {
-        assert(node->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(node->kind() == kind_t::leaf);
         if (shift == endshift<B, BL>)
             return node;
         else {
             auto n = node_t::make_inner_n(1);
-            try {
-                n->inner() [0] = make_path(shift - B, node);
-            } catch (...) {
+            IMMER_TRY {
+                n->inner()[0] = make_path(shift - B, node);
+            }
+            IMMER_CATCH (...) {
                 heap::deallocate(node_t::sizeof_inner_n(1), n);
-                throw;
+                IMMER_RETHROW;
             }
             return n;
         }
@@ -479,16 +504,17 @@ struct node
 
     static node_t* make_path_e(edit_t e, shift_t shift, node_t* node)
     {
-        assert(node->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(node->kind() == kind_t::leaf);
         if (shift == endshift<B, BL>)
             return node;
         else {
             auto n = node_t::make_inner_e(e);
-            try {
-                n->inner() [0] = make_path_e(e, shift - B, node);
-            } catch (...) {
+            IMMER_TRY {
+                n->inner()[0] = make_path_e(e, shift - B, node);
+            }
+            IMMER_CATCH (...) {
                 heap::deallocate(node_t::max_sizeof_inner, n);
-                throw;
+                IMMER_RETHROW;
             }
             return n;
         }
@@ -496,41 +522,54 @@ struct node
 
     static node_t* copy_inner(node_t* src, count_t n)
     {
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         auto dst = make_inner_n(n);
         inc_nodes(src->inner(), n);
-        std::uninitialized_copy(src->inner(), src->inner() + n, dst->inner());
+        std::copy(src->inner(), src->inner() + n, dst->inner());
         return dst;
     }
 
     static node_t* copy_inner_n(count_t allocn, node_t* src, count_t n)
     {
         assert(allocn >= n);
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         auto dst = make_inner_n(allocn);
         return do_copy_inner(dst, src, n);
     }
 
     static node_t* copy_inner_e(edit_t e, node_t* src, count_t n)
     {
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         auto dst = make_inner_e(e);
         return do_copy_inner(dst, src, n);
     }
 
     static node_t* do_copy_inner(node_t* dst, node_t* src, count_t n)
     {
-        assert(dst->kind() == kind_t::inner);
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(dst->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         auto p = src->inner();
         inc_nodes(p, n);
-        std::uninitialized_copy(p, p + n, dst->inner());
+        std::copy(p, p + n, dst->inner());
+        return dst;
+    }
+
+    static node_t* do_copy_inner_replace(
+        node_t* dst, node_t* src, count_t n, count_t offset, node_t* child)
+    {
+        IMMER_ASSERT_TAGGED(dst->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto p = src->inner();
+        inc_nodes(p, offset);
+        inc_nodes(p + offset + 1, n - offset - 1);
+        std::copy(p, p + n, dst->inner());
+        dst->inner()[offset] = child;
         return dst;
     }
 
     static node_t* copy_inner_r(node_t* src, count_t n)
     {
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         auto dst = make_inner_r_n(n);
         return do_copy_inner_r(dst, src, n);
     }
@@ -538,29 +577,29 @@ struct node
     static node_t* copy_inner_r_n(count_t allocn, node_t* src, count_t n)
     {
         assert(allocn >= n);
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         auto dst = make_inner_r_n(allocn);
         return do_copy_inner_r(dst, src, n);
     }
 
     static node_t* copy_inner_r_e(edit_t e, node_t* src, count_t n)
     {
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         auto dst = make_inner_r_e(e);
         return do_copy_inner_r(dst, src, n);
     }
 
     static node_t* copy_inner_sr_e(edit_t e, node_t* src, count_t n)
     {
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         auto dst = make_inner_sr_e(e, src->relaxed());
         return do_copy_inner_sr(dst, src, n);
     }
 
     static node_t* do_copy_inner_r(node_t* dst, node_t* src, count_t n)
     {
-        assert(dst->kind() == kind_t::inner);
-        assert(src->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(dst->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
         auto src_r = src->relaxed();
         auto dst_r = dst->relaxed();
         inc_nodes(src->inner(), n);
@@ -570,6 +609,23 @@ struct node
         return dst;
     }
 
+    static node_t* do_copy_inner_replace_r(
+        node_t* dst, node_t* src, count_t n, count_t offset, node_t* child)
+    {
+        IMMER_ASSERT_TAGGED(dst->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
+        auto src_r = src->relaxed();
+        auto dst_r = dst->relaxed();
+        auto p     = src->inner();
+        inc_nodes(p, offset);
+        inc_nodes(p + offset + 1, n - offset - 1);
+        std::copy(src->inner(), src->inner() + n, dst->inner());
+        std::copy(src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes);
+        dst_r->d.count       = n;
+        dst->inner()[offset] = child;
+        return dst;
+    }
+
     static node_t* do_copy_inner_sr(node_t* dst, node_t* src, count_t n)
     {
         if (embed_relaxed)
@@ -581,28 +637,47 @@ struct node
         }
     }
 
+    static node_t* do_copy_inner_replace_sr(
+        node_t* dst, node_t* src, count_t n, count_t offset, node_t* child)
+    {
+        if (embed_relaxed)
+            return do_copy_inner_replace_r(dst, src, n, offset, child);
+        else {
+            auto p = src->inner();
+            inc_nodes(p, offset);
+            inc_nodes(p + offset + 1, n - offset - 1);
+            std::copy(p, p + n, dst->inner());
+            dst->inner()[offset] = child;
+            return dst;
+        }
+    }
+
     static node_t* copy_leaf(node_t* src, count_t n)
     {
-        assert(src->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
         auto dst = make_leaf_n(n);
-        try {
-            std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf());
-        } catch (...) {
+        IMMER_TRY {
+            detail::uninitialized_copy(
+                src->leaf(), src->leaf() + n, dst->leaf());
+        }
+        IMMER_CATCH (...) {
             heap::deallocate(node_t::sizeof_leaf_n(n), dst);
-            throw;
+            IMMER_RETHROW;
         }
         return dst;
     }
 
     static node_t* copy_leaf_e(edit_t e, node_t* src, count_t n)
     {
-        assert(src->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
         auto dst = make_leaf_e(e);
-        try {
-            std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf());
-        } catch (...) {
+        IMMER_TRY {
+            detail::uninitialized_copy(
+                src->leaf(), src->leaf() + n, dst->leaf());
+        }
+        IMMER_CATCH (...) {
             heap::deallocate(node_t::max_sizeof_leaf, dst);
-            throw;
+            IMMER_RETHROW;
         }
         return dst;
     }
@@ -610,90 +685,96 @@ struct node
     static node_t* copy_leaf_n(count_t allocn, node_t* src, count_t n)
     {
         assert(allocn >= n);
-        assert(src->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
         auto dst = make_leaf_n(allocn);
-        try {
-            std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf());
-        } catch (...) {
+        IMMER_TRY {
+            detail::uninitialized_copy(
+                src->leaf(), src->leaf() + n, dst->leaf());
+        }
+        IMMER_CATCH (...) {
             heap::deallocate(node_t::sizeof_leaf_n(allocn), dst);
-            throw;
+            IMMER_RETHROW;
         }
         return dst;
     }
 
-    static node_t* copy_leaf(node_t* src1, count_t n1,
-                             node_t* src2, count_t n2)
+    static node_t* copy_leaf(node_t* src1, count_t n1, node_t* src2, count_t n2)
     {
-        assert(src1->kind() == kind_t::leaf);
-        assert(src2->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(src1->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(src2->kind() == kind_t::leaf);
         auto dst = make_leaf_n(n1 + n2);
-        try {
-            std::uninitialized_copy(
+        IMMER_TRY {
+            detail::uninitialized_copy(
                 src1->leaf(), src1->leaf() + n1, dst->leaf());
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             heap::deallocate(node_t::sizeof_leaf_n(n1 + n2), dst);
-            throw;
+            IMMER_RETHROW;
         }
-        try {
-            std::uninitialized_copy(
+        IMMER_TRY {
+            detail::uninitialized_copy(
                 src2->leaf(), src2->leaf() + n2, dst->leaf() + n1);
-        } catch (...) {
-            destroy_n(dst->leaf(), n1);
+        }
+        IMMER_CATCH (...) {
+            detail::destroy_n(dst->leaf(), n1);
             heap::deallocate(node_t::sizeof_leaf_n(n1 + n2), dst);
-            throw;
+            IMMER_RETHROW;
         }
         return dst;
     }
 
-    static node_t* copy_leaf_e(edit_t e,
-                               node_t* src1, count_t n1,
-                               node_t* src2, count_t n2)
+    static node_t*
+    copy_leaf_e(edit_t e, node_t* src1, count_t n1, node_t* src2, count_t n2)
     {
-        assert(src1->kind() == kind_t::leaf);
-        assert(src2->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(src1->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(src2->kind() == kind_t::leaf);
         auto dst = make_leaf_e(e);
-        try {
-            std::uninitialized_copy(
+        IMMER_TRY {
+            detail::uninitialized_copy(
                 src1->leaf(), src1->leaf() + n1, dst->leaf());
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             heap::deallocate(max_sizeof_leaf, dst);
-            throw;
+            IMMER_RETHROW;
         }
-        try {
-            std::uninitialized_copy(
+        IMMER_TRY {
+            detail::uninitialized_copy(
                 src2->leaf(), src2->leaf() + n2, dst->leaf() + n1);
-        } catch (...) {
-            destroy_n(dst->leaf(), n1);
+        }
+        IMMER_CATCH (...) {
+            detail::destroy_n(dst->leaf(), n1);
             heap::deallocate(max_sizeof_leaf, dst);
-            throw;
+            IMMER_RETHROW;
         }
         return dst;
     }
 
     static node_t* copy_leaf_e(edit_t e, node_t* src, count_t idx, count_t last)
     {
-        assert(src->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
         auto dst = make_leaf_e(e);
-        try {
-            std::uninitialized_copy(
+        IMMER_TRY {
+            detail::uninitialized_copy(
                 src->leaf() + idx, src->leaf() + last, dst->leaf());
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             heap::deallocate(max_sizeof_leaf, dst);
-            throw;
+            IMMER_RETHROW;
         }
         return dst;
     }
 
     static node_t* copy_leaf(node_t* src, count_t idx, count_t last)
     {
-        assert(src->kind() == kind_t::leaf);
+        IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
         auto dst = make_leaf_n(last - idx);
-        try {
-            std::uninitialized_copy(
+        IMMER_TRY {
+            detail::uninitialized_copy(
                 src->leaf() + idx, src->leaf() + last, dst->leaf());
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             heap::deallocate(node_t::sizeof_leaf_n(last - idx), dst);
-            throw;
+            IMMER_RETHROW;
         }
         return dst;
     }
@@ -702,28 +783,29 @@ struct node
     static node_t* copy_leaf_emplace(node_t* src, count_t n, U&& x)
     {
         auto dst = copy_leaf_n(n + 1, src, n);
-        try {
+        IMMER_TRY {
             new (dst->leaf() + n) T{std::forward<U>(x)};
-        } catch (...) {
-            destroy_n(dst->leaf(), n);
+        }
+        IMMER_CATCH (...) {
+            detail::destroy_n(dst->leaf(), n);
             heap::deallocate(node_t::sizeof_leaf_n(n + 1), dst);
-            throw;
+            IMMER_RETHROW;
         }
         return dst;
     }
 
     static void delete_inner(node_t* p, count_t n)
     {
-        assert(p->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
         assert(!p->relaxed());
-        heap::deallocate(ownee(p).owned()
-                         ? node_t::max_sizeof_inner
-                         : node_t::sizeof_inner_n(n), p);
+        heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_inner
+                                          : node_t::sizeof_inner_n(n),
+                         p);
     }
 
     static void delete_inner_e(node_t* p)
     {
-        assert(p->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
         assert(!p->relaxed());
         heap::deallocate(node_t::max_sizeof_inner, p);
     }
@@ -738,26 +820,27 @@ struct node
 
     static void delete_inner_r(node_t* p, count_t n)
     {
-        assert(p->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
         auto r = p->relaxed();
         assert(r);
-        static_if<!embed_relaxed>([&] (auto) {
+        static_if<!embed_relaxed>([&](auto) {
             if (node_t::refs(r).dec())
                 heap::deallocate(node_t::ownee(r).owned()
-                                 ? node_t::max_sizeof_relaxed
-                                 : node_t::sizeof_relaxed_n(n), r);
+                                     ? node_t::max_sizeof_relaxed
+                                     : node_t::sizeof_relaxed_n(n),
+                                 r);
         });
-        heap::deallocate(ownee(p).owned()
-                         ? node_t::max_sizeof_inner_r
-                         : node_t::sizeof_inner_r_n(n), p);
+        heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_inner_r
+                                          : node_t::sizeof_inner_r_n(n),
+                         p);
     }
 
     static void delete_inner_r_e(node_t* p)
     {
-        assert(p->kind() == kind_t::inner);
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
         auto r = p->relaxed();
         assert(r);
-        static_if<!embed_relaxed>([&] (auto) {
+        static_if<!embed_relaxed>([&](auto) {
             if (node_t::refs(r).dec())
                 heap::deallocate(node_t::max_sizeof_relaxed, r);
         });
@@ -766,38 +849,40 @@ struct node
 
     static void delete_leaf(node_t* p, count_t n)
     {
-        assert(p->kind() == kind_t::leaf);
-        destroy_n(p->leaf(), n);
-        heap::deallocate(ownee(p).owned()
-                         ? node_t::max_sizeof_leaf
-                         : node_t::sizeof_leaf_n(n), p);
+        IMMER_ASSERT_TAGGED(p->kind() == kind_t::leaf);
+        detail::destroy_n(p->leaf(), n);
+        heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_leaf
+                                          : node_t::sizeof_leaf_n(n),
+                         p);
     }
 
     bool can_mutate(edit_t e) const
     {
-        return refs(this).unique()
-            || ownee(this).can_mutate(e);
+        return refs(this).unique() || ownee(this).can_mutate(e);
     }
 
-    bool can_relax() const
-    {
-        return !embed_relaxed || relaxed();
-    }
+    bool can_relax() const { return !embed_relaxed || relaxed(); }
 
     relaxed_t* ensure_mutable_relaxed(edit_t e)
     {
         auto src_r = relaxed();
         return static_if<embed_relaxed, relaxed_t*>(
-            [&] (auto) { return src_r; },
-            [&] (auto) {
+            [&](auto) { return src_r; },
+            [&](auto) {
                 if (node_t::refs(src_r).unique() ||
                     node_t::ownee(src_r).can_mutate(e))
                     return src_r;
                 else {
-                    if (src_r)
-                        node_t::refs(src_r).dec_unsafe();
                     auto dst_r = impl.d.data.inner.relaxed =
                         new (heap::allocate(max_sizeof_relaxed)) relaxed_t;
+                    if (src_r) {
+                        auto n = dst_r->d.count = src_r->d.count;
+                        std::copy(
+                            src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes);
+                        if (node_t::refs(src_r).dec())
+                            heap::deallocate(node_t::sizeof_inner_r_n(n),
+                                             src_r);
+                    }
                     node_t::ownee(dst_r) = e;
                     return dst_r;
                 }
@@ -808,17 +893,23 @@ struct node
     {
         auto src_r = relaxed();
         return static_if<embed_relaxed, relaxed_t*>(
-            [&] (auto) { return src_r; },
-            [&] (auto) {
+            [&](auto) { return src_r; },
+            [&](auto) {
                 if (src_r && (node_t::refs(src_r).unique() ||
                               node_t::ownee(src_r).can_mutate(e))) {
                     node_t::ownee(src_r) = ec;
                     return src_r;
                 } else {
-                    if (src_r)
-                        node_t::refs(src_r).dec_unsafe();
                     auto dst_r = impl.d.data.inner.relaxed =
                         new (heap::allocate(max_sizeof_relaxed)) relaxed_t;
+                    if (src_r) {
+                        auto n = dst_r->d.count = src_r->d.count;
+                        std::copy(
+                            src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes);
+                        if (node_t::refs(src_r).dec())
+                            heap::deallocate(node_t::sizeof_inner_r_n(n),
+                                             src_r);
+                    }
                     node_t::ownee(dst_r) = ec;
                     return dst_r;
                 }
@@ -829,18 +920,23 @@ struct node
     {
         auto src_r = relaxed();
         return static_if<embed_relaxed, relaxed_t*>(
-            [&] (auto) { return src_r; },
-            [&] (auto) {
+            [&](auto) { return src_r; },
+            [&](auto) {
                 if (node_t::refs(src_r).unique() ||
                     node_t::ownee(src_r).can_mutate(e))
                     return src_r;
                 else {
-                    if (src_r)
-                        node_t::refs(src_r).dec_unsafe();
                     auto dst_r =
                         new (heap::allocate(max_sizeof_relaxed)) relaxed_t;
-                    std::copy(src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes);
-                    node_t::ownee(dst_r) = e;
+                    if (src_r) {
+                        std::copy(
+                            src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes);
+                        if (node_t::refs(src_r).dec())
+                            heap::deallocate(node_t::sizeof_inner_r_n(n),
+                                             src_r);
+                    }
+                    dst_r->d.count                   = n;
+                    node_t::ownee(dst_r)             = e;
                     return impl.d.data.inner.relaxed = dst_r;
                 }
             });
@@ -859,7 +955,6 @@ struct node
     }
 
     bool dec() const { return refs(this).dec(); }
-    void dec_unsafe() const { refs(this).dec_unsafe(); }
 
     static void inc_nodes(node_t** p, count_t n)
     {
@@ -867,13 +962,13 @@ struct node
             refs(*i).inc();
     }
 
-#if IMMER_RBTS_TAGGED_NODE
+#if IMMER_TAGGED_NODE
     shift_t compute_shift()
     {
         if (kind() == kind_t::leaf)
             return endshift<B, BL>;
         else
-            return B + inner() [0]->compute_shift();
+            return B + inner()[0]->compute_shift();
     }
 #endif
 
@@ -882,7 +977,7 @@ struct node
 #if IMMER_DEBUG_DEEP_CHECK
         assert(size > 0);
         if (shift == endshift<B, BL>) {
-            assert(kind() == kind_t::leaf);
+            IMMER_ASSERT_TAGGED(kind() == kind_t::leaf);
             assert(size <= branches<BL>);
         } else if (auto r = relaxed()) {
             auto count = r->d.count;
@@ -894,28 +989,23 @@ struct node
                 IMMER_TRACE_E(size);
             }
             assert(r->d.sizes[count - 1] == size);
-            for (auto i = 1; i < count; ++i)
+            for (auto i = 1u; i < count; ++i)
                 assert(r->d.sizes[i - 1] < r->d.sizes[i]);
             auto last_size = size_t{};
-            for (auto i = 0; i < count; ++i) {
-                assert(inner()[i]->check(
-                           shift - B,
-                           r->d.sizes[i] - last_size));
+            for (auto i = 0u; i < count; ++i) {
+                assert(inner()[i]->check(shift - B, r->d.sizes[i] - last_size));
                 last_size = r->d.sizes[i];
             }
         } else {
             assert(size <= branches<B> << shift);
-            auto count = (size >> shift)
-                + (size - ((size >> shift) << shift) > 0);
+            auto count =
+                (size >> shift) + (size - ((size >> shift) << shift) > 0);
             assert(count <= branches<B>);
             if (count) {
-                for (auto i = 1; i < count - 1; ++i)
-                    assert(inner()[i]->check(
-                               shift - B,
-                               1 << shift));
+                for (auto i = 1u; i < count - 1; ++i)
+                    assert(inner()[i]->check(shift - B, 1 << shift));
                 assert(inner()[count - 1]->check(
-                           shift - B,
-                           size - ((count - 1) << shift)));
+                    shift - B, size - ((count - 1) << shift)));
             }
         }
 #endif // IMMER_DEBUG_DEEP_CHECK
@@ -926,11 +1016,12 @@ struct node
 template <typename T, typename MP, bits_t B>
 constexpr bits_t derive_bits_leaf_aux()
 {
-    using node_t = node<T, MP, B, B>;
+    using node_t               = node<T, MP, B, B>;
     constexpr auto sizeof_elem = sizeof(T);
-    constexpr auto space = node_t::max_sizeof_inner - node_t::sizeof_packed_leaf_n(0);
+    constexpr auto space =
+        node_t::max_sizeof_inner - node_t::sizeof_packed_leaf_n(0);
     constexpr auto full_elems = space / sizeof_elem;
-    constexpr auto BL = log2(full_elems);
+    constexpr auto BL         = log2(full_elems);
     return BL;
 }
 
diff --git a/src/immer/detail/rbts/operations.hpp b/src/immer/detail/rbts/operations.hpp
index 17ec9911ef..d5ff67934b 100644
--- a/src/immer/detail/rbts/operations.hpp
+++ b/src/immer/detail/rbts/operations.hpp
@@ -14,10 +14,10 @@
 #include <utility>
 
 #include <immer/config.hpp>
-#include <immer/heap/tags.hpp>
-#include <immer/detail/util.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 {
@@ -30,26 +30,34 @@ struct array_for_visitor : visitor_base<array_for_visitor<T>>
 
     template <typename PosT>
     static T* visit_inner(PosT&& pos, size_t idx)
-    { return pos.descend(this_t{}, idx); }
+    {
+        return pos.descend(this_t{}, idx);
+    }
 
     template <typename PosT>
     static T* visit_leaf(PosT&& pos, size_t)
-    { return pos.node()->leaf(); }
+    {
+        return pos.node()->leaf();
+    }
 };
 
 template <typename T>
 struct region_for_visitor : visitor_base<region_for_visitor<T>>
 {
-    using this_t = region_for_visitor;
+    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); }
+    {
+        return pos.towards(this_t{}, idx);
+    }
 
     template <typename PosT>
     static result_t visit_leaf(PosT&& pos, size_t idx)
-    { return { pos.node()->leaf(), pos.index(idx), pos.count() }; }
+    {
+        return std::make_tuple(pos.node()->leaf(), pos.index(idx), pos.count());
+    }
 };
 
 template <typename T>
@@ -59,11 +67,15 @@ struct get_visitor : visitor_base<get_visitor<T>>
 
     template <typename PosT>
     static const T& visit_inner(PosT&& pos, size_t idx)
-    { return pos.descend(this_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)]; }
+    {
+        return pos.node()->leaf()[pos.index(idx)];
+    }
 };
 
 struct for_each_chunk_visitor : visitor_base<for_each_chunk_visitor>
@@ -72,13 +84,15 @@ struct for_each_chunk_visitor : visitor_base<for_each_chunk_visitor>
 
     template <typename Pos, typename Fn>
     static void visit_inner(Pos&& pos, Fn&& fn)
-    { pos.each(this_t{}, 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());
+        fn(as_const(data), as_const(data) + pos.count());
     }
 };
 
@@ -88,12 +102,14 @@ struct for_each_chunk_p_visitor : visitor_base<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); }
+    {
+        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();
+        auto data = as_const(pos.node()->leaf());
         return fn(data, data + pos.count());
     }
 };
@@ -103,8 +119,7 @@ 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)
+    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);
@@ -112,12 +127,10 @@ struct for_each_chunk_left_visitor : visitor_base<for_each_chunk_left_visitor>
     }
 
     template <typename Pos, typename Fn>
-    static void visit_leaf(Pos&& pos,
-                           size_t last,
-                           Fn&& fn)
+    static void visit_leaf(Pos&& pos, size_t last, Fn&& fn)
     {
         auto data = pos.node()->leaf();
-        auto l = pos.index(last);
+        auto l    = pos.index(last);
         fn(data, data + l + 1);
     }
 };
@@ -127,8 +140,7 @@ 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)
+    static void visit_inner(Pos&& pos, size_t first, Fn&& fn)
     {
         auto f = pos.index(first);
         pos.towards_oh(this_t{}, first, f, fn);
@@ -136,12 +148,10 @@ struct for_each_chunk_right_visitor : visitor_base<for_each_chunk_right_visitor>
     }
 
     template <typename Pos, typename Fn>
-    static void visit_leaf(Pos&& pos,
-                           size_t first,
-                           Fn&& fn)
+    static void visit_leaf(Pos&& pos, size_t first, Fn&& fn)
     {
         auto data = pos.node()->leaf();
-        auto f = pos.index(first);
+        auto f    = pos.index(first);
         fn(data + f, data + pos.count());
     }
 };
@@ -151,13 +161,11 @@ 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)
+    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)  {
+        if (first < last) {
             auto f = pos.index(first);
             auto l = pos.index(last - 1);
             if (f == l) {
@@ -173,11 +181,9 @@ struct for_each_chunk_i_visitor : visitor_base<for_each_chunk_i_visitor>
     }
 
     template <typename Pos, typename Fn>
-    static void visit_regular(Pos&& pos,
-                              size_t first, size_t last,
-                              Fn&& fn)
+    static void visit_regular(Pos&& pos, size_t first, size_t last, Fn&& fn)
     {
-        if (first < last)  {
+        if (first < last) {
             auto f = pos.index(first);
             auto l = pos.index(last - 1);
             if (f == l)
@@ -192,9 +198,7 @@ struct for_each_chunk_i_visitor : visitor_base<for_each_chunk_i_visitor>
     }
 
     template <typename Pos, typename Fn>
-    static void visit_leaf(Pos&& pos,
-                           size_t first, size_t last,
-                           Fn&& fn)
+    static void visit_leaf(Pos&& pos, size_t first, size_t last, Fn&& fn)
     {
         auto data = pos.node()->leaf();
         if (first < last) {
@@ -211,21 +215,18 @@ struct 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)
+    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);
+        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)
+    static bool visit_leaf(Pos&& pos, size_t last, Fn&& fn)
     {
         auto data = pos.node()->leaf();
-        auto l = pos.index(last);
+        auto l    = pos.index(last);
         return fn(data, data + l + 1);
     }
 };
@@ -236,21 +237,18 @@ struct 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)
+    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);
+        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)
+    static bool visit_leaf(Pos&& pos, size_t first, Fn&& fn)
     {
         auto data = pos.node()->leaf();
-        auto f = pos.index(first);
+        auto f    = pos.index(first);
         return fn(data + f, data + pos.count());
     }
 };
@@ -260,52 +258,53 @@ 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)
+    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)  {
+        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);
+                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 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)
+    static bool visit_regular(Pos&& pos, size_t first, size_t last, Fn&& fn)
     {
-        if (first < last)  {
+        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 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)
+    static bool visit_leaf(Pos&& pos, size_t first, size_t last, Fn&& fn)
     {
         auto data = pos.node()->leaf();
         if (first < last) {
@@ -323,37 +322,48 @@ struct equals_visitor : visitor_base<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); }
+        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)
+        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);
+                       ? 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 {
+        return [iter](auto f, auto e) mutable {
             if (f == &*iter) {
                 iter += e - f;
                 return true;
@@ -366,8 +376,8 @@ struct equals_visitor : visitor_base<equals_visitor>
     }
 
     template <typename PosL, typename PosR, typename Iter>
-    static bool visit_relaxed(PosL&& posl, PosR&& posr,
-                              Iter&& first, size_t idx)
+    static bool
+    visit_relaxed(PosL&& posl, PosR&& posr, Iter&& first, size_t idx)
     {
         auto nl = posl.node();
         auto nr = posr.node();
@@ -377,16 +387,20 @@ struct equals_visitor : visitor_base<equals_visitor>
         auto cr = posr.count();
         assert(cr > 0);
         auto sbr = size_t{};
-        auto i = count_t{};
-        auto j = count_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;
+            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;
     }
@@ -403,43 +417,40 @@ struct equals_visitor : visitor_base<equals_visitor>
     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());
+                   ? 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)
+    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));
+        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);
+        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());
+        return node == other || std::equal(node->leaf(),
+                                           node->leaf() + pos.count(),
+                                           other->leaf());
     }
 };
 
@@ -452,51 +463,52 @@ struct update_visitor : visitor_base<update_visitor<NodeT>>
     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 offset = pos.index(idx);
+        auto count  = pos.count();
+        auto node   = node_t::make_inner_sr_n(count, pos.relaxed());
+        IMMER_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;
+            node_t::do_copy_inner_replace_sr(
+                node, pos.node(), count, offset, child);
             return node;
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             node_t::delete_inner_r(node, count);
-            throw;
+            IMMER_RETHROW;
         }
     }
 
     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 offset = pos.index(idx);
+        auto count  = pos.count();
+        auto node   = node_t::make_inner_n(count);
+        IMMER_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;
+            node_t::do_copy_inner_replace(
+                node, pos.node(), count, offset, child);
             return node;
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             node_t::delete_inner(node, count);
-            throw;
+            IMMER_RETHROW;
         }
     }
 
     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]));
+        auto offset = pos.index(idx);
+        auto node   = node_t::copy_leaf(pos.node(), pos.count());
+        IMMER_TRY {
+            node->leaf()[offset] =
+                std::forward<Fn>(fn)(std::move(node->leaf()[offset]));
             return node;
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             node_t::delete_leaf(node, pos.count());
-            throw;
+            IMMER_RETHROW;
         }
     }
 };
@@ -509,7 +521,7 @@ struct dec_visitor : visitor_base<dec_visitor>
     static void visit_relaxed(Pos&& p)
     {
         using node_t = node_type<Pos>;
-        auto node = p.node();
+        auto node    = p.node();
         if (node->dec()) {
             p.each(this_t{});
             node_t::delete_inner_r(node, p.count());
@@ -520,7 +532,7 @@ struct dec_visitor : visitor_base<dec_visitor>
     static void visit_regular(Pos&& p)
     {
         using node_t = node_type<Pos>;
-        auto node = p.node();
+        auto node    = p.node();
         if (node->dec()) {
             p.each(this_t{});
             node_t::delete_inner(node, p.count());
@@ -531,7 +543,7 @@ struct dec_visitor : visitor_base<dec_visitor>
     static void visit_leaf(Pos&& p)
     {
         using node_t = node_type<Pos>;
-        auto node = p.node();
+        auto node    = p.node();
         if (node->dec()) {
             node_t::delete_leaf(node, p.count());
         }
@@ -577,69 +589,75 @@ struct get_mut_visitor : visitor_base<get_mut_visitor<NodeT>>
     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)
+    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();
+        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]);
+            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]);
+            IMMER_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 (...) {
+            }
+            IMMER_CATCH (...) {
                 dec_relaxed(new_node, pos.shift());
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
 
     template <typename Pos>
-    static value_t& visit_regular(Pos&& pos, size_t idx,
-                                  edit_t e, node_t** location)
+    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();
+        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]);
+            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]);
+            IMMER_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 (...) {
+            }
+            IMMER_CATCH (...) {
                 dec_regular(new_node, pos.shift(), pos.size());
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
 
     template <typename Pos>
-    static value_t& visit_leaf(Pos&& pos, size_t idx,
-                               edit_t e, node_t** location)
+    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)];
+            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)];
+            return new_node->leaf()[pos.index(idx)];
         }
     }
 };
@@ -651,29 +669,30 @@ struct push_tail_mut_visitor
     static constexpr auto B  = NodeT::bits;
     static constexpr auto BL = NodeT::bits_leaf;
 
-    using this_t = push_tail_mut_visitor;
+    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;
+    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 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);
+        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);
+            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);
@@ -684,30 +703,34 @@ struct push_tail_mut_visitor
             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;
+            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;
+            relaxed->d.count          = count;
+            assert(relaxed->d.sizes[new_idx]);
             return node;
         } else {
-            try {
+            IMMER_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{});
+                relaxed->d.sizes[new_idx]  = pos.size() + ts;
+                relaxed->d.count           = count;
+                assert(relaxed->d.sizes[new_idx]);
+                if (Mutating)
+                    pos.visit(dec_visitor{});
                 return new_node;
-            } catch (...) {
+            }
+            IMMER_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;
+                IMMER_RETHROW;
             }
         }
     }
@@ -716,34 +739,41 @@ struct push_tail_mut_visitor
     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);
+        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);
+                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 {
+            auto new_parent = node_t::make_inner_e(e);
+            IMMER_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);
+                    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{});
+                if (Mutating)
+                    pos.visit(dec_visitor{});
                 return new_parent;
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 node_t::delete_inner_e(new_parent);
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
 
     template <typename Pos, typename... Args>
     static node_t* visit_leaf(Pos&& pos, edit_t e, node_t* tail, Args&&...)
-    { IMMER_UNREACHABLE; }
+    {
+        IMMER_UNREACHABLE;
+    }
 };
 
 template <typename NodeT>
@@ -758,11 +788,11 @@ struct push_tail_visitor : visitor_base<push_tail_visitor<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 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;
@@ -776,22 +806,25 @@ struct push_tail_visitor : visitor_base<push_tail_visitor<NodeT>>
             }
         } 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;
+        IMMER_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;
+            new_relaxed->d.count          = count;
+            assert(new_relaxed->d.sizes[new_idx]);
             return new_parent;
-        } catch (...) {
+        }
+        IMMER_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;
+            IMMER_RETHROW;
         }
     }
 
@@ -799,24 +832,28 @@ struct push_tail_visitor : visitor_base<push_tail_visitor<NodeT>>
     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 {
+        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);
+        IMMER_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 (...) {
+                idx == new_idx ? pos.last_oh(this_t{}, idx, tail)
+                               /* otherwise */
+                               : node_t::make_path(pos.shift() - B, tail);
+        }
+        IMMER_CATCH (...) {
             node_t::delete_inner(new_parent, count);
-            throw;
+            IMMER_RETHROW;
         }
         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; }
+    {
+        IMMER_UNREACHABLE;
+    }
 };
 
 struct dec_right_visitor : visitor_base<dec_right_visitor>
@@ -828,7 +865,7 @@ struct dec_right_visitor : visitor_base<dec_right_visitor>
     static void visit_relaxed(Pos&& p, count_t idx)
     {
         using node_t = node_type<Pos>;
-        auto node = p.node();
+        auto node    = p.node();
         if (node->dec()) {
             p.each_right(dec_t{}, idx);
             node_t::delete_inner_r(node, p.count());
@@ -839,7 +876,7 @@ struct dec_right_visitor : visitor_base<dec_right_visitor>
     static void visit_regular(Pos&& p, count_t idx)
     {
         using node_t = node_type<Pos>;
-        auto node = p.node();
+        auto node    = p.node();
         if (node->dec()) {
             p.each_right(dec_t{}, idx);
             node_t::delete_inner(node, p.count());
@@ -848,10 +885,12 @@ struct dec_right_visitor : visitor_base<dec_right_visitor>
 
     template <typename Pos>
     static void visit_leaf(Pos&& p, count_t idx)
-    { IMMER_UNREACHABLE; }
+    {
+        IMMER_UNREACHABLE;
+    }
 };
 
-template <typename NodeT, bool Collapse=true, bool Mutating=true>
+template <typename NodeT, bool Collapse = true, bool Mutating = true>
 struct slice_right_mut_visitor
     : visitor_base<slice_right_mut_visitor<NodeT, Collapse, Mutating>>
 {
@@ -860,8 +899,8 @@ struct 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 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>;
 
@@ -871,68 +910,77 @@ struct slice_right_mut_visitor
     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 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});
+            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 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 {
+            IMMER_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 { pos.shift(), node, ts, tail };
+                        nodr->d.count      = idx + 1;
+                        assert(nodr->d.sizes[idx]);
+                        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 { pos.shift(), newn, ts, tail };
+                        newr->d.count      = idx + 1;
+                        assert(newr->d.sizes[idx]);
+                        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 { pos.shift(), nullptr, ts, tail };
+                    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 { pos.shift() - B, newn, ts, tail };
+                    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 { pos.shift(), node, ts, tail };
+                        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 { pos.shift(), newn, ts, tail };
+                        if (Mutating)
+                            pos.visit(dec_visitor{});
+                        return std::make_tuple(pos.shift(), newn, ts, tail);
                     }
                 }
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 assert(!mutate);
                 assert(!next || pos.shift() > BL);
                 if (next)
-                    dec_inner(next, pos.shift() - B,
+                    dec_inner(next,
+                              pos.shift() - B,
                               last + 1 - ts - pos.size_before(idx));
                 dec_leaf(tail, ts);
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
@@ -940,60 +988,67 @@ struct slice_right_mut_visitor
     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 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});
+            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 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 {
+            IMMER_TRY {
                 if (next) {
                     if (mutate) {
                         node->inner()[idx] = next;
                         pos.each_right(dec_visitor{}, idx + 1);
-                        return { pos.shift(), node, ts, tail };
+                        return std::make_tuple(pos.shift(), node, ts, tail);
                     } else {
-                        auto newn  = node_t::copy_inner_e(e, node, idx);
+                        auto newn          = node_t::copy_inner_e(e, node, idx);
                         newn->inner()[idx] = next;
-                        if (Mutating) pos.visit(dec_visitor{});
-                        return { pos.shift(), newn, ts, tail };
+                        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 { pos.shift(), nullptr, ts, tail };
+                    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 { pos.shift() - B, newn, ts, tail };
+                    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 { pos.shift(), node, ts, tail };
+                        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 { pos.shift(), newn, ts, tail };
+                        if (Mutating)
+                            pos.visit(dec_visitor{});
+                        return std::make_tuple(pos.shift(), newn, ts, tail);
                     }
                 }
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 assert(!mutate);
                 assert(!next || pos.shift() > BL);
                 assert(tail);
-                if (next) dec_regular(next, pos.shift() - B, last + 1 - ts);
+                if (next)
+                    dec_regular(next, pos.shift() - B, last + 1 - ts);
                 dec_leaf(tail, ts);
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
@@ -1006,28 +1061,30 @@ struct slice_right_mut_visitor
         auto node          = pos.node();
         auto mutate        = Mutating && node->can_mutate(e);
         if (new_tail_size == old_tail_size) {
-            if (!Mutating) node->inc();
-            return { 0, nullptr, new_tail_size, node };
+            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 { 0, nullptr, new_tail_size, node };
+            detail::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 { 0, nullptr, new_tail_size, new_tail };
+            if (Mutating)
+                pos.visit(dec_visitor{});
+            return std::make_tuple(0, nullptr, new_tail_size, new_tail);
         }
     }
 };
 
-template <typename NodeT, bool Collapse=true>
+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 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;
@@ -1045,30 +1102,36 @@ struct slice_right_visitor : visitor_base<slice_right_visitor<NodeT, Collapse>>
             auto next = get<1>(subs);
             auto ts   = get<2>(subs);
             auto tail = get<3>(subs);
-            try {
+            IMMER_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 { pos.shift(), newn, ts, tail };
+                    newr->d.count      = count;
+                    assert(newr->d.sizes[idx]);
+                    return std::make_tuple(pos.shift(), newn, ts, tail);
                 } else if (idx == 0) {
-                    return { pos.shift(), nullptr, ts, tail };
+                    return std::make_tuple(pos.shift(), nullptr, ts, tail);
                 } else if (Collapse && idx == 1 && pos.shift() > BL) {
                     auto newn = pos.node()->inner()[0];
-                    return { pos.shift() - B, newn->inc(), ts, tail };
+                    return std::make_tuple(
+                        pos.shift() - B, newn->inc(), ts, tail);
                 } else {
                     auto newn = node_t::copy_inner_r(pos.node(), idx);
-                    return { pos.shift(), newn, ts, tail };
+                    return std::make_tuple(pos.shift(), newn, ts, tail);
                 }
-            } catch (...) {
+            }
+            IMMER_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;
+                if (next)
+                    dec_inner(next,
+                              pos.shift() - B,
+                              last + 1 - ts - pos.size_before(idx));
+                if (tail)
+                    dec_leaf(tail, ts);
+                IMMER_RETHROW;
             }
         }
     }
@@ -1085,26 +1148,29 @@ struct slice_right_visitor : visitor_base<slice_right_visitor<NodeT, Collapse>>
             auto next = get<1>(subs);
             auto ts   = get<2>(subs);
             auto tail = get<3>(subs);
-            try {
+            IMMER_TRY {
                 if (next) {
-                    auto newn  = node_t::copy_inner_n(idx + 1, pos.node(), idx);
+                    auto newn = node_t::copy_inner_n(idx + 1, pos.node(), idx);
                     newn->inner()[idx] = next;
-                    return { pos.shift(), newn, ts, tail };
+                    return std::make_tuple(pos.shift(), newn, ts, tail);
                 } else if (idx == 0) {
-                    return { pos.shift(), nullptr, ts, tail };
+                    return std::make_tuple(pos.shift(), nullptr, ts, tail);
                 } else if (Collapse && idx == 1 && pos.shift() > BL) {
                     auto newn = pos.node()->inner()[0];
-                    return { pos.shift() - B, newn->inc(), ts, tail };
+                    return std::make_tuple(
+                        pos.shift() - B, newn->inc(), ts, tail);
                 } else {
                     auto newn = node_t::copy_inner_n(idx, pos.node(), idx);
-                    return { pos.shift(), newn, ts, tail };
+                    return std::make_tuple(pos.shift(), newn, ts, tail);
                 }
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 assert(!next || pos.shift() > BL);
                 assert(tail);
-                if (next) dec_regular(next, pos.shift() - B, last + 1 - ts);
+                if (next)
+                    dec_regular(next, pos.shift() - B, last + 1 - ts);
                 dec_leaf(tail, ts);
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
@@ -1115,9 +1181,9 @@ struct slice_right_visitor : visitor_base<slice_right_visitor<NodeT, Collapse>>
         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 { 0, nullptr, new_tail_size, new_tail };
+                                 ? pos.node()->inc()
+                                 : node_t::copy_leaf(pos.node(), new_tail_size);
+        return std::make_tuple(0, nullptr, new_tail_size, new_tail);
     }
 };
 
@@ -1130,7 +1196,7 @@ struct dec_left_visitor : visitor_base<dec_left_visitor>
     static void visit_relaxed(Pos&& p, count_t idx)
     {
         using node_t = node_type<Pos>;
-        auto node = p.node();
+        auto node    = p.node();
         if (node->dec()) {
             p.each_left(dec_t{}, idx);
             node_t::delete_inner_r(node, p.count());
@@ -1141,7 +1207,7 @@ struct dec_left_visitor : visitor_base<dec_left_visitor>
     static void visit_regular(Pos&& p, count_t idx)
     {
         using node_t = node_type<Pos>;
-        auto node = p.node();
+        auto node    = p.node();
         if (node->dec()) {
             p.each_left(dec_t{}, idx);
             node_t::delete_inner(node, p.count());
@@ -1150,24 +1216,26 @@ struct dec_left_visitor : visitor_base<dec_left_visitor>
 
     template <typename Pos>
     static void visit_leaf(Pos&& p, count_t idx)
-    { IMMER_UNREACHABLE; }
+    {
+        IMMER_UNREACHABLE;
+    }
 };
 
-template <typename NodeT, bool Collapse=true, bool Mutating=true>
+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 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_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>;
+    using no_mut_t             = slice_left_mut_visitor<NodeT, Collapse, false>;
 
     static constexpr auto B  = NodeT::bits;
     static constexpr auto BL = NodeT::bits_leaf;
@@ -1175,49 +1243,56 @@ struct slice_left_mut_visitor
     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 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);
+            auto r = mutate ? pos.towards_sub_oh(this_t{}, first, idx, e)
+                            : pos.towards_sub_oh(no_mut_t{}, first, idx, e);
+            if (mutate)
+                pos.visit(dec_left_visitor{}, idx);
+            else if (Mutating)
+                pos.visit(dec_visitor{});
             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 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);
+            IMMER_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::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;
+                newr->d.count    = newcount;
+                assert(new_child_size);
                 if (!mutate) {
                     node_t::inc_nodes(newn->inner() + 1, newcount - 1);
-                    if (Mutating) pos.visit(dec_visitor{});
+                    if (Mutating)
+                        pos.visit(dec_visitor{});
                 }
-                return { pos.shift(), newn };
-            } catch (...) {
-                if (!mutate) node_t::delete_inner_r_e(newn);
-                throw;
+                return std::make_tuple(pos.shift(), newn);
+            }
+            IMMER_CATCH (...) {
+                if (!mutate)
+                    node_t::delete_inner_r_e(newn);
+                IMMER_RETHROW;
             }
         }
     }
@@ -1229,56 +1304,61 @@ struct slice_left_mut_visitor
         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;
+                      // 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);
+            auto r = mutate ? pos.towards_sub_oh(this_t{}, first, idx, e)
+                            : pos.towards_sub_oh(no_mut_t{}, first, idx, e);
+            if (mutate)
+                pos.visit(dec_left_visitor{}, idx);
+            else if (Mutating)
+                pos.visit(dec_visitor{});
             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...
+            // 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 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);
+            IMMER_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;
+                assert(newr->d.sizes[0]);
+                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);
+                std::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{});
+                    if (Mutating)
+                        pos.visit(dec_visitor{});
                 }
-                return { pos.shift(), newn };
-            } catch (...) {
-                if (!mutate) node_t::delete_inner_r_e(newn);
+                return std::make_tuple(pos.shift(), newn);
+            }
+            IMMER_CATCH (...) {
+                if (!mutate)
+                    node_t::delete_inner_r_e(newn);
                 else {
                     // restore the regular node that we were
                     // attempting to relax...
@@ -1286,7 +1366,7 @@ struct slice_left_mut_visitor
                                              node->impl.d.data.inner.relaxed);
                     node->impl.d.data.inner.relaxed = nullptr;
                 }
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
@@ -1297,31 +1377,32 @@ struct slice_left_mut_visitor
         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);
+        auto mutate = Mutating &&
+                      std::is_nothrow_move_constructible<value_t>::value &&
+                      node->can_mutate(e);
         if (mutate) {
-            auto data = node->leaf();
+            auto data     = node->leaf();
             auto newcount = count - idx;
             std::move(data + idx, data + count, data);
-            destroy_n(data + newcount, idx);
-            return { 0, node };
+            detail::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 { 0, newn };
+            if (Mutating)
+                pos.visit(dec_visitor{});
+            return std::make_tuple(0, newn);
         }
     }
 };
 
-template <typename NodeT, bool Collapse=true>
+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 result_t      = std::tuple<shift_t, NodeT*>;
     using no_collapse_t = slice_left_visitor<NodeT, false>;
 
     static constexpr auto B  = NodeT::bits;
@@ -1330,36 +1411,41 @@ struct slice_left_visitor : visitor_base<slice_left_visitor<NodeT, Collapse>>
     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 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();
+            auto n    = pos.node();
+            auto newc = count - idx;
+            auto newn = node_t::make_inner_r_n(newc);
+            IMMER_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);
+                assert(newr->d.sizes[0]);
+                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);
+                std::copy(n->inner() + idx + 1,
+                          n->inner() + count,
+                          newn->inner() + 1);
                 node_t::inc_nodes(newn->inner() + 1, newr->d.count - 1);
-                return { pos.shift(), newn };
-            } catch (...) {
+                return std::make_tuple(pos.shift(), newn);
+            }
+            IMMER_CATCH (...) {
                 node_t::delete_inner_r(newn, newc);
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
@@ -1368,7 +1454,7 @@ struct slice_left_visitor : visitor_base<slice_left_visitor<NodeT, Collapse>>
     static result_t visit_leaf(PosT&& pos, size_t first)
     {
         auto n = node_t::copy_leaf(pos.node(), pos.index(first), pos.count());
-        return { 0, n };
+        return std::make_tuple(0, n);
     }
 };
 
@@ -1386,54 +1472,71 @@ struct concat_center_pos
     shift_t shift_ = 0u;
     count_t count_ = 0u;
     node_t* nodes_[max_children];
-    size_t  sizes_[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)
+        : 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)
+        : shift_{s}
+        , count_{2}
+        , nodes_{n0, n1}
+        , sizes_{s0, 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} {}
+                      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, s0 + s1, s0 + s1 + s2}
+    {}
 
     template <typename Visitor, typename... Args>
-    void each_sub(Visitor v, Args&& ...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...);
+            auto s = size_t{};
+            for (auto i = count_t{0}; i < count_; ++i) {
+                make_leaf_sub_pos(nodes_[i], sizes_[i] - s).visit(v, args...);
+                s = sizes_[i];
+            }
         } else {
             for (auto i = count_t{0}; i < count_; ++i)
-                make_relaxed_pos(nodes_[i], shift_ - B, nodes_[i]->relaxed()).visit(v, args...);
+                make_relaxed_pos(nodes_[i], shift_ - B, nodes_[i]->relaxed())
+                    .visit(v, args...);
         }
     }
 
     relaxed_pos<Node> realize() &&
     {
         if (count_ > 1) {
-            try {
+            IMMER_TRY {
                 auto result = node_t::make_inner_r_n(count_);
                 auto r      = result->relaxed();
-                r->d.count    = count_;
+                r->d.count  = count_;
                 std::copy(nodes_, nodes_ + count_, result->inner());
                 std::copy(sizes_, sizes_ + count_, r->d.sizes);
-                return { result, shift_, r };
-            } catch (...) {
+                return {result, shift_, r};
+            }
+            IMMER_CATCH (...) {
                 each_sub(dec_visitor{});
-                throw;
+                IMMER_RETHROW;
             }
         } else {
             assert(shift_ >= B + BL);
-            return { nodes_[0], shift_ - B, nodes_[0]->relaxed() };
+            return {nodes_[0], shift_ - B, nodes_[0]->relaxed()};
         }
     }
 
@@ -1442,13 +1545,13 @@ struct concat_center_pos
         if (count_ > 1) {
             auto result = node_t::make_inner_r_e(e);
             auto r      = result->relaxed();
-            r->d.count    = count_;
+            r->d.count  = count_;
             std::copy(nodes_, nodes_ + count_, result->inner());
             std::copy(sizes_, sizes_ + count_, r->d.sizes);
-            return { result, shift_, r };
+            return {result, shift_, r};
         } else {
             assert(shift_ >= B + BL);
-            return { nodes_[0], shift_ - B, nodes_[0]->relaxed() };
+            return {nodes_[0], shift_ - B, nodes_[0]->relaxed()};
         }
     }
 };
@@ -1456,28 +1559,30 @@ struct concat_center_pos
 template <typename Node>
 struct concat_merger
 {
-    using node_t = Node;
+    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_;
+    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}
+        , 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_   = {};
+    node_t* to_        = {};
+    count_t to_offset_ = {};
+    size_t to_size_    = {};
 
     void add_child(node_t* p, size_t size)
     {
+        assert(size);
         ++curr_;
         auto parent  = result_.nodes_[result_.count_ - 1];
         auto relaxed = parent->relaxed();
@@ -1488,12 +1593,15 @@ struct concat_merger
             relaxed = parent->relaxed();
             result_.nodes_[result_.count_] = parent;
             result_.sizes_[result_.count_] = result_.sizes_[result_.count_ - 1];
+            assert(result_.sizes_[result_.count_]);
             ++result_.count_;
         }
         auto idx = relaxed->d.count++;
         result_.sizes_[result_.count_ - 1] += size;
+        assert(result_.sizes_[result_.count_ - 1]);
         relaxed->d.sizes[idx] = size + (idx ? relaxed->d.sizes[idx - 1] : 0);
-        parent->inner() [idx] = p;
+        assert(relaxed->d.sizes[idx]);
+        parent->inner()[idx] = p;
     };
 
     template <typename Pos>
@@ -1511,16 +1619,16 @@ struct concat_merger
             auto from_data   = from->leaf();
             do {
                 if (!to_) {
-                    to_ = node_t::make_leaf_n(*curr_);
+                    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;
+                auto to_copy =
+                    std::min(from_count - from_offset, *curr_ - to_offset_);
+                detail::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_);
@@ -1542,26 +1650,27 @@ struct concat_merger
             from->inc();
         } else {
             auto from_offset = count_t{};
-            auto from_data  = from->inner();
+            auto from_data   = from->inner();
             do {
                 if (!to_) {
-                    to_ = node_t::make_inner_r_n(*curr_);
+                    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_);
+                auto data = to_->inner();
+                auto to_copy =
+                    std::min(from_count - from_offset, *curr_ - to_offset_);
+                std::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;
+                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];
+                to_size_ = sizes[to_offset_ - 1];
+                assert(to_size_);
                 if (*curr_ == to_offset_) {
                     to_->relaxed()->d.count = to_offset_;
                     add_child(to_, to_size_);
@@ -1598,11 +1707,15 @@ struct concat_merger_visitor : visitor_base<concat_merger_visitor>
 
     template <typename Pos, typename Merger>
     static void visit_inner(Pos&& p, Merger& merger)
-    { merger.merge_inner(p); }
+    {
+        merger.merge_inner(p);
+    }
 
     template <typename Pos, typename Merger>
     static void visit_leaf(Pos&& p, Merger& merger)
-    { merger.merge_leaf(p); }
+    {
+        merger.merge_leaf(p);
+    }
 };
 
 struct concat_rebalance_plan_fill_visitor
@@ -1625,8 +1738,8 @@ struct concat_rebalance_plan
 {
     static constexpr auto max_children = 2 * branches<B> + 1;
 
-    count_t counts [max_children];
-    count_t n = 0u;
+    count_t counts[max_children];
+    count_t n     = 0u;
     count_t total = 0u;
 
     template <typename LPos, typename CPos, typename RPos>
@@ -1649,19 +1762,23 @@ struct concat_rebalance_plan
 #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;
+        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++;
+            while (counts[i] > branches - rrb_invariant)
+                i++;
+            assert(i < n);
             // 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;
+                auto next  = counts[i + 1];
+                auto count = std::min(remaining + next, branches);
+                counts[i]  = count;
+                assert(counts[i]);
+                remaining += next - count;
                 ++i;
             } while (remaining > 0);
             // remove node
@@ -1681,38 +1798,38 @@ struct concat_rebalance_plan
         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 {
+        auto merger     = merger_t{cpos.shift(), counts, n};
+        IMMER_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 (...) {
+        }
+        IMMER_CATCH (...) {
             merger.abort();
-            throw;
+            IMMER_RETHROW;
         }
     }
 };
 
 template <typename Node, typename LPos, typename CPos, typename RPos>
-concat_center_pos<Node>
-concat_rebalance(LPos&& lpos, CPos&& cpos, RPos&& 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 {
+    IMMER_TRY {
         return plan.merge(lpos, cpos, rpos);
-    } catch (...) {
+    }
+    IMMER_CATCH (...) {
         cpos.each_sub(dec_visitor{});
-        throw;
+        IMMER_RETHROW;
     }
 }
 
 template <typename Node, typename LPos, typename TPos, typename RPos>
-concat_center_pos<Node>
-concat_leafs(LPos&& lpos, TPos&& tpos, RPos&& rpos)
+concat_center_pos<Node> concat_leafs(LPos&& lpos, TPos&& tpos, RPos&& rpos)
 {
     static_assert(Node::bits >= 2, "");
     assert(lpos.shift() == tpos.shift());
@@ -1721,15 +1838,20 @@ concat_leafs(LPos&& lpos, TPos&& tpos, RPos&& rpos)
     if (tpos.count() > 0)
         return {
             Node::bits_leaf,
-            lpos.node()->inc(), lpos.count(),
-            tpos.node()->inc(), tpos.count(),
-            rpos.node()->inc(), rpos.count(),
+            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(),
+            lpos.node()->inc(),
+            lpos.count(),
+            rpos.node()->inc(),
+            rpos.count(),
         };
 }
 
@@ -1741,8 +1863,7 @@ 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)
+concat_center_pos<Node> concat_inners(LPos&& lpos, TPos&& tpos, RPos&& rpos)
 {
     auto lshift = lpos.shift();
     auto rshift = rpos.shift();
@@ -1768,12 +1889,16 @@ struct concat_left_visitor : visitor_base<concat_left_visitor<Node>>
     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); }
+    {
+        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; }
+    {
+        IMMER_UNREACHABLE;
+    }
 };
 
 template <typename Node>
@@ -1784,29 +1909,36 @@ struct concat_right_visitor : visitor_base<concat_right_visitor<Node>>
     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); }
+    {
+        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); }
+    {
+        return concat_leafs<Node>(lpos, tpos, rpos);
+    }
 };
 
 template <typename Node>
-struct concat_both_visitor
-    : visitor_base<concat_both_visitor<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); }
+    {
+        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); }
+    {
+        return rpos.first_sub_leaf(concat_right_visitor<Node>{}, lpos, tpos);
+    }
 };
 
 template <typename Node>
@@ -1818,47 +1950,56 @@ struct 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); }
+    {
+        return concat_inners<Node>(lpos, tpos, rpos);
+    }
 };
 
 template <typename Node>
-struct concat_trees_left_visitor
-    : visitor_base<concat_trees_left_visitor<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); }
+    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)
+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)
+    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)
+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)
+    return make_singleton_regular_sub_pos(ltail, ltcount)
+        .visit(concat_trees_left_visitor<Node>{},
+               empty_leaf_pos<Node>{},
+               rroot,
+               rshift,
+               rsize)
         .realize();
 }
 
@@ -1876,18 +2017,21 @@ struct concat_merger_mut
 
     using result_t = concat_center_pos<Node>;
 
-    edit_t ec_  = {};
+    edit_t ec_ = {};
 
     count_t* curr_;
-    count_t  n_;
+    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)
+    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}
@@ -1901,15 +2045,19 @@ struct concat_merger_mut
         }
     }
 
-    node_t*  to_        = {};
-    count_t  to_offset_ = {};
-    size_t   to_size_   = {};
+    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; }
+    {
+        candidate_   = candidate;
+        candidate_e_ = candidate_e;
+    }
 
     void add_child(node_t* p, size_t size)
     {
+        assert(size);
         ++curr_;
         auto parent  = result_.nodes_[result_.count_ - 1];
         auto relaxed = parent->relaxed();
@@ -1922,17 +2070,21 @@ struct concat_merger_mut
                 parent->ensure_mutable_relaxed_e(candidate_e_, ec_);
                 candidate_ = nullptr;
             } else
-                parent  = node_t::make_inner_r_e(ec_);
-            count_ = 0;
-            relaxed = parent->relaxed();
+                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];
+            assert(result_.sizes_[result_.count_]);
             ++result_.count_;
         }
         auto idx = count_++;
         result_.sizes_[result_.count_ - 1] += size;
+        assert(size);
+        assert(result_.sizes_[result_.count_ - 1]);
         relaxed->d.sizes[idx] = size + (idx ? relaxed->d.sizes[idx - 1] : 0);
-        parent->inner() [idx] = p;
+        assert(relaxed->d.sizes[idx]);
+        parent->inner()[idx] = p;
     };
 
     template <typename Pos>
@@ -1941,18 +2093,19 @@ struct concat_merger_mut
         auto from       = p.node();
         auto from_size  = p.size();
         auto from_count = p.count();
+        assert(from);
         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);
+            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();
+                        to_                 = from->inc();
                         assert(from_count);
                     } else {
                         to_ = node_t::make_leaf_e(ec_);
@@ -1960,8 +2113,8 @@ struct concat_merger_mut
                     to_offset_ = 0;
                 }
                 auto data = to_->leaf();
-                auto to_copy = std::min(from_count - from_offset,
-                                        *curr_ - to_offset_);
+                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,
@@ -1969,15 +2122,17 @@ struct concat_merger_mut
                                   data + to_offset_);
                 } else {
                     if (!from_mutate)
-                        std::uninitialized_copy(from_data + from_offset,
-                                                from_data + from_offset + to_copy,
-                                                data + to_offset_);
+                        detail::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_);
+                                                   from_data + from_offset +
+                                                       to_copy,
+                                                   data + to_offset_);
                 }
-                to_offset_  += to_copy;
+                to_offset_ += to_copy;
                 from_offset += to_copy;
                 if (*curr_ == to_offset_) {
                     add_child(to_, to_offset_);
@@ -1997,8 +2152,8 @@ struct concat_merger_mut
         if (!to_ && *curr_ == from_count) {
             add_child(from, from_size);
         } else {
-            auto from_offset  = count_t{};
-            auto from_data    = from->inner();
+            auto from_offset = count_t{};
+            auto from_data   = from->inner();
             auto from_mutate = from->can_relax() && from->can_mutate(e);
             do {
                 if (!to_) {
@@ -2012,20 +2167,20 @@ struct concat_merger_mut
                     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;
+                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_);
+                    p.copy_sizes(
+                        from_offset, to_copy, to_size_, sizes + to_offset_);
                 }
-                to_offset_  += to_copy;
+                to_offset_ += to_copy;
                 from_offset += to_copy;
-                to_size_     = sizes[to_offset_ - 1];
+                to_size_ = sizes[to_offset_ - 1];
                 if (*curr_ == to_offset_) {
                     to_->relaxed()->d.count = to_offset_;
                     add_child(to_, to_size_);
@@ -2057,14 +2212,16 @@ 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); }
+    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); }
+    static void visit_leaf(Pos&& p, Merger& merger, edit_type<Pos> e)
+    {
+        merger.merge_leaf(p, e);
+    }
 };
 
 template <bits_t B, bits_t BL>
@@ -2073,40 +2230,48 @@ 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)
+    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 {
+        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};
+        IMMER_TRY {
             lpos.each_left_sub(visitor_t{}, merger, el);
             cpos.each_sub(visitor_t{}, merger, ec);
-            if (rmut2) merger.set_candidate(er, rnode);
+            if (rmut2)
+                merger.set_candidate(er, rnode);
             rpos.each_right_sub(visitor_t{}, merger, er);
             return merger.finish();
-        } catch (...) {
+        }
+        IMMER_CATCH (...) {
             merger.abort();
-            throw;
+            IMMER_RETHROW;
         }
     }
 };
 
 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)
+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);
@@ -2115,10 +2280,12 @@ concat_rebalance_mut(edit_type<Node> ec,
 }
 
 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)
+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());
@@ -2127,15 +2294,20 @@ concat_leafs_mut(edit_type<Node> ec,
     if (tpos.count() > 0)
         return {
             Node::bits_leaf,
-            lpos.node(), lpos.count(),
-            tpos.node(), tpos.count(),
-            rpos.node(), rpos.count(),
+            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(),
+            lpos.node(),
+            lpos.count(),
+            rpos.node(),
+            rpos.count(),
         };
 }
 
@@ -2147,35 +2319,33 @@ 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)
+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{});
+        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);
+        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);
+        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);
     }
 }
 
@@ -2186,67 +2356,62 @@ struct concat_left_mut_visitor : visitor_base<concat_left_mut_visitor<Node>>
     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); }
+    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; }
+    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>>
+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); }
+    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); }
+    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>>
+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); }
+    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); }
+    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>
@@ -2257,12 +2422,11 @@ struct 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); }
+    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>
@@ -2273,47 +2437,69 @@ struct 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); }
+    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)
+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)
+    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)
+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)
+    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);
 }
 
diff --git a/src/immer/detail/rbts/position.hpp b/src/immer/detail/rbts/position.hpp
index 4ff579f8f9..cbdd1c2f1f 100644
--- a/src/immer/detail/rbts/position.hpp
+++ b/src/immer/detail/rbts/position.hpp
@@ -12,8 +12,8 @@
 #include <immer/detail/rbts/bits.hpp>
 
 #include <cassert>
-#include <utility>
 #include <type_traits>
+#include <utility>
 
 namespace immer {
 namespace detail {
@@ -38,17 +38,21 @@ struct empty_regular_pos
     node_t* node_;
 
     count_t count() const { return 0; }
-    node_t* node()  const { return node_; }
+    node_t* node() const { return node_; }
     shift_t shift() const { return 0; }
-    size_t  size()  const { return 0; }
+    size_t size() const { return 0; }
 
     template <typename Visitor, typename... Args>
-    void each(Visitor, Args&&...) {}
+    void each(Visitor, Args&&...)
+    {}
     template <typename Visitor, typename... Args>
-    bool each_pred(Visitor, Args&&...) { return true; }
+    bool each_pred(Visitor, Args&&...)
+    {
+        return true;
+    }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
@@ -67,12 +71,12 @@ struct empty_leaf_pos
     node_t* node_;
 
     count_t count() const { return 0; }
-    node_t* node()  const { return node_; }
+    node_t* node() const { return node_; }
     shift_t shift() const { return 0; }
-    size_t  size()  const { return 0; }
+    size_t size() const { return 0; }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
     }
@@ -96,14 +100,14 @@ struct leaf_pos
     size_t size_;
 
     count_t count() const { return index(size_ - 1) + 1; }
-    node_t* node()  const { return node_; }
-    size_t  size()  const { return size_; }
+    node_t* node() const { return node_; }
+    size_t size() const { return size_; }
     shift_t shift() const { return 0; }
     count_t index(size_t idx) const { return idx & mask<BL>; }
     count_t subindex(size_t idx) const { return idx; }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
     }
@@ -128,14 +132,14 @@ struct leaf_sub_pos
     count_t count_;
 
     count_t count() const { return count_; }
-    node_t* node()  const { return node_; }
-    size_t  size()  const { return count_; }
+    node_t* node() const { return node_; }
+    size_t size() const { return count_; }
     shift_t shift() const { return 0; }
     count_t index(size_t idx) const { return idx & mask<BL>; }
     count_t subindex(size_t idx) const { return idx; }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
     }
@@ -158,15 +162,16 @@ struct leaf_descent_pos
     using node_t = NodeT;
     node_t* node_;
 
-    node_t* node()  const { return node_; }
+    node_t* node() const { return node_; }
     shift_t shift() const { return 0; }
     count_t index(size_t idx) const { return idx & mask<BL>; }
 
     template <typename... Args>
-    decltype(auto) descend(Args&&...) {}
+    decltype(auto) descend(Args&&...)
+    {}
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
     }
@@ -189,14 +194,14 @@ struct full_leaf_pos
     node_t* node_;
 
     count_t count() const { return branches<BL>; }
-    node_t* node()  const { return node_; }
-    size_t  size()  const { return branches<BL>; }
+    node_t* node() const { return node_; }
+    size_t size() const { return branches<BL>; }
     shift_t shift() const { return 0; }
     count_t index(size_t idx) const { return idx & mask<BL>; }
     count_t subindex(size_t idx) const { return idx; }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_leaf(*this, std::forward<Args>(args)...);
     }
@@ -221,78 +226,111 @@ struct regular_pos
     size_t size_;
 
     count_t count() const { return index(size_ - 1) + 1; }
-    node_t* node()  const { return node_; }
-    size_t  size()  const { return size_; }
+    node_t* node() const { return node_; }
+    size_t size() const { return size_; }
     shift_t shift() const { return shift_; }
     count_t index(size_t idx) const { return (idx >> shift_) & mask<B>; }
     count_t subindex(size_t idx) const { return idx >> shift_; }
-    size_t  this_size() const { return ((size_ - 1) & ~(~size_t{} << (shift_ + B))) + 1; }
+    size_t this_size() const
+    {
+        return ((size_ - 1) & ~(~size_t{} << (shift_ + B))) + 1;
+    }
 
     template <typename Visitor, typename... Args>
     void each(Visitor v, Args&&... args)
-    { return each_regular(*this, v, args...); }
+    {
+        return each_regular(*this, v, args...);
+    }
 
     template <typename Visitor, typename... Args>
     bool each_pred(Visitor v, Args&&... args)
-    { return each_pred_regular(*this, v, args...); }
+    {
+        return each_pred_regular(*this, v, args...);
+    }
 
     template <typename Visitor, typename... Args>
     bool each_pred_zip(Visitor v, node_t* other, Args&&... args)
-    { return each_pred_zip_regular(*this, v, other, args...); }
+    {
+        return each_pred_zip_regular(*this, v, other, args...);
+    }
 
     template <typename Visitor, typename... Args>
     bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args)
-    { return each_pred_i_regular(*this, v, i, n, args...); }
+    {
+        return each_pred_i_regular(*this, v, i, n, args...);
+    }
 
     template <typename Visitor, typename... Args>
     bool each_pred_right(Visitor v, count_t start, Args&&... args)
-    { return each_pred_right_regular(*this, v, start, args...); }
+    {
+        return each_pred_right_regular(*this, v, start, args...);
+    }
 
     template <typename Visitor, typename... Args>
     bool each_pred_left(Visitor v, count_t n, Args&&... args)
-    { return each_pred_left_regular(*this, v, n, args...); }
+    {
+        return each_pred_left_regular(*this, v, n, args...);
+    }
 
     template <typename Visitor, typename... Args>
     void each_i(Visitor v, count_t i, count_t n, Args&&... args)
-    { return each_i_regular(*this, v, i, n, args...); }
+    {
+        return each_i_regular(*this, v, i, n, args...);
+    }
 
     template <typename Visitor, typename... Args>
     void each_right(Visitor v, count_t start, Args&&... args)
-    { return each_right_regular(*this, v, start, args...); }
+    {
+        return each_right_regular(*this, v, start, args...);
+    }
 
     template <typename Visitor, typename... Args>
     void each_left(Visitor v, count_t n, Args&&... args)
-    { return each_left_regular(*this, v, n, args...); }
+    {
+        return each_left_regular(*this, v, n, args...);
+    }
 
     template <typename Visitor, typename... Args>
     decltype(auto) towards(Visitor v, size_t idx, Args&&... args)
-    { return towards_oh_ch_regular(*this, v, idx, index(idx), count(), args...); }
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, index(idx), count(), args...);
+    }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_oh(Visitor v, size_t idx,
-                              count_t offset_hint,
-                              Args&&... args)
-    { return towards_oh_ch_regular(*this, v, idx, offset_hint, count(), args...); }
+    decltype(auto)
+    towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, offset_hint, count(), args...);
+    }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_oh_ch(Visitor v, size_t idx,
+    decltype(auto) towards_oh_ch(Visitor v,
+                                 size_t idx,
                                  count_t offset_hint,
                                  count_t count_hint,
                                  Args&&... args)
-    { return towards_oh_ch_regular(*this, v, idx, offset_hint, count(), args...); }
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, offset_hint, count(), args...);
+    }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_sub_oh(Visitor v, size_t idx,
-                                  count_t offset_hint,
-                                  Args&&... args)
-    { return towards_sub_oh_regular(*this, v, idx, offset_hint, args...); }
+    decltype(auto)
+    towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        return towards_sub_oh_regular(*this, v, idx, offset_hint, args...);
+    }
 
     template <typename Visitor, typename... Args>
     decltype(auto) last_oh(Visitor v, count_t offset_hint, Args&&... args)
-    { return last_oh_regular(*this, v, offset_hint, args...); }
+    {
+        return last_oh_regular(*this, v, offset_hint, args...);
+    }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
@@ -303,9 +341,9 @@ void each_regular(Pos&& p, Visitor v, Args&&... args)
 {
     constexpr auto B  = bits<Pos>;
     constexpr auto BL = bits_leaf<Pos>;
-    auto n = p.node()->inner();
-    auto last = p.count() - 1;
-    auto e = n + last;
+    auto n            = p.node()->inner();
+    auto last         = p.count() - 1;
+    auto e            = n + last;
     if (p.shift() == BL) {
         for (; n != e; ++n) {
             IMMER_PREFETCH(n + 1);
@@ -325,9 +363,9 @@ bool each_pred_regular(Pos&& p, Visitor v, Args&&... args)
 {
     constexpr auto B  = bits<Pos>;
     constexpr auto BL = bits_leaf<Pos>;
-    auto n = p.node()->inner();
-    auto last = p.count() - 1;
-    auto e = n + last;
+    auto n            = p.node()->inner();
+    auto last         = p.count() - 1;
+    auto e            = n + last;
     if (p.shift() == BL) {
         for (; n != e; ++n) {
             IMMER_PREFETCH(n + 1);
@@ -345,15 +383,18 @@ bool each_pred_regular(Pos&& p, Visitor v, Args&&... args)
 }
 
 template <typename Pos, typename Visitor, typename... Args>
-bool each_pred_zip_regular(Pos&& p, Visitor v, node_type<Pos>* other, Args&&... args)
+bool each_pred_zip_regular(Pos&& p,
+                           Visitor v,
+                           node_type<Pos>* other,
+                           Args&&... args)
 {
     constexpr auto B  = bits<Pos>;
     constexpr auto BL = bits_leaf<Pos>;
 
-    auto n = p.node()->inner();
-    auto n2 = other->inner();
+    auto n    = p.node()->inner();
+    auto n2   = other->inner();
     auto last = p.count() - 1;
-    auto e = n + last;
+    auto e    = n + last;
     if (p.shift() == BL) {
         for (; n != e; ++n, ++n2) {
             IMMER_PREFETCH(n + 1);
@@ -372,7 +413,8 @@ bool each_pred_zip_regular(Pos&& p, Visitor v, node_type<Pos>* other, Args&&...
 }
 
 template <typename Pos, typename Visitor, typename... Args>
-bool each_pred_i_regular(Pos&& p, Visitor v, count_t f, count_t l, Args&&... args)
+bool each_pred_i_regular(
+    Pos&& p, Visitor v, count_t f, count_t l, Args&&... args)
 {
     constexpr auto B  = bits<Pos>;
     constexpr auto BL = bits_leaf<Pos>;
@@ -437,8 +479,8 @@ bool each_pred_left_regular(Pos&& p, Visitor v, count_t last, Args&&... args)
                 return false;
         }
     } else {
-        auto n = p.node()->inner();
-        auto e = n + last;
+        auto n  = p.node()->inner();
+        auto e  = n + last;
         auto ss = p.shift() - B;
         for (; n != e; ++n)
             if (!make_full_pos(*n, ss).visit(v, args...))
@@ -454,9 +496,9 @@ bool each_pred_right_regular(Pos&& p, Visitor v, count_t start, Args&&... args)
     constexpr auto BL = bits_leaf<Pos>;
 
     if (p.shift() == BL) {
-        auto n = p.node()->inner() + start;
+        auto n    = p.node()->inner() + start;
         auto last = p.count() - 1;
-        auto e = p.node()->inner() + last;
+        auto e    = p.node()->inner() + last;
         if (n <= e) {
             for (; n != e; ++n) {
                 IMMER_PREFETCH(n + 1);
@@ -467,10 +509,10 @@ bool each_pred_right_regular(Pos&& p, Visitor v, count_t start, Args&&... args)
                 return false;
         }
     } else {
-        auto n = p.node()->inner() + start;
+        auto n    = p.node()->inner() + start;
         auto last = p.count() - 1;
-        auto e = p.node()->inner() + last;
-        auto ss = p.shift() - B;
+        auto e    = p.node()->inner() + last;
+        auto ss   = p.shift() - B;
         if (n <= e) {
             for (; n != e; ++n)
                 if (!make_full_pos(*n, ss).visit(v, args...))
@@ -540,8 +582,8 @@ void each_left_regular(Pos&& p, Visitor v, count_t last, Args&&... args)
             make_full_leaf_pos(*n).visit(v, args...);
         }
     } else {
-        auto n = p.node()->inner();
-        auto e = n + last;
+        auto n  = p.node()->inner();
+        auto e  = n + last;
         auto ss = p.shift() - B;
         for (; n != e; ++n)
             make_full_pos(*n, ss).visit(v, args...);
@@ -555,9 +597,9 @@ void each_right_regular(Pos&& p, Visitor v, count_t start, Args&&... args)
     constexpr auto BL = bits_leaf<Pos>;
 
     if (p.shift() == BL) {
-        auto n = p.node()->inner() + start;
+        auto n    = p.node()->inner() + start;
         auto last = p.count() - 1;
-        auto e = p.node()->inner() + last;
+        auto e    = p.node()->inner() + last;
         if (n <= e) {
             for (; n != e; ++n) {
                 IMMER_PREFETCH(n + 1);
@@ -566,10 +608,10 @@ void each_right_regular(Pos&& p, Visitor v, count_t start, Args&&... args)
             make_leaf_pos(*n, p.size()).visit(v, args...);
         }
     } else {
-        auto n = p.node()->inner() + start;
+        auto n    = p.node()->inner() + start;
         auto last = p.count() - 1;
-        auto e = p.node()->inner() + last;
-        auto ss = p.shift() - B;
+        auto e    = p.node()->inner() + last;
+        auto ss   = p.shift() - B;
         if (n <= e) {
             for (; n != e; ++n)
                 make_full_pos(*n, ss).visit(v, args...);
@@ -579,7 +621,9 @@ void each_right_regular(Pos&& p, Visitor v, count_t start, Args&&... args)
 }
 
 template <typename Pos, typename Visitor, typename... Args>
-decltype(auto) towards_oh_ch_regular(Pos&& p, Visitor v, size_t idx,
+decltype(auto) towards_oh_ch_regular(Pos&& p,
+                                     Visitor v,
+                                     size_t idx,
                                      count_t offset_hint,
                                      count_t count_hint,
                                      Args&&... args)
@@ -587,64 +631,60 @@ decltype(auto) towards_oh_ch_regular(Pos&& p, Visitor v, size_t idx,
     constexpr auto B  = bits<Pos>;
     constexpr auto BL = bits_leaf<Pos>;
     assert(offset_hint == p.index(idx));
-    assert(count_hint  == p.count());
+    assert(count_hint == p.count());
     auto is_leaf = p.shift() == BL;
-    auto child   = p.node()->inner() [offset_hint];
+    auto child   = p.node()->inner()[offset_hint];
     auto is_full = offset_hint + 1 != count_hint;
     return is_full
-        ? (is_leaf
-           ? make_full_leaf_pos(child).visit(v, idx, args...)
-           : make_full_pos(child, p.shift() - B).visit(v, idx, args...))
-        : (is_leaf
-           ? make_leaf_pos(child, p.size()).visit(v, idx, args...)
-           : make_regular_pos(child, p.shift() - B, p.size()).visit(v, idx, args...));
+               ? (is_leaf ? make_full_leaf_pos(child).visit(v, idx, args...)
+                          : make_full_pos(child, p.shift() - B)
+                                .visit(v, idx, args...))
+               : (is_leaf
+                      ? make_leaf_pos(child, p.size()).visit(v, idx, args...)
+                      : make_regular_pos(child, p.shift() - B, p.size())
+                            .visit(v, idx, args...));
 }
 
 template <typename Pos, typename Visitor, typename... Args>
-decltype(auto) towards_sub_oh_regular(Pos&& p, Visitor v, size_t idx,
-                                      count_t offset_hint,
-                                      Args&&... args)
+decltype(auto) towards_sub_oh_regular(
+    Pos&& p, Visitor v, size_t idx, count_t offset_hint, Args&&... args)
 {
     constexpr auto B  = bits<Pos>;
     constexpr auto BL = bits_leaf<Pos>;
     assert(offset_hint == p.index(idx));
     auto is_leaf = p.shift() == BL;
-    auto child   = p.node()->inner() [offset_hint];
+    auto child   = p.node()->inner()[offset_hint];
     auto lsize   = offset_hint << p.shift();
     auto size    = p.this_size();
     auto is_full = (size - lsize) >= (size_t{1} << p.shift());
     return is_full
-        ? (is_leaf
-           ? make_full_leaf_pos(child).visit(
-               v, idx - lsize, args...)
-           : make_full_pos(child, p.shift() - B).visit(
-               v, idx - lsize, args...))
-        : (is_leaf
-           ? make_leaf_sub_pos(child, size - lsize).visit(
-               v, idx - lsize, args...)
-           : make_regular_sub_pos(child, p.shift() - B, size - lsize).visit(
-               v, idx - lsize, args...));
+               ? (is_leaf
+                      ? make_full_leaf_pos(child).visit(v, idx - lsize, args...)
+                      : make_full_pos(child, p.shift() - B)
+                            .visit(v, idx - lsize, args...))
+               : (is_leaf
+                      ? make_leaf_sub_pos(child, size - lsize)
+                            .visit(v, idx - lsize, args...)
+                      : make_regular_sub_pos(child, p.shift() - B, size - lsize)
+                            .visit(v, idx - lsize, args...));
 }
 
 template <typename Pos, typename Visitor, typename... Args>
-decltype(auto) last_oh_regular(Pos&& p, Visitor v,
-                               count_t offset_hint,
-                               Args&&... args)
+decltype(auto)
+last_oh_regular(Pos&& p, Visitor v, count_t offset_hint, Args&&... args)
 {
     assert(offset_hint == p.count() - 1);
     constexpr auto B  = bits<Pos>;
     constexpr auto BL = bits_leaf<Pos>;
-    auto child     = p.node()->inner() [offset_hint];
-    auto is_leaf   = p.shift() == BL;
-    return is_leaf
-        ? make_leaf_pos(child, p.size()).visit(v, args...)
-        : make_regular_pos(child, p.shift() - B, p.size()).visit(v, args...);
+    auto child        = p.node()->inner()[offset_hint];
+    auto is_leaf      = p.shift() == BL;
+    return is_leaf ? make_leaf_pos(child, p.size()).visit(v, args...)
+                   : make_regular_pos(child, p.shift() - B, p.size())
+                         .visit(v, args...);
 }
 
 template <typename NodeT>
-regular_pos<NodeT> make_regular_pos(NodeT* node,
-                                    shift_t shift,
-                                    size_t size)
+regular_pos<NodeT> make_regular_pos(NodeT* node, shift_t shift, size_t size)
 {
     assert(node);
     assert(shift >= NodeT::bits_leaf);
@@ -657,13 +697,17 @@ struct null_sub_pos
     auto node() const { return nullptr; }
 
     template <typename Visitor, typename... Args>
-    void each_sub(Visitor, Args&&...) {}
+    void each_sub(Visitor, Args&&...)
+    {}
     template <typename Visitor, typename... Args>
-    void each_right_sub(Visitor, Args&&...) {}
+    void each_right_sub(Visitor, Args&&...)
+    {}
     template <typename Visitor, typename... Args>
-    void each_left_sub(Visitor, Args&&...) {}
+    void each_left_sub(Visitor, Args&&...)
+    {}
     template <typename Visitor, typename... Args>
-    void visit(Visitor, Args&&...) {}
+    void visit(Visitor, Args&&...)
+    {}
 };
 
 template <typename NodeT>
@@ -680,19 +724,21 @@ struct singleton_regular_sub_pos
     count_t count_;
 
     count_t count() const { return 1; }
-    node_t* node()  const { return nullptr; }
-    size_t  size()  const { return count_; }
+    node_t* node() const { return nullptr; }
+    size_t size() const { return count_; }
     shift_t shift() const { return BL; }
     count_t index(size_t idx) const { return 0; }
     count_t subindex(size_t idx) const { return 0; }
-    size_t  size_before(count_t offset) const { return 0; }
-    size_t  this_size() const { return count_; }
-    size_t  size(count_t offset) { return count_; }
+    size_t size_before(count_t offset) const { return 0; }
+    size_t this_size() const { return count_; }
+    size_t size(count_t offset) { return count_; }
 
     template <typename Visitor, typename... Args>
-    void each_left_sub(Visitor v, Args&&... args) {}
+    void each_left_sub(Visitor v, Args&&... args)
+    {}
     template <typename Visitor, typename... Args>
-    void each(Visitor v, Args&&... args) {}
+    void each(Visitor v, Args&&... args)
+    {}
 
     template <typename Visitor, typename... Args>
     decltype(auto) last_sub(Visitor v, Args&&... args)
@@ -700,8 +746,8 @@ struct singleton_regular_sub_pos
         return make_leaf_sub_pos(leaf_, count_).visit(v, args...);
     }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
@@ -711,7 +757,7 @@ template <typename NodeT>
 auto make_singleton_regular_sub_pos(NodeT* leaf, count_t count)
 {
     assert(leaf);
-    assert(leaf->kind() == NodeT::kind_t::leaf);
+    IMMER_ASSERT_TAGGED(leaf->kind() == NodeT::kind_t::leaf);
     assert(count > 0);
     return singleton_regular_sub_pos<NodeT>{leaf, count};
 }
@@ -728,86 +774,105 @@ struct regular_sub_pos
     size_t size_;
 
     count_t count() const { return subindex(size_ - 1) + 1; }
-    node_t* node()  const { return node_; }
-    size_t  size()  const { return size_; }
+    node_t* node() const { return node_; }
+    size_t size() const { return size_; }
     shift_t shift() const { return shift_; }
     count_t index(size_t idx) const { return (idx >> shift_) & mask<B>; }
     count_t subindex(size_t idx) const { return idx >> shift_; }
-    size_t  size_before(count_t offset) const { return offset << shift_; }
-    size_t  this_size() const { return size_; }
+    size_t size_before(count_t offset) const
+    {
+        return size_t{offset} << shift_;
+    }
+    size_t this_size() const { return size_; }
 
     auto size(count_t offset)
     {
-        return offset == subindex(size_ - 1)
-            ? size_ - size_before(offset)
-            : 1 << shift_;
+        return offset == subindex(size_ - 1) ? size_ - size_before(offset)
+                                             : size_t{1} << shift_;
     }
 
     auto size_sbh(count_t offset, size_t size_before_hint)
     {
         assert(size_before_hint == size_before(offset));
-        return offset == subindex(size_ - 1)
-            ? size_ - size_before_hint
-            : 1 << shift_;
+        return offset == subindex(size_ - 1) ? size_ - size_before_hint
+                                             : size_t{1} << shift_;
     }
 
-    void copy_sizes(count_t offset,
-                    count_t n,
-                    size_t init,
-                    size_t* sizes)
+    void copy_sizes(count_t offset, count_t n, size_t init, size_t* sizes)
     {
         if (n) {
             auto last = offset + n - 1;
-            auto e = sizes + n - 1;
-            for (; sizes != e; ++sizes)
-                init = *sizes = init + (1 << shift_);
+            auto e    = sizes + n - 1;
+            for (; sizes != e; ++sizes) {
+                init = *sizes = init + (size_t{1} << shift_);
+                assert(init);
+            }
             *sizes = init + size(last);
+            assert(*sizes);
         }
     }
 
     template <typename Visitor, typename... Args>
-    void each(Visitor v, Args&& ...args)
-    { return each_regular(*this, v, args...); }
+    void each(Visitor v, Args&&... args)
+    {
+        return each_regular(*this, v, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    bool each_pred(Visitor v, Args&& ...args)
-    { return each_pred_regular(*this, v, args...); }
+    bool each_pred(Visitor v, Args&&... args)
+    {
+        return each_pred_regular(*this, v, args...);
+    }
 
     template <typename Visitor, typename... Args>
     bool each_pred_zip(Visitor v, node_t* other, Args&&... args)
-    { return each_pred_zip_regular(*this, v, other, args...); }
+    {
+        return each_pred_zip_regular(*this, v, other, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    bool each_pred_i(Visitor v, count_t i, count_t n, Args&& ...args)
-    { return each_pred_i_regular(*this, v, i, n, args...); }
+    bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args)
+    {
+        return each_pred_i_regular(*this, v, i, n, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    bool each_pred_right(Visitor v, count_t start, Args&& ...args)
-    { return each_pred_right_regular(*this, v, start, args...); }
+    bool each_pred_right(Visitor v, count_t start, Args&&... args)
+    {
+        return each_pred_right_regular(*this, v, start, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    bool each_pred_left(Visitor v, count_t last, Args&& ...args)
-    { return each_pred_left_regular(*this, v, last, args...); }
+    bool each_pred_left(Visitor v, count_t last, Args&&... args)
+    {
+        return each_pred_left_regular(*this, v, last, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    void each_i(Visitor v, count_t i, count_t n, Args&& ...args)
-    { return each_i_regular(*this, v, i, n, args...); }
+    void each_i(Visitor v, count_t i, count_t n, Args&&... args)
+    {
+        return each_i_regular(*this, v, i, n, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    void each_right(Visitor v, count_t start, Args&& ...args)
-    { return each_right_regular(*this, v, start, args...); }
+    void each_right(Visitor v, count_t start, Args&&... args)
+    {
+        return each_right_regular(*this, v, start, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    void each_left(Visitor v, count_t last, Args&& ...args)
-    { return each_left_regular(*this, v, last, args...); }
+    void each_left(Visitor v, count_t last, Args&&... args)
+    {
+        return each_left_regular(*this, v, last, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    void each_right_sub_(Visitor v, count_t i, Args&& ...args)
+    void each_right_sub_(Visitor v, count_t i, Args&&... args)
     {
         auto last  = count() - 1;
         auto lsize = size_ - (last << shift_);
-        auto n = node()->inner() + i;
-        auto e = node()->inner() + last;
+        auto n     = node()->inner() + i;
+        auto e     = node()->inner() + last;
         if (shift() == BL) {
             for (; n != e; ++n) {
                 IMMER_PREFETCH(n + 1);
@@ -823,91 +888,110 @@ struct regular_sub_pos
     }
 
     template <typename Visitor, typename... Args>
-    void each_sub(Visitor v, Args&& ...args)
-    { each_right_sub_(v, 0, args...); }
+    void each_sub(Visitor v, Args&&... args)
+    {
+        each_right_sub_(v, 0, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    void each_right_sub(Visitor v, Args&& ...args)
-    { if (count() > 1) each_right_sub_(v, 1, args...); }
+    void each_right_sub(Visitor v, Args&&... args)
+    {
+        if (count() > 1)
+            each_right_sub_(v, 1, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    void each_left_sub(Visitor v, Args&& ...args)
-    { each_left(v, count() - 1, args...); }
+    void each_left_sub(Visitor v, Args&&... args)
+    {
+        each_left(v, count() - 1, args...);
+    }
 
     template <typename Visitor, typename... Args>
     decltype(auto) towards(Visitor v, size_t idx, Args&&... args)
-    { return towards_oh_ch_regular(*this, v, idx, index(idx), count(), args...); }
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, index(idx), count(), args...);
+    }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_oh(Visitor v, size_t idx,
-                              count_t offset_hint,
-                              Args&&... args)
-    { return towards_oh_ch_regular(*this, v, idx, offset_hint, count(), args...); }
+    decltype(auto)
+    towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, offset_hint, count(), args...);
+    }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_oh_ch(Visitor v, size_t idx,
+    decltype(auto) towards_oh_ch(Visitor v,
+                                 size_t idx,
                                  count_t offset_hint,
                                  count_t count_hint,
                                  Args&&... args)
-    { return towards_oh_ch_regular(*this, v, idx, offset_hint, count(), args...); }
+    {
+        return towards_oh_ch_regular(
+            *this, v, idx, offset_hint, count(), args...);
+    }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_sub_oh(Visitor v, size_t idx,
-                                  count_t offset_hint,
-                                  Args&& ...args)
-    { return towards_sub_oh_regular(*this, v, idx, offset_hint, args...); }
+    decltype(auto)
+    towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
+    {
+        return towards_sub_oh_regular(*this, v, idx, offset_hint, args...);
+    }
 
     template <typename Visitor, typename... Args>
     decltype(auto) last_oh(Visitor v, count_t offset_hint, Args&&... args)
-    { return last_oh_regular(*this, v, offset_hint, args...); }
+    {
+        return last_oh_regular(*this, v, offset_hint, args...);
+    }
 
     template <typename Visitor, typename... Args>
     decltype(auto) last_sub(Visitor v, Args&&... args)
     {
-        auto offset    = count() - 1;
-        auto child     = node_->inner() [offset];
-        auto is_leaf   = shift_ == BL;
-        auto lsize     = size_ - (offset << shift_);
-        return is_leaf
-            ? make_leaf_sub_pos(child, lsize).visit(v, args...)
-            : make_regular_sub_pos(child, shift_ - B, lsize).visit(v, args...);
+        auto offset  = count() - 1;
+        auto child   = node_->inner()[offset];
+        auto is_leaf = shift_ == BL;
+        auto lsize   = size_ - (size_t{offset} << shift_);
+        return is_leaf ? make_leaf_sub_pos(child, lsize).visit(v, args...)
+                       : make_regular_sub_pos(child, shift_ - B, lsize)
+                             .visit(v, args...);
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) first_sub(Visitor v, Args&&... args)
     {
         auto is_leaf = shift_ == BL;
-        auto child   = node_->inner() [0];
+        auto child   = node_->inner()[0];
         auto is_full = size_ >= (size_t{1} << shift_);
         return is_full
-            ? (is_leaf
-               ? make_full_leaf_pos(child).visit(v, args...)
-               : make_full_pos(child, shift_ - B).visit(v, args...))
-            : (is_leaf
-               ? make_leaf_sub_pos(child, size_).visit(v, args...)
-               : make_regular_sub_pos(child, shift_ - B, size_).visit(v, args...));
+                   ? (is_leaf
+                          ? make_full_leaf_pos(child).visit(v, args...)
+                          : make_full_pos(child, shift_ - B).visit(v, args...))
+                   : (is_leaf
+                          ? make_leaf_sub_pos(child, size_).visit(v, args...)
+                          : make_regular_sub_pos(child, shift_ - B, size_)
+                                .visit(v, args...));
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) first_sub_leaf(Visitor v, Args&&... args)
     {
         assert(shift_ == BL);
-        auto child   = node_->inner() [0];
+        auto child   = node_->inner()[0];
         auto is_full = size_ >= branches<BL>;
-        return is_full
-            ? make_full_leaf_pos(child).visit(v, args...)
-            : make_leaf_sub_pos(child, size_).visit(v, args...);
+        return is_full ? make_full_leaf_pos(child).visit(v, args...)
+                       : make_leaf_sub_pos(child, size_).visit(v, args...);
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) first_sub_inner(Visitor v, Args&&... args)
     {
         assert(shift_ >= BL);
-        auto child   = node_->inner() [0];
+        auto child   = node_->inner()[0];
         auto is_full = size_ >= branches<BL>;
-        return is_full
-            ? make_full_pos(child, shift_ - B).visit(v, args...)
-            : make_regular_sub_pos(child, shift_ - B, size_).visit(v, args...);
+        return is_full ? make_full_pos(child, shift_ - B).visit(v, args...)
+                       : make_regular_sub_pos(child, shift_ - B, size_)
+                             .visit(v, args...);
     }
 
     template <typename Visitor, typename... Args>
@@ -915,41 +999,40 @@ struct regular_sub_pos
     {
         assert(idx < count());
         auto is_leaf = shift_ == BL;
-        auto child   = node_->inner() [idx];
+        auto child   = node_->inner()[idx];
         auto lsize   = size(idx);
         auto is_full = idx + 1 < count();
         return is_full
-            ? (is_leaf
-               ? make_full_leaf_pos(child).visit(v, args...)
-               : make_full_pos(child, shift_ - B).visit(v, args...))
-            : (is_leaf
-               ? make_leaf_sub_pos(child, lsize).visit(v, args...)
-               : make_regular_sub_pos(child, shift_ - B, lsize).visit(v, args...));
+                   ? (is_leaf
+                          ? make_full_leaf_pos(child).visit(v, args...)
+                          : make_full_pos(child, shift_ - B).visit(v, args...))
+                   : (is_leaf
+                          ? make_leaf_sub_pos(child, lsize).visit(v, args...)
+                          : make_regular_sub_pos(child, shift_ - B, lsize)
+                                .visit(v, args...));
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) nth_sub_leaf(count_t idx, Visitor v, Args&&... args)
     {
         assert(shift_ == BL);
-        auto child   = node_->inner() [idx];
+        auto child   = node_->inner()[idx];
         auto lsize   = size(idx);
         auto is_full = idx + 1 < count();
-        return is_full
-            ? make_full_leaf_pos(child).visit(v, args...)
-            : make_leaf_sub_pos(child, lsize).visit(v, args...);
+        return is_full ? make_full_leaf_pos(child).visit(v, args...)
+                       : make_leaf_sub_pos(child, lsize).visit(v, args...);
     }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
 };
 
 template <typename NodeT>
-regular_sub_pos<NodeT> make_regular_sub_pos(NodeT* node,
-                                            shift_t shift,
-                                            size_t size)
+regular_sub_pos<NodeT>
+make_regular_sub_pos(NodeT* node, shift_t shift, size_t size)
 {
     assert(node);
     assert(shift >= NodeT::bits_leaf);
@@ -958,7 +1041,8 @@ regular_sub_pos<NodeT> make_regular_sub_pos(NodeT* node,
     return {node, shift, size};
 }
 
-template <typename NodeT, shift_t Shift,
+template <typename NodeT,
+          shift_t Shift,
           bits_t B  = NodeT::bits,
           bits_t BL = NodeT::bits_leaf>
 struct regular_descent_pos
@@ -968,9 +1052,10 @@ struct regular_descent_pos
     using node_t = NodeT;
     node_t* node_;
 
-    node_t* node()  const { return node_; }
+    node_t* node() const { return node_; }
     shift_t shift() const { return Shift; }
-    count_t index(size_t idx) const {
+    count_t index(size_t idx) const
+    {
 #if !defined(_MSC_VER)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wshift-count-overflow"
@@ -989,8 +1074,8 @@ struct regular_descent_pos
         return regular_descent_pos<NodeT, Shift - B>{child}.visit(v, idx);
     }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
@@ -1002,7 +1087,7 @@ struct regular_descent_pos<NodeT, BL, B, BL>
     using node_t = NodeT;
     node_t* node_;
 
-    node_t* node()  const { return node_; }
+    node_t* node() const { return node_; }
     shift_t shift() const { return BL; }
     count_t index(size_t idx) const { return (idx >> BL) & mask<B>; }
 
@@ -1014,32 +1099,38 @@ struct regular_descent_pos<NodeT, BL, B, BL>
         return make_leaf_descent_pos(child).visit(v, idx);
     }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
 };
 
 template <typename NodeT, typename Visitor>
-decltype(auto) visit_regular_descent(NodeT* node, shift_t shift, Visitor v,
-                                     size_t idx)
+decltype(auto)
+visit_regular_descent(NodeT* node, shift_t shift, Visitor v, size_t idx)
 {
     constexpr auto B  = NodeT::bits;
     constexpr auto BL = NodeT::bits_leaf;
     assert(node);
     assert(shift >= BL);
     switch (shift) {
-    case BL + B * 0: return regular_descent_pos<NodeT, BL + B * 0>{node}.visit(v, idx);
-    case BL + B * 1: return regular_descent_pos<NodeT, BL + B * 1>{node}.visit(v, idx);
-    case BL + B * 2: return regular_descent_pos<NodeT, BL + B * 2>{node}.visit(v, idx);
-    case BL + B * 3: return regular_descent_pos<NodeT, BL + B * 3>{node}.visit(v, idx);
-    case BL + B * 4: return regular_descent_pos<NodeT, BL + B * 4>{node}.visit(v, idx);
-    case BL + B * 5: return regular_descent_pos<NodeT, BL + B * 5>{node}.visit(v, idx);
+    case BL + B * 0:
+        return regular_descent_pos<NodeT, BL + B * 0>{node}.visit(v, idx);
+    case BL + B * 1:
+        return regular_descent_pos<NodeT, BL + B * 1>{node}.visit(v, idx);
+    case BL + B * 2:
+        return regular_descent_pos<NodeT, BL + B * 2>{node}.visit(v, idx);
+    case BL + B * 3:
+        return regular_descent_pos<NodeT, BL + B * 3>{node}.visit(v, idx);
+    case BL + B * 4:
+        return regular_descent_pos<NodeT, BL + B * 4>{node}.visit(v, idx);
+    case BL + B * 5:
+        return regular_descent_pos<NodeT, BL + B * 5>{node}.visit(v, idx);
 #if IMMER_DESCENT_DEEP
     default:
         for (auto level = shift; level != endshift<B, BL>; level -= B)
-            node = node->inner() [(idx >> level) & mask<B>];
+            node = node->inner()[(idx >> level) & mask<B>];
         return make_leaf_descent_pos(node).visit(v, idx);
 #endif // IMMER_DEEP_DESCENT
     }
@@ -1057,23 +1148,28 @@ struct full_pos
     shift_t shift_;
 
     count_t count() const { return branches<B>; }
-    node_t* node()  const { return node_; }
-    size_t  size()  const { return branches<B> << shift_; }
+    node_t* node() const { return node_; }
+    size_t size() const { return branches<B, size_t> << shift_; }
     shift_t shift() const { return shift_; }
     count_t index(size_t idx) const { return (idx >> shift_) & mask<B>; }
     count_t subindex(size_t idx) const { return idx >> shift_; }
-    size_t  size(count_t offset) const { return 1 << shift_; }
-    size_t  size_sbh(count_t offset, size_t) const { return 1 << shift_; }
-    size_t  size_before(count_t offset) const { return offset << shift_; }
+    size_t size(count_t offset) const { return size_t{1} << shift_; }
+    size_t size_sbh(count_t offset, size_t) const
+    {
+        return size_t{1} << shift_;
+    }
+    size_t size_before(count_t offset) const
+    {
+        return size_t{offset} << shift_;
+    }
 
-    void copy_sizes(count_t offset,
-                    count_t n,
-                    size_t init,
-                    size_t* sizes)
+    void copy_sizes(count_t offset, count_t n, size_t init, size_t* sizes)
     {
         auto e = sizes + n;
-        for (; sizes != e; ++sizes)
-            init = *sizes = init + (1 << shift_);
+        for (; sizes != e; ++sizes) {
+            init = *sizes = init + (size_t{1} << shift_);
+            assert(init);
+        }
     }
 
     template <typename Visitor, typename... Args>
@@ -1116,9 +1212,9 @@ struct full_pos
     template <typename Visitor, typename... Args>
     bool each_pred_zip(Visitor v, node_t* other, Args&&... args)
     {
-        auto p = node_->inner();
+        auto p  = node_->inner();
         auto p2 = other->inner();
-        auto e = p + branches<B>;
+        auto e  = p + branches<B>;
         if (shift_ == BL) {
             for (; p != e; ++p, ++p2) {
                 IMMER_PREFETCH(p + 1);
@@ -1173,84 +1269,99 @@ struct full_pos
 
     template <typename Visitor, typename... Args>
     bool each_pred_right(Visitor v, count_t start, Args&&... args)
-    { return each_pred_i(v, start, branches<B>, args...); }
+    {
+        return each_pred_i(v, start, branches<B>, args...);
+    }
 
     template <typename Visitor, typename... Args>
     bool each_pred_left(Visitor v, count_t last, Args&&... args)
-    { return each_pred_i(v, 0, last, args...); }
+    {
+        return each_pred_i(v, 0, last, args...);
+    }
 
     template <typename Visitor, typename... Args>
     void each_sub(Visitor v, Args&&... args)
-    { each(v, args...); }
+    {
+        each(v, args...);
+    }
 
     template <typename Visitor, typename... Args>
     void each_left_sub(Visitor v, Args&&... args)
-    { each_i(v, 0, branches<B> - 1, args...); }
+    {
+        each_i(v, 0, branches<B> - 1, args...);
+    }
 
     template <typename Visitor, typename... Args>
     void each_right_sub(Visitor v, Args&&... args)
-    { each_i(v, 1, branches<B>, args...); }
+    {
+        each_i(v, 1, branches<B>, args...);
+    }
 
     template <typename Visitor, typename... Args>
     void each_right(Visitor v, count_t start, Args&&... args)
-    { each_i(v, start, branches<B>, args...); }
+    {
+        each_i(v, start, branches<B>, args...);
+    }
 
     template <typename Visitor, typename... Args>
     void each_left(Visitor v, count_t last, Args&&... args)
-    { each_i(v, 0, last, args...); }
+    {
+        each_i(v, 0, last, args...);
+    }
 
     template <typename Visitor, typename... Args>
     decltype(auto) towards(Visitor v, size_t idx, Args&&... args)
-    { return towards_oh(v, idx, index(idx), args...); }
+    {
+        return towards_oh(v, idx, index(idx), args...);
+    }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_oh_ch(Visitor v, size_t idx,
-                                 count_t offset_hint, count_t,
-                                 Args&&... args)
-    { return towards_oh(v, idx, offset_hint, args...); }
+    decltype(auto) towards_oh_ch(
+        Visitor v, size_t idx, count_t offset_hint, count_t, Args&&... args)
+    {
+        return towards_oh(v, idx, offset_hint, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_oh(Visitor v, size_t idx,
-                              count_t offset_hint,
-                              Args&&... args)
+    decltype(auto)
+    towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
     {
         assert(offset_hint == index(idx));
         auto is_leaf = shift_ == BL;
-        auto child   = node_->inner() [offset_hint];
+        auto child   = node_->inner()[offset_hint];
         return is_leaf
-            ? make_full_leaf_pos(child).visit(v, idx, args...)
-            : make_full_pos(child, shift_ - B).visit(v, idx, args...);
+                   ? make_full_leaf_pos(child).visit(v, idx, args...)
+                   : make_full_pos(child, shift_ - B).visit(v, idx, args...);
     }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_sub_oh(Visitor v, size_t idx,
-                                  count_t offset_hint,
-                                  Args&&... args)
+    decltype(auto)
+    towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
     {
         assert(offset_hint == index(idx));
         auto is_leaf = shift_ == BL;
-        auto child   = node_->inner() [offset_hint];
-        auto lsize   = offset_hint << shift_;
+        auto child   = node_->inner()[offset_hint];
+        auto lsize   = size_t{offset_hint} << shift_;
         return is_leaf
-            ? make_full_leaf_pos(child).visit(v, idx - lsize, args...)
-            : make_full_pos(child, shift_ - B).visit(v, idx - lsize, args...);
+                   ? make_full_leaf_pos(child).visit(v, idx - lsize, args...)
+                   : make_full_pos(child, shift_ - B)
+                         .visit(v, idx - lsize, args...);
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) first_sub(Visitor v, Args&&... args)
     {
         auto is_leaf = shift_ == BL;
-        auto child   = node_->inner() [0];
-        return is_leaf
-            ? make_full_leaf_pos(child).visit(v, args...)
-            : make_full_pos(child, shift_ - B).visit(v, args...);
+        auto child   = node_->inner()[0];
+        return is_leaf ? make_full_leaf_pos(child).visit(v, args...)
+                       : make_full_pos(child, shift_ - B).visit(v, args...);
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) first_sub_leaf(Visitor v, Args&&... args)
     {
         assert(shift_ == BL);
-        auto child   = node_->inner() [0];
+        auto child = node_->inner()[0];
         return make_full_leaf_pos(child).visit(v, args...);
     }
 
@@ -1258,7 +1369,7 @@ struct full_pos
     decltype(auto) first_sub_inner(Visitor v, Args&&... args)
     {
         assert(shift_ >= BL);
-        auto child   = node_->inner() [0];
+        auto child = node_->inner()[0];
         return make_full_pos(child, shift_ - B).visit(v, args...);
     }
 
@@ -1267,10 +1378,9 @@ struct full_pos
     {
         assert(idx < count());
         auto is_leaf = shift_ == BL;
-        auto child   = node_->inner() [idx];
-        return is_leaf
-            ? make_full_leaf_pos(child).visit(v, args...)
-            : make_full_pos(child, shift_ - B).visit(v, args...);
+        auto child   = node_->inner()[idx];
+        return is_leaf ? make_full_leaf_pos(child).visit(v, args...)
+                       : make_full_pos(child, shift_ - B).visit(v, args...);
     }
 
     template <typename Visitor, typename... Args>
@@ -1278,12 +1388,12 @@ struct full_pos
     {
         assert(shift_ == BL);
         assert(idx < count());
-        auto child   = node_->inner() [idx];
+        auto child = node_->inner()[idx];
         return make_full_leaf_pos(child).visit(v, args...);
     }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_regular(*this, std::forward<Args>(args)...);
     }
@@ -1303,24 +1413,28 @@ struct relaxed_pos
     static constexpr auto B  = NodeT::bits;
     static constexpr auto BL = NodeT::bits_leaf;
 
-    using node_t = NodeT;
+    using node_t    = NodeT;
     using relaxed_t = typename NodeT::relaxed_t;
     node_t* node_;
     shift_t shift_;
     relaxed_t* relaxed_;
 
     count_t count() const { return relaxed_->d.count; }
-    node_t* node()  const { return node_; }
-    size_t  size()  const { return relaxed_->d.sizes[relaxed_->d.count - 1]; }
+    node_t* node() const { return node_; }
+    size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; }
     shift_t shift() const { return shift_; }
     count_t subindex(size_t idx) const { return index(idx); }
     relaxed_t* relaxed() const { return relaxed_; }
 
     size_t size_before(count_t offset) const
-    { return offset ? relaxed_->d.sizes[offset - 1] : 0; }
+    {
+        return offset ? relaxed_->d.sizes[offset - 1] : 0;
+    }
 
     size_t size(count_t offset) const
-    { return size_sbh(offset, size_before(offset)); }
+    {
+        return size_sbh(offset, size_before(offset));
+    }
 
     size_t size_sbh(count_t offset, size_t size_before_hint) const
     {
@@ -1331,28 +1445,29 @@ struct relaxed_pos
     count_t index(size_t idx) const
     {
         auto offset = idx >> shift_;
-        while (relaxed_->d.sizes[offset] <= idx) ++offset;
+        while (relaxed_->d.sizes[offset] <= idx)
+            ++offset;
         return offset;
     }
 
-    void copy_sizes(count_t offset,
-                    count_t n,
-                    size_t init,
-                    size_t* sizes)
+    void copy_sizes(count_t offset, count_t n, size_t init, size_t* sizes)
     {
-        auto e = sizes + n;
-        auto prev = size_before(offset);
+        auto e     = sizes + n;
+        auto prev  = size_before(offset);
         auto these = relaxed_->d.sizes + offset;
         for (; sizes != e; ++sizes, ++these) {
             auto this_size = *these;
             init = *sizes = init + (this_size - prev);
+            assert(init);
             prev = this_size;
         }
     }
 
     template <typename Visitor, typename... Args>
     void each(Visitor v, Args&&... args)
-    { each_left(v, relaxed_->d.count, args...); }
+    {
+        each_left(v, relaxed_->d.count, args...);
+    }
 
     template <typename Visitor, typename... Args>
     bool each_pred(Visitor v, Args&&... args)
@@ -1364,15 +1479,15 @@ struct relaxed_pos
             for (auto i = count_t{0}; i < n; ++i) {
                 IMMER_PREFETCH(p + i + 1);
                 if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
-                    .visit(v, args...))
+                         .visit(v, args...))
                     return false;
                 s = relaxed_->d.sizes[i];
             }
         } else {
             auto ss = shift_ - B;
             for (auto i = count_t{0}; i < n; ++i) {
-                if (!visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s,
-                                             v, args...))
+                if (!visit_maybe_relaxed_sub(
+                        p[i], ss, relaxed_->d.sizes[i] - s, v, args...))
                     return false;
                 s = relaxed_->d.sizes[i];
             }
@@ -1389,17 +1504,17 @@ struct relaxed_pos
             for (; i < n; ++i) {
                 IMMER_PREFETCH(p + i + 1);
                 if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
-                    .visit(v, args...))
+                         .visit(v, args...))
                     return false;
                 s = relaxed_->d.sizes[i];
             }
         } else {
-            auto p = node_->inner();
-            auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0;
+            auto p  = node_->inner();
+            auto s  = i > 0 ? relaxed_->d.sizes[i - 1] : 0;
             auto ss = shift_ - B;
             for (; i < n; ++i) {
-                if (!visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s,
-                                             v, args...))
+                if (!visit_maybe_relaxed_sub(
+                        p[i], ss, relaxed_->d.sizes[i] - s, v, args...))
                     return false;
                 s = relaxed_->d.sizes[i];
             }
@@ -1416,15 +1531,15 @@ struct relaxed_pos
             for (auto i = count_t{0}; i < n; ++i) {
                 IMMER_PREFETCH(p + i + 1);
                 if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
-                    .visit(v, args...))
+                         .visit(v, args...))
                     return false;
                 s = relaxed_->d.sizes[i];
             }
         } else {
             auto ss = shift_ - B;
             for (auto i = count_t{0}; i < n; ++i) {
-                if (!visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s,
-                                             v, args...))
+                if (!visit_maybe_relaxed_sub(
+                        p[i], ss, relaxed_->d.sizes[i] - s, v, args...))
                     return false;
                 s = relaxed_->d.sizes[i];
             }
@@ -1443,15 +1558,15 @@ struct relaxed_pos
             for (auto i = start; i < relaxed_->d.count; ++i) {
                 IMMER_PREFETCH(p + i + 1);
                 if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
-                    .visit(v, args...))
+                         .visit(v, args...))
                     return false;
                 s = relaxed_->d.sizes[i];
             }
         } else {
             auto ss = shift_ - B;
             for (auto i = start; i < relaxed_->d.count; ++i) {
-                if (!visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s,
-                                             v, args...))
+                if (!visit_maybe_relaxed_sub(
+                        p[i], ss, relaxed_->d.sizes[i] - s, v, args...))
                     return false;
                 s = relaxed_->d.sizes[i];
             }
@@ -1472,12 +1587,12 @@ struct relaxed_pos
                 s = relaxed_->d.sizes[i];
             }
         } else {
-            auto p = node_->inner();
-            auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0;
+            auto p  = node_->inner();
+            auto s  = i > 0 ? relaxed_->d.sizes[i - 1] : 0;
             auto ss = shift_ - B;
             for (; i < n; ++i) {
-                visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s,
-                                        v, args...);
+                visit_maybe_relaxed_sub(
+                    p[i], ss, relaxed_->d.sizes[i] - s, v, args...);
                 s = relaxed_->d.sizes[i];
             }
         }
@@ -1485,11 +1600,15 @@ struct relaxed_pos
 
     template <typename Visitor, typename... Args>
     void each_sub(Visitor v, Args&&... args)
-    { each_left(v, relaxed_->d.count, args...); }
+    {
+        each_left(v, relaxed_->d.count, args...);
+    }
 
     template <typename Visitor, typename... Args>
     void each_left_sub(Visitor v, Args&&... args)
-    { each_left(v, relaxed_->d.count - 1, args...); }
+    {
+        each_left(v, relaxed_->d.count - 1, args...);
+    }
 
     template <typename Visitor, typename... Args>
     void each_left(Visitor v, count_t n, Args&&... args)
@@ -1502,20 +1621,24 @@ struct relaxed_pos
                 make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
                     .visit(v, args...);
                 s = relaxed_->d.sizes[i];
+                assert(s);
             }
         } else {
             auto ss = shift_ - B;
             for (auto i = count_t{0}; i < n; ++i) {
-                visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s,
-                                        v, args...);
+                visit_maybe_relaxed_sub(
+                    p[i], ss, relaxed_->d.sizes[i] - s, v, args...);
                 s = relaxed_->d.sizes[i];
+                assert(s);
             }
         }
     }
 
     template <typename Visitor, typename... Args>
     void each_right_sub(Visitor v, Args&&... args)
-    { each_right(v, 1, std::forward<Args>(args)...); }
+    {
+        each_right(v, 1, std::forward<Args>(args)...);
+    }
 
     template <typename Visitor, typename... Args>
     void each_right(Visitor v, count_t start, Args&&... args)
@@ -1530,25 +1653,28 @@ struct relaxed_pos
                 make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s)
                     .visit(v, args...);
                 s = relaxed_->d.sizes[i];
+                assert(s);
             }
         } else {
             auto ss = shift_ - B;
             for (auto i = start; i < relaxed_->d.count; ++i) {
-                visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s,
-                                        v, args...);
+                visit_maybe_relaxed_sub(
+                    p[i], ss, relaxed_->d.sizes[i] - s, v, args...);
                 s = relaxed_->d.sizes[i];
+                assert(s);
             }
         }
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) towards(Visitor v, size_t idx, Args&&... args)
-    { return towards_oh(v, idx, subindex(idx), args...); }
+    {
+        return towards_oh(v, idx, subindex(idx), args...);
+    }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_oh(Visitor v, size_t idx,
-                              count_t offset_hint,
-                              Args&&... args)
+    decltype(auto)
+    towards_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
     {
         assert(offset_hint == index(idx));
         auto left_size = offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0;
@@ -1556,16 +1682,18 @@ struct relaxed_pos
     }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_oh_sbh(Visitor v, size_t idx,
+    decltype(auto) towards_oh_sbh(Visitor v,
+                                  size_t idx,
                                   count_t offset_hint,
                                   size_t left_size_hint,
                                   Args&&... args)
-    { return towards_sub_oh_sbh(v, idx, offset_hint, left_size_hint, args...); }
+    {
+        return towards_sub_oh_sbh(v, idx, offset_hint, left_size_hint, args...);
+    }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_sub_oh(Visitor v, size_t idx,
-                                  count_t offset_hint,
-                                  Args&&... args)
+    decltype(auto)
+    towards_sub_oh(Visitor v, size_t idx, count_t offset_hint, Args&&... args)
     {
         assert(offset_hint == index(idx));
         auto left_size = offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0;
@@ -1573,7 +1701,8 @@ struct relaxed_pos
     }
 
     template <typename Visitor, typename... Args>
-    decltype(auto) towards_sub_oh_sbh(Visitor v, size_t idx,
+    decltype(auto) towards_sub_oh_sbh(Visitor v,
+                                      size_t idx,
                                       count_t offset_hint,
                                       size_t left_size_hint,
                                       Args&&... args)
@@ -1581,15 +1710,15 @@ struct relaxed_pos
         assert(offset_hint == index(idx));
         assert(left_size_hint ==
                (offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0));
-        auto child     = node_->inner() [offset_hint];
+        auto child     = node_->inner()[offset_hint];
         auto is_leaf   = shift_ == BL;
         auto next_size = relaxed_->d.sizes[offset_hint] - left_size_hint;
         auto next_idx  = idx - left_size_hint;
         return is_leaf
-            ? make_leaf_sub_pos(child, next_size).visit(
-                v, next_idx, args...)
-            : visit_maybe_relaxed_sub(child, shift_ - B, next_size,
-                                      v, next_idx, args...);
+                   ? make_leaf_sub_pos(child, next_size)
+                         .visit(v, next_idx, args...)
+                   : visit_maybe_relaxed_sub(
+                         child, shift_ - B, next_size, v, next_idx, args...);
     }
 
     template <typename Visitor, typename... Args>
@@ -1600,42 +1729,43 @@ struct relaxed_pos
     {
         assert(offset_hint == count() - 1);
         assert(child_size_hint == size(offset_hint));
-        auto child     = node_->inner() [offset_hint];
-        auto is_leaf   = shift_ == BL;
+        auto child   = node_->inner()[offset_hint];
+        auto is_leaf = shift_ == BL;
         return is_leaf
-            ? make_leaf_sub_pos(child, child_size_hint).visit(v, args...)
-            : visit_maybe_relaxed_sub(child, shift_ - B, child_size_hint,
-                                      v, args...);
+                   ? make_leaf_sub_pos(child, child_size_hint).visit(v, args...)
+                   : visit_maybe_relaxed_sub(
+                         child, shift_ - B, child_size_hint, v, args...);
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) last_sub(Visitor v, Args&&... args)
     {
         auto offset     = relaxed_->d.count - 1;
-        auto child      = node_->inner() [offset];
+        auto child      = node_->inner()[offset];
         auto child_size = size(offset);
         auto is_leaf    = shift_ == BL;
-        return is_leaf
-            ? make_leaf_sub_pos(child, child_size).visit(v, args...)
-            : visit_maybe_relaxed_sub(child, shift_ - B, child_size, v, args...);
+        return is_leaf ? make_leaf_sub_pos(child, child_size).visit(v, args...)
+                       : visit_maybe_relaxed_sub(
+                             child, shift_ - B, child_size, v, args...);
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) first_sub(Visitor v, Args&&... args)
     {
-        auto child      = node_->inner() [0];
+        auto child      = node_->inner()[0];
         auto child_size = relaxed_->d.sizes[0];
         auto is_leaf    = shift_ == BL;
-        return is_leaf
-            ? make_leaf_sub_pos(child, child_size).visit(v, args...)
-            : visit_maybe_relaxed_sub(child, shift_ - B, child_size, v, args...);
+        assert(child_size);
+        return is_leaf ? make_leaf_sub_pos(child, child_size).visit(v, args...)
+                       : visit_maybe_relaxed_sub(
+                             child, shift_ - B, child_size, v, args...);
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) first_sub_leaf(Visitor v, Args&&... args)
     {
         assert(shift_ == BL);
-        auto child      = node_->inner() [0];
+        auto child      = node_->inner()[0];
         auto child_size = relaxed_->d.sizes[0];
         return make_leaf_sub_pos(child, child_size).visit(v, args...);
     }
@@ -1644,33 +1774,34 @@ struct relaxed_pos
     decltype(auto) first_sub_inner(Visitor v, Args&&... args)
     {
         assert(shift_ > BL);
-        auto child      = node_->inner() [0];
+        auto child      = node_->inner()[0];
         auto child_size = relaxed_->d.sizes[0];
-        return visit_maybe_relaxed_sub(child, shift_ - B, child_size, v, args...);
+        return visit_maybe_relaxed_sub(
+            child, shift_ - B, child_size, v, args...);
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) nth_sub(count_t offset, Visitor v, Args&&... args)
     {
-        auto child      = node_->inner() [offset];
+        auto child      = node_->inner()[offset];
         auto child_size = size(offset);
         auto is_leaf    = shift_ == BL;
-        return is_leaf
-            ? make_leaf_sub_pos(child, child_size).visit(v, args...)
-            : visit_maybe_relaxed_sub(child, shift_ - B, child_size, v, args...);
+        return is_leaf ? make_leaf_sub_pos(child, child_size).visit(v, args...)
+                       : visit_maybe_relaxed_sub(
+                             child, shift_ - B, child_size, v, args...);
     }
 
     template <typename Visitor, typename... Args>
     decltype(auto) nth_sub_leaf(count_t offset, Visitor v, Args&&... args)
     {
         assert(shift_ == BL);
-        auto child      = node_->inner() [offset];
+        auto child      = node_->inner()[offset];
         auto child_size = size(offset);
         return make_leaf_sub_pos(child, child_size).visit(v, args...);
     }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_relaxed(*this, std::forward<Args>(args)...);
     }
@@ -1684,9 +1815,8 @@ template <typename Pos>
 constexpr auto is_relaxed_v = is_relaxed<Pos>::value;
 
 template <typename NodeT>
-relaxed_pos<NodeT> make_relaxed_pos(NodeT* node,
-                                    shift_t shift,
-                                    typename NodeT::relaxed_t* relaxed)
+relaxed_pos<NodeT>
+make_relaxed_pos(NodeT* node, shift_t shift, typename NodeT::relaxed_t* relaxed)
 {
     assert(node);
     assert(relaxed);
@@ -1695,8 +1825,8 @@ relaxed_pos<NodeT> make_relaxed_pos(NodeT* node,
 }
 
 template <typename NodeT, typename Visitor, typename... Args>
-decltype(auto) visit_maybe_relaxed_sub(NodeT* node, shift_t shift, size_t size,
-                                       Visitor v, Args&& ...args)
+decltype(auto) visit_maybe_relaxed_sub(
+    NodeT* node, shift_t shift, size_t size, Visitor v, Args&&... args)
 {
     assert(node);
     auto relaxed = node->relaxed();
@@ -1710,22 +1840,23 @@ decltype(auto) visit_maybe_relaxed_sub(NodeT* node, shift_t shift, size_t size,
     }
 }
 
-template <typename NodeT, shift_t Shift,
+template <typename NodeT,
+          shift_t Shift,
           bits_t B  = NodeT::bits,
           bits_t BL = NodeT::bits_leaf>
 struct relaxed_descent_pos
 {
     static_assert(Shift > 0, "not leaf...");
 
-    using node_t = NodeT;
+    using node_t    = NodeT;
     using relaxed_t = typename NodeT::relaxed_t;
     node_t* node_;
     relaxed_t* relaxed_;
 
     count_t count() const { return relaxed_->d.count; }
-    node_t* node()  const { return node_; }
+    node_t* node() const { return node_; }
     shift_t shift() const { return Shift; }
-    size_t  size()  const { return relaxed_->d.sizes[relaxed_->d.count - 1]; }
+    size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; }
 
     count_t index(size_t idx) const
     {
@@ -1738,7 +1869,8 @@ struct relaxed_descent_pos
 #if !defined(_MSC_VER)
 #pragma GCC diagnostic pop
 #endif
-        while (relaxed_->d.sizes[offset] <= idx) ++offset;
+        while (relaxed_->d.sizes[offset] <= idx)
+            ++offset;
         return offset;
     }
 
@@ -1746,17 +1878,18 @@ struct relaxed_descent_pos
     decltype(auto) descend(Visitor v, size_t idx)
     {
         auto offset    = index(idx);
-        auto child     = node_->inner() [offset];
+        auto child     = node_->inner()[offset];
         auto left_size = offset ? relaxed_->d.sizes[offset - 1] : 0;
         auto next_idx  = idx - left_size;
-        auto r  = child->relaxed();
-        return r
-            ? relaxed_descent_pos<NodeT, Shift - B>{child, r}.visit(v, next_idx)
-            : regular_descent_pos<NodeT, Shift - B>{child}.visit(v, next_idx);
+        auto r         = child->relaxed();
+        return r ? relaxed_descent_pos<NodeT, Shift - B>{child, r}.visit(
+                       v, next_idx)
+                 : regular_descent_pos<NodeT, Shift - B>{child}.visit(v,
+                                                                      next_idx);
     }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_relaxed(*this, std::forward<Args>(args)...);
     }
@@ -1765,20 +1898,21 @@ struct relaxed_descent_pos
 template <typename NodeT, bits_t B, bits_t BL>
 struct relaxed_descent_pos<NodeT, BL, B, BL>
 {
-    using node_t = NodeT;
+    using node_t    = NodeT;
     using relaxed_t = typename NodeT::relaxed_t;
     node_t* node_;
     relaxed_t* relaxed_;
 
     count_t count() const { return relaxed_->d.count; }
-    node_t* node()  const { return node_; }
+    node_t* node() const { return node_; }
     shift_t shift() const { return BL; }
-    size_t  size()  const { return relaxed_->d.sizes[relaxed_->d.count - 1]; }
+    size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; }
 
     count_t index(size_t idx) const
     {
         auto offset = (idx >> BL) & mask<B>;
-        while (relaxed_->d.sizes[offset] <= idx) ++offset;
+        while (relaxed_->d.sizes[offset] <= idx)
+            ++offset;
         return offset;
     }
 
@@ -1786,22 +1920,22 @@ struct relaxed_descent_pos<NodeT, BL, B, BL>
     decltype(auto) descend(Visitor v, size_t idx)
     {
         auto offset    = index(idx);
-        auto child     = node_->inner() [offset];
+        auto child     = node_->inner()[offset];
         auto left_size = offset ? relaxed_->d.sizes[offset - 1] : 0;
         auto next_idx  = idx - left_size;
         return leaf_descent_pos<NodeT>{child}.visit(v, next_idx);
     }
 
-    template <typename Visitor, typename ...Args>
-    decltype(auto) visit(Visitor v, Args&& ...args)
+    template <typename Visitor, typename... Args>
+    decltype(auto) visit(Visitor v, Args&&... args)
     {
         return Visitor::visit_relaxed(*this, std::forward<Args>(args)...);
     }
 };
 
 template <typename NodeT, typename Visitor, typename... Args>
-decltype(auto) visit_maybe_relaxed_descent(NodeT* node, shift_t shift,
-                                           Visitor v, size_t idx)
+decltype(auto)
+visit_maybe_relaxed_descent(NodeT* node, shift_t shift, Visitor v, size_t idx)
 {
     constexpr auto B  = NodeT::bits;
     constexpr auto BL = NodeT::bits_leaf;
@@ -1810,24 +1944,38 @@ decltype(auto) visit_maybe_relaxed_descent(NodeT* node, shift_t shift,
     auto r = node->relaxed();
     if (r) {
         switch (shift) {
-        case BL + B * 0: return relaxed_descent_pos<NodeT, BL + B * 0>{node, r}.visit(v, idx);
-        case BL + B * 1: return relaxed_descent_pos<NodeT, BL + B * 1>{node, r}.visit(v, idx);
-        case BL + B * 2: return relaxed_descent_pos<NodeT, BL + B * 2>{node, r}.visit(v, idx);
-        case BL + B * 3: return relaxed_descent_pos<NodeT, BL + B * 3>{node, r}.visit(v, idx);
-        case BL + B * 4: return relaxed_descent_pos<NodeT, BL + B * 4>{node, r}.visit(v, idx);
-        case BL + B * 5: return relaxed_descent_pos<NodeT, BL + B * 5>{node, r}.visit(v, idx);
+        case BL + B * 0:
+            return relaxed_descent_pos<NodeT, BL + B * 0>{node, r}.visit(v,
+                                                                         idx);
+        case BL + B * 1:
+            return relaxed_descent_pos<NodeT, BL + B * 1>{node, r}.visit(v,
+                                                                         idx);
+        case BL + B * 2:
+            return relaxed_descent_pos<NodeT, BL + B * 2>{node, r}.visit(v,
+                                                                         idx);
+        case BL + B * 3:
+            return relaxed_descent_pos<NodeT, BL + B * 3>{node, r}.visit(v,
+                                                                         idx);
+        case BL + B * 4:
+            return relaxed_descent_pos<NodeT, BL + B * 4>{node, r}.visit(v,
+                                                                         idx);
+        case BL + B * 5:
+            return relaxed_descent_pos<NodeT, BL + B * 5>{node, r}.visit(v,
+                                                                         idx);
 #if IMMER_DESCENT_DEEP
         default:
             for (auto level = shift; level != endshift<B, BL>; level -= B) {
                 auto r = node->relaxed();
                 if (r) {
                     auto node_idx = (idx >> level) & mask<B>;
-                    while (r->d.sizes[node_idx] <= idx) ++node_idx;
-                    if (node_idx) idx -= r->d.sizes[node_idx - 1];
-                    node = node->inner() [node_idx];
+                    while (r->d.sizes[node_idx] <= idx)
+                        ++node_idx;
+                    if (node_idx)
+                        idx -= r->d.sizes[node_idx - 1];
+                    node = node->inner()[node_idx];
                 } else {
                     do {
-                        node = node->inner() [(idx >> level) & mask<B>];
+                        node = node->inner()[(idx >> level) & mask<B>];
                     } while ((level -= B) != endshift<B, BL>);
                     return make_leaf_descent_pos(node).visit(v, idx);
                 }
diff --git a/src/immer/detail/rbts/rbtree.hpp b/src/immer/detail/rbts/rbtree.hpp
index aab5bd9fe3..c8fa4251f0 100644
--- a/src/immer/detail/rbts/rbtree.hpp
+++ b/src/immer/detail/rbts/rbtree.hpp
@@ -8,63 +8,68 @@
 
 #pragma once
 
+#include <immer/config.hpp>
 #include <immer/detail/rbts/node.hpp>
-#include <immer/detail/rbts/position.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>
+#include <stdexcept>
 
 namespace immer {
 namespace detail {
 namespace rbts {
 
-template <typename T,
-          typename MemoryPolicy,
-          bits_t B,
-          bits_t BL>
+template <typename T, typename MemoryPolicy, bits_t B, bits_t BL>
 struct rbtree
 {
     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;
+    size_t size;
+    shift_t shift;
+    node_t* root;
+    node_t* tail;
+
+    constexpr static size_t max_size()
+    {
+        auto S = sizeof(size_t) * 8;
+        return (size_t{1} << BL) * ipow(size_t{1} << B, (S - BL) / B);
+    }
+
+    static node_t* empty_root()
+    {
+        static const auto empty_ = node_t::make_inner_n(0u);
+        return empty_->inc();
+    }
 
-    static const rbtree& empty()
+    static node_t* empty_tail()
     {
-        static const rbtree empty_ {
-            0,
-            BL,
-            node_t::make_inner_n(0u),
-            node_t::make_leaf_n(0u)
-        };
-        return empty_;
+        static const auto empty_ = node_t::make_leaf_n(0u);
+        return empty_->inc();
     }
 
     template <typename U>
     static auto from_initializer_list(std::initializer_list<U> values)
     {
-        auto e = owner_t{};
-        auto result = rbtree{empty()};
+        auto e      = owner_t{};
+        auto result = rbtree{};
         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>
+    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 = rbtree{empty()};
+        auto e      = owner_t{};
+        auto result = rbtree{};
         for (; first != last; ++first)
             result.push_back_mut(e, *first);
         return result;
@@ -72,15 +77,27 @@ struct rbtree
 
     static auto from_fill(size_t n, T v)
     {
-        auto e = owner_t{};
-        auto result = rbtree{empty()};
-        while (n --> 0)
+        auto e      = owner_t{};
+        auto result = rbtree{};
+        while (n-- > 0)
             result.push_back_mut(e, v);
         return result;
     }
 
+    rbtree()
+        : size{0}
+        , shift{BL}
+        , root{empty_root()}
+        , tail{empty_tail()}
+    {
+        assert(check_tree());
+    }
+
     rbtree(size_t sz, shift_t sh, node_t* r, node_t* t)
-        : size{sz}, shift{sh}, root{r}, tail{t}
+        : size{sz}
+        , shift{sh}
+        , root{r}
+        , tail{t}
     {
         assert(check_tree());
     }
@@ -92,7 +109,7 @@ struct rbtree
     }
 
     rbtree(rbtree&& other)
-        : rbtree{empty()}
+        : rbtree{}
     {
         swap(*this, other);
     }
@@ -113,16 +130,13 @@ struct rbtree
     friend void swap(rbtree& x, rbtree& y)
     {
         using std::swap;
-        swap(x.size,  y.size);
+        swap(x.size, y.size);
         swap(x.shift, y.shift);
-        swap(x.root,  y.root);
-        swap(x.tail,  y.tail);
+        swap(x.root, y.root);
+        swap(x.tail, y.tail);
     }
 
-    ~rbtree()
-    {
-        dec();
-    }
+    ~rbtree() { dec(); }
 
     void inc() const
     {
@@ -130,20 +144,11 @@ struct rbtree
         tail->inc();
     }
 
-    void dec() const
-    {
-        traverse(dec_visitor());
-    }
+    void dec() const { traverse(dec_visitor()); }
 
-    auto tail_size() const
-    {
-        return size ? ((size - 1) & mask<BL>) + 1 : 0;
-    }
+    auto tail_size() const { return size ? ((size - 1) & mask<BL>) +1 : 0; }
 
-    auto tail_offset() const
-    {
-        return size ? (size - 1) & ~mask<BL> : 0;
-    }
+    auto tail_offset() const { return size ? (size - 1) & ~mask<BL> : 0; }
 
     template <typename Visitor, typename... Args>
     void traverse(Visitor v, Args&&... args) const
@@ -151,8 +156,10 @@ struct rbtree
         auto tail_off  = tail_offset();
         auto tail_size = size - tail_off;
 
-        if (tail_off) make_regular_sub_pos(root, shift, tail_off).visit(v, args...);
-        else make_empty_regular_pos(root).visit(v, args...);
+        if (tail_off)
+            make_regular_sub_pos(root, shift, tail_off).visit(v, args...);
+        else
+            make_empty_regular_pos(root).visit(v, args...);
 
         make_leaf_sub_pos(tail, tail_size).visit(v, args...);
     }
@@ -164,17 +171,14 @@ struct rbtree
         auto tail_size = size - tail_off;
 
         if (first < tail_off)
-            make_regular_sub_pos(root, shift, tail_off).visit(
-                v,
-                first,
-                last < tail_off ? last : tail_off,
-                args...);
+            make_regular_sub_pos(root, shift, tail_off)
+                .visit(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...);
+            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>
@@ -182,10 +186,10 @@ struct rbtree
     {
         auto tail_off  = tail_offset();
         auto tail_size = size - tail_off;
-        return (tail_off
-                ? make_regular_sub_pos(root, shift, tail_off).visit(v, args...)
-                : make_empty_regular_pos(root).visit(v, args...))
-            && make_leaf_sub_pos(tail, tail_size).visit(v, args...);
+        return (tail_off ? make_regular_sub_pos(root, shift, tail_off)
+                               .visit(v, args...)
+                         : make_empty_regular_pos(root).visit(v, args...)) &&
+               make_leaf_sub_pos(tail, tail_size).visit(v, args...);
     }
 
     template <typename Visitor, typename... Args>
@@ -194,30 +198,27 @@ struct rbtree
         auto tail_off  = tail_offset();
         auto tail_size = size - tail_off;
 
-        return
-            (first < tail_off
-             ? make_regular_sub_pos(root, shift, tail_off).visit(
-                 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);
+        return (first < tail_off ? make_regular_sub_pos(root, shift, tail_off)
+                                       .visit(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)
-            : visit_regular_descent(root, shift, v, idx);
+        auto tail_off = tail_offset();
+        return idx >= tail_off ? make_leaf_descent_pos(tail).visit(v, idx)
+                               : visit_regular_descent(root, shift, v, idx);
     }
 
     template <typename Fn>
@@ -241,18 +242,21 @@ struct rbtree
     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));
+        return traverse_p(
+            for_each_chunk_p_i_visitor{}, first, last, std::forward<Fn>(fn));
     }
 
     bool equals(const rbtree& other) const
     {
-        if (size != other.size) return false;
-        if (size == 0) return true;
-        return (size <= branches<BL>
-                || make_regular_sub_pos(root, shift, tail_offset()).visit(
-                    equals_visitor{}, other.root))
-            && make_leaf_sub_pos(tail, tail_size()).visit(
-                equals_visitor{}, other.tail);
+        if (size != other.size)
+            return false;
+        if (size == 0)
+            return true;
+        return (size <= branches<BL> ||
+                make_regular_sub_pos(root, shift, tail_offset())
+                    .visit(equals_visitor{}, other.root)) &&
+               make_leaf_sub_pos(tail, tail_size())
+                   .visit(equals_visitor{}, other.tail);
     }
 
     void ensure_mutable_tail(edit_t e, count_t n)
@@ -267,29 +271,31 @@ struct rbtree
     void push_back_mut(edit_t e, T value)
     {
         auto tail_off = tail_offset();
-        auto ts = size - tail_off;
+        auto ts       = size - tail_off;
         if (ts < branches<BL>) {
             ensure_mutable_tail(e, ts);
             new (&tail->leaf()[ts]) T{std::move(value)};
         } else {
             auto new_tail = node_t::make_leaf_e(e, std::move(value));
-            try {
+            IMMER_TRY {
                 if (tail_off == size_t{branches<B>} << shift) {
                     auto new_root = node_t::make_inner_e(e);
-                    try {
+                    IMMER_TRY {
                         auto path = node_t::make_path_e(e, shift, tail);
-                        new_root->inner() [0] = root;
-                        new_root->inner() [1] = path;
-                        root = new_root;
-                        tail = new_tail;
+                        new_root->inner()[0] = root;
+                        new_root->inner()[1] = path;
+                        root                 = new_root;
+                        tail                 = new_tail;
                         shift += B;
-                    } catch (...) {
+                    }
+                    IMMER_CATCH (...) {
                         node_t::delete_inner_e(new_root);
-                        throw;
+                        IMMER_RETHROW;
                     }
                 } else if (tail_off) {
-                    auto new_root = make_regular_sub_pos(root, shift, tail_off)
-                        .visit(push_tail_mut_visitor<node_t>{}, e, tail);
+                    auto new_root =
+                        make_regular_sub_pos(root, shift, tail_off)
+                            .visit(push_tail_mut_visitor<node_t>{}, e, tail);
                     root = new_root;
                     tail = new_tail;
                 } else {
@@ -299,9 +305,10 @@ struct rbtree
                     root = new_root;
                     tail = new_tail;
                 }
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 node_t::delete_leaf(new_tail, 1);
-                throw;
+                IMMER_RETHROW;
             }
         }
         ++size;
@@ -310,40 +317,43 @@ struct rbtree
     rbtree push_back(T value) const
     {
         auto tail_off = tail_offset();
-        auto ts = size - tail_off;
+        auto ts       = size - tail_off;
         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 };
+            auto new_tail =
+                node_t::copy_leaf_emplace(tail, ts, std::move(value));
+            return {size + 1, shift, root->inc(), new_tail};
         } else {
             auto new_tail = node_t::make_leaf_n(1, std::move(value));
-            try {
+            IMMER_TRY {
                 if (tail_off == size_t{branches<B>} << shift) {
                     auto new_root = node_t::make_inner_n(2);
-                    try {
-                        auto path = node_t::make_path(shift, tail);
-                        new_root->inner() [0] = root;
-                        new_root->inner() [1] = path;
+                    IMMER_TRY {
+                        auto path            = node_t::make_path(shift, tail);
+                        new_root->inner()[0] = root;
+                        new_root->inner()[1] = path;
                         root->inc();
                         tail->inc();
-                        return { size + 1, shift + B, new_root, new_tail };
-                    } catch (...) {
+                        return {size + 1, shift + B, new_root, new_tail};
+                    }
+                    IMMER_CATCH (...) {
                         node_t::delete_inner(new_root, 2);
-                        throw;
+                        IMMER_RETHROW;
                     }
                 } else if (tail_off) {
-                    auto new_root = make_regular_sub_pos(root, shift, tail_off)
-                        .visit(push_tail_visitor<node_t>{}, tail);
+                    auto new_root =
+                        make_regular_sub_pos(root, shift, tail_off)
+                            .visit(push_tail_visitor<node_t>{}, tail);
                     tail->inc();
-                    return { size + 1, shift, new_root, new_tail };
+                    return {size + 1, shift, new_root, new_tail};
                 } else {
                     auto new_root = node_t::make_path(shift, tail);
                     tail->inc();
-                    return { size + 1, shift, new_root, new_tail };
+                    return {size + 1, shift, new_root, new_tail};
                 }
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 node_t::delete_leaf(new_tail, 1);
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
@@ -358,7 +368,7 @@ struct rbtree
         auto tail_off = tail_offset();
         if (idx >= tail_off) {
             ensure_mutable_tail(e, size - tail_off);
-            return tail->leaf() [idx & mask<BL>];
+            return tail->leaf()[idx & mask<BL>];
         } else {
             return make_regular_sub_pos(root, shift, tail_off)
                 .visit(get_mut_visitor<node_t>{}, idx, e, &root);
@@ -373,67 +383,58 @@ struct rbtree
     const T& get_check(size_t index) const
     {
         if (index >= size)
-            throw std::out_of_range{"index out of range"};
+            IMMER_THROW(std::out_of_range{"index out of range"});
         return descend(get_visitor<T>(), index);
     }
 
-    const T& front() const
-    {
-        return get(0);
-    }
+    const T& front() const { return get(0); }
 
-    const T& back() const
-    {
-        return tail->leaf()[(size - 1) & mask<BL>];
-    }
+    const T& back() const { return tail->leaf()[(size - 1) & mask<BL>]; }
 
     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));
+        elem       = std::forward<FnT>(fn)(std::move(elem));
     }
 
     template <typename FnT>
     rbtree update(size_t idx, FnT&& fn) const
     {
-        auto tail_off  = tail_offset();
+        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 };
+            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  = make_regular_sub_pos(root, shift, tail_off)
-                .visit(update_visitor<node_t>{}, idx, fn);
-            return { size, shift, new_root, tail->inc() };
+            auto new_root = make_regular_sub_pos(root, shift, tail_off)
+                                .visit(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);
-            });
+        update_mut(e, idx, [&](auto&&) { return std::move(value); });
     }
 
     rbtree assoc(size_t idx, T value) const
     {
-        return update(idx, [&] (auto&&) {
-                return std::move(value);
-            });
+        return update(idx, [&](auto&&) { return std::move(value); });
     }
 
     rbtree take(size_t new_size) const
     {
         auto tail_off = tail_offset();
         if (new_size == 0) {
-            return empty();
+            return {};
         } 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 };
+            return {new_size, shift, root->inc(), new_tail};
         } else {
             using std::get;
             auto l = new_size - 1;
@@ -443,11 +444,11 @@ struct rbtree
             auto new_root  = get<1>(r);
             auto new_tail  = get<3>(r);
             if (new_root) {
-                assert(new_root->compute_shift() == get<0>(r));
+                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 };
+                return {new_size, new_shift, new_root, new_tail};
             } else {
-                return { new_size, BL, empty().root->inc(), new_tail };
+                return {new_size, BL, empty_root(), new_tail};
             }
         }
     }
@@ -457,14 +458,14 @@ struct rbtree
         auto tail_off = tail_offset();
         if (new_size == 0) {
             // todo: more efficient?
-            *this = empty();
+            *this = {};
         } 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);
+                detail::destroy_n(tail->leaf() + newts, ts - newts);
             } else {
                 auto new_tail = node_t::copy_leaf_e(e, tail, newts);
                 dec_leaf(tail, ts);
@@ -484,12 +485,12 @@ struct rbtree
                 root  = new_root;
                 shift = new_shift;
             } else {
-                root  = empty().root->inc();
+                root  = empty_root();
                 shift = BL;
             }
             dec_leaf(tail, size - tail_off);
-            size  = new_size;
-            tail  = new_tail;
+            size = new_size;
+            tail = new_tail;
             return;
         }
     }
@@ -509,7 +510,7 @@ struct rbtree
     {
 #if IMMER_DEBUG_DEEP_CHECK
         if (tail_size() > 0)
-            assert(tail->check(0, tail_size()));
+            assert(tail->check(endshift<B, BL>, tail_size()));
 #endif
         return true;
     }
@@ -520,7 +521,7 @@ struct rbtree
         if (tail_offset() > 0)
             assert(root->check(shift, tail_offset()));
         else {
-            assert(root->kind() == node_t::kind_t::inner);
+            IMMER_ASSERT_TAGGED(root->kind() == node_t::kind_t::inner);
             assert(shift == BL);
         }
 #endif
diff --git a/src/immer/detail/rbts/rbtree_iterator.hpp b/src/immer/detail/rbts/rbtree_iterator.hpp
index 672484eae3..90613b10b9 100644
--- a/src/immer/detail/rbts/rbtree_iterator.hpp
+++ b/src/immer/detail/rbts/rbtree_iterator.hpp
@@ -8,8 +8,8 @@
 
 #pragma once
 
-#include <immer/detail/rbts/rbtree.hpp>
 #include <immer/detail/iterator_facade.hpp>
+#include <immer/detail/rbts/rbtree.hpp>
 
 namespace immer {
 namespace detail {
@@ -26,22 +26,23 @@ struct rbtree_iterator
 {
     using tree_t = rbtree<T, MP, B, BL>;
 
-    struct end_t {};
+    struct end_t
+    {};
 
     rbtree_iterator() = default;
 
     rbtree_iterator(const tree_t& v)
-        : v_    { &v }
-        , i_    { 0 }
-        , base_ { ~size_t{} }
-        , curr_ { nullptr }
+        : v_{&v}
+        , i_{0}
+        , base_{~size_t{}}
+        , curr_{nullptr}
     {}
 
     rbtree_iterator(const tree_t& v, end_t)
-        : v_    { &v }
-        , i_    { v.size }
-        , base_ { ~size_t{} }
-        , curr_ { nullptr }
+        : v_{&v}
+        , i_{v.size}
+        , base_{~size_t{}}
+        , curr_{nullptr}
     {}
 
     const tree_t& impl() const { return *v_; }
@@ -50,9 +51,9 @@ struct rbtree_iterator
 private:
     friend iterator_core_access;
 
-    const tree_t*    v_;
-    size_t           i_;
-    mutable size_t   base_;
+    const tree_t* v_;
+    size_t i_;
+    mutable size_t base_;
     mutable const T* curr_ = nullptr;
 
     void increment()
@@ -74,16 +75,12 @@ struct rbtree_iterator
         i_ += n;
     }
 
-    bool equal(const rbtree_iterator& other) const
-    {
-        return i_ == other.i_;
-    }
+    bool equal(const rbtree_iterator& other) const { return i_ == other.i_; }
 
     std::ptrdiff_t distance_to(const rbtree_iterator& other) const
     {
-        return other.i_ > i_
-            ?   static_cast<std::ptrdiff_t>(other.i_ - i_)
-            : - static_cast<std::ptrdiff_t>(i_ - other.i_);
+        return other.i_ > i_ ? static_cast<std::ptrdiff_t>(other.i_ - i_)
+                             : -static_cast<std::ptrdiff_t>(i_ - other.i_);
     }
 
     const T& dereference() const
diff --git a/src/immer/detail/rbts/rrbtree.hpp b/src/immer/detail/rbts/rrbtree.hpp
index 7d26e50a66..843921ba22 100644
--- a/src/immer/detail/rbts/rrbtree.hpp
+++ b/src/immer/detail/rbts/rrbtree.hpp
@@ -10,68 +10,80 @@
 
 #include <immer/config.hpp>
 #include <immer/detail/rbts/node.hpp>
-#include <immer/detail/rbts/position.hpp>
 #include <immer/detail/rbts/operations.hpp>
+#include <immer/detail/rbts/position.hpp>
 
 #include <immer/detail/type_traits.hpp>
 
 #include <cassert>
+#include <limits>
 #include <memory>
 #include <numeric>
+#include <stdexcept>
 
 namespace immer {
 namespace detail {
 namespace rbts {
 
-template <typename T,
-          typename MemoryPolicy,
-          bits_t   B,
-          bits_t   BL>
+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>
+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;
+    size_t size;
     shift_t shift;
     node_t* root;
     node_t* tail;
 
-    static const rrbtree& empty()
+    constexpr static size_t max_size()
+    {
+        auto S = sizeof(size_t) * 8;
+        return ((size_t{1} << BL) - std::min(size_t{BL}, size_t{2})) *
+               ipow((size_t{1} << B) - 2, (S - BL) / B);
+    }
+
+    static node_t* empty_root()
+    {
+        static const auto empty_ = []{
+            constexpr auto size = node_t::sizeof_inner_n(0);
+            static std::aligned_storage_t<size, alignof(std::max_align_t)> storage;
+            return node_t::make_inner_n_into(&storage, size, 0u);
+        }();
+        return empty_->inc();
+    }
+
+    static node_t* empty_tail()
     {
-        static const rrbtree empty_ {
-            0,
-            BL,
-            node_t::make_inner_n(0u),
-            node_t::make_leaf_n(0u)
-        };
-        return empty_;
+        static const auto empty_ = []{
+            constexpr auto size = node_t::sizeof_leaf_n(0);
+            static std::aligned_storage_t<size, alignof(std::max_align_t)> storage;
+            return node_t::make_leaf_n_into(&storage, size, 0u);
+        }();
+        return empty_->inc();
     }
 
     template <typename U>
     static auto from_initializer_list(std::initializer_list<U> values)
     {
-        auto e = owner_t{};
-        auto result = rrbtree{empty()};
+        auto e      = owner_t{};
+        auto result = rrbtree{};
         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>
+    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()};
+        auto e      = owner_t{};
+        auto result = rrbtree{};
         for (; first != last; ++first)
             result.push_back_mut(e, *first);
         return result;
@@ -79,27 +91,39 @@ struct rrbtree
 
     static auto from_fill(size_t n, T v)
     {
-        auto e = owner_t{};
-        auto result = rrbtree{empty()};
-        while (n --> 0)
+        auto e      = owner_t{};
+        auto result = rrbtree{};
+        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}
+    rrbtree() noexcept
+        : size{0}
+        , shift{BL}
+        , root{empty_root()}
+        , tail{empty_tail()}
     {
         assert(check_tree());
     }
 
-    rrbtree(const rrbtree& other)
+    rrbtree(size_t sz, shift_t sh, node_t* r, node_t* t) noexcept
+        : size{sz}
+        , shift{sh}
+        , root{r}
+        , tail{t}
+    {
+        assert(check_tree());
+    }
+
+    rrbtree(const rrbtree& other) noexcept
         : rrbtree{other.size, other.shift, other.root, other.tail}
     {
         inc();
     }
 
-    rrbtree(rrbtree&& other)
-        : rrbtree{empty()}
+    rrbtree(rrbtree&& other) noexcept
+        : rrbtree{}
     {
         swap(*this, other);
     }
@@ -111,25 +135,22 @@ struct rrbtree
         return *this;
     }
 
-    rrbtree& operator=(rrbtree&& other)
+    rrbtree& operator=(rrbtree&& other) noexcept
     {
         swap(*this, other);
         return *this;
     }
 
-    friend void swap(rrbtree& x, rrbtree& y)
+    friend void swap(rrbtree& x, rrbtree& y) noexcept
     {
         using std::swap;
-        swap(x.size,  y.size);
+        swap(x.size, y.size);
         swap(x.shift, y.shift);
-        swap(x.root,  y.root);
-        swap(x.tail,  y.tail);
+        swap(x.root, y.root);
+        swap(x.tail, y.tail);
     }
 
-    ~rrbtree()
-    {
-        dec();
-    }
+    ~rrbtree() { dec(); }
 
     void inc() const
     {
@@ -137,24 +158,18 @@ struct rrbtree
         tail->inc();
     }
 
-    void dec() const
-    {
-        traverse(dec_visitor());
-    }
+    void dec() const { traverse(dec_visitor()); }
 
-    auto tail_size() const
-    {
-        return size - tail_offset();
-    }
+    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;
+        return r      ? r->d.sizes[r->d.count - 1]
+               : size ? (size - 1) & ~mask<BL>
+                      /* otherwise */
+                      : 0;
     }
 
     template <typename Visitor, typename... Args>
@@ -163,11 +178,15 @@ struct rrbtree
         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_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...);
+        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>
@@ -177,16 +196,19 @@ struct rrbtree
         auto tail_size = size - tail_off;
 
         if (first < tail_off)
-            visit_maybe_relaxed_sub(root, shift, tail_off, v,
+            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...);
+            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>
@@ -195,11 +217,10 @@ struct rrbtree
         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...));
+                    ? 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>
@@ -207,29 +228,31 @@ struct rrbtree
     {
         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);
+        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();
+        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);
+                   ? make_leaf_descent_pos(tail).visit(v, idx - tail_off)
+                   : visit_maybe_relaxed_descent(root, shift, v, idx);
     }
 
     template <typename Fn>
@@ -253,15 +276,18 @@ struct rrbtree
     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));
+        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();
+        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) {
@@ -269,114 +295,137 @@ struct rrbtree
             // 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))
+                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))
+                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);
+        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
+    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);
+            auto new_root =
+                make_relaxed_pos(root, shift, r)
+                    .visit(push_tail_visitor<node_t>{}, tail, tail_size);
             if (new_root)
-                return { shift, 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;
+                IMMER_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;
+                    assert(size);
+                    assert(tail_size);
                     new_root->relaxed()->d.count = 2u;
-                } catch (...) {
+                }
+                IMMER_CATCH (...) {
                     node_t::delete_inner_r(new_root, 2);
-                    throw;
+                    IMMER_RETHROW;
                 }
-                return { shift + B, new_root };
+                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 (...) {
+            IMMER_TRY {
+                auto new_path        = node_t::make_path(shift, tail);
+                new_root->inner()[0] = root->inc();
+                new_root->inner()[1] = new_path;
+            }
+            IMMER_CATCH (...) {
                 node_t::delete_inner(new_root, 2);
-                throw;
+                IMMER_RETHROW;
             }
-            return { shift + B, new_root };
+            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 { shift, new_root };
+                                .visit(push_tail_visitor<node_t>{}, tail);
+            return std::make_tuple(shift, new_root);
         } else {
-            return { shift, node_t::make_path(shift, tail) };
+            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)
+    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);
+            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;
+                IMMER_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;
+                    assert(tail_off);
+                    assert(tail_size);
                     new_root->relaxed()->d.count = 2u;
-                    root = new_root;
+                    root                         = new_root;
                     shift += B;
-                } catch (...) {
+                }
+                IMMER_CATCH (...) {
                     node_t::delete_inner_r_e(new_root);
-                    throw;
+                    IMMER_RETHROW;
                 }
             }
         } 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;
+            IMMER_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 (...) {
+            }
+            IMMER_CATCH (...) {
                 node_t::delete_inner_e(new_root);
-                throw;
+                IMMER_RETHROW;
             }
         } else if (tail_off) {
-            auto new_root = make_regular_sub_pos(root, shift, tail_off)
-                .visit(push_tail_mut_visitor<node_t>{}, e, tail);
+            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);
@@ -404,12 +453,13 @@ struct rrbtree
             using std::get;
             auto new_tail = node_t::make_leaf_e(e, std::move(value));
             auto tail_off = tail_offset();
-            try {
+            IMMER_TRY {
                 push_tail_mut(e, tail_off, tail, ts);
                 tail = new_tail;
-            } catch (...) {
+            }
+            IMMER_CATCH (...) {
                 node_t::delete_leaf(new_tail, 1u);
-                throw;
+                IMMER_RETHROW;
             }
         }
         ++size;
@@ -419,39 +469,38 @@ struct rrbtree
     {
         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 };
+            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);
+            IMMER_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 (...) {
+                return {size + 1, get<0>(new_root), get<1>(new_root), new_tail};
+            }
+            IMMER_CATCH (...) {
                 node_t::delete_leaf(new_tail, 1u);
-                throw;
+                IMMER_RETHROW;
             }
         }
     }
 
-    std::tuple<const T*, size_t, size_t>
-    region_for(size_t idx) const
+    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 { tail->leaf(), tail_off, size };
+            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);
+                root, shift, tail_off, region_for_visitor<T>(), idx);
             auto first = idx - get<1>(subs);
             auto end   = first + get<2>(subs);
-            return { get<0>(subs), first, end };
+            return std::make_tuple(get<0>(subs), first, end);
         }
     }
 
@@ -460,11 +509,15 @@ struct rrbtree
         auto tail_off = tail_offset();
         if (idx >= tail_off) {
             ensure_mutable_tail(e, size - tail_off);
-            return tail->leaf() [(idx - tail_off) & mask<BL>];
+            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);
+            return visit_maybe_relaxed_sub(root,
+                                           shift,
+                                           tail_off,
+                                           get_mut_visitor<node_t>{},
+                                           idx,
+                                           e,
+                                           &root);
         }
     }
 
@@ -476,70 +529,60 @@ struct rrbtree
     const T& get_check(size_t index) const
     {
         if (index >= size)
-            throw std::out_of_range{"out of range"};
+            IMMER_THROW(std::out_of_range{"out of range"});
         return descend(get_visitor<T>(), index);
     }
 
-    const T& front() const
-    {
-        return get(0);
-    }
+    const T& front() const { return get(0); }
 
-    const T& back() const
-    {
-        return get(size - 1);
-    }
+    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));
+        elem       = std::forward<FnT>(fn)(std::move(elem));
     }
 
     template <typename FnT>
     rrbtree update(size_t idx, FnT&& fn) const
     {
-        auto tail_off  = tail_offset();
+        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 };
+            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() };
+            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);
-            });
+        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);
-            });
+        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();
+            *this = {};
         } 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);
+                detail::destroy_n(tail->leaf() + newts, ts - newts);
             } else {
                 auto new_tail = node_t::copy_leaf_e(e, tail, newts);
                 dec_leaf(tail, ts);
@@ -559,12 +602,12 @@ struct rrbtree
                 root  = new_root;
                 shift = new_shift;
             } else {
-                root  = empty().root->inc();
+                root  = empty_root();
                 shift = BL;
             }
             dec_leaf(tail, size - tail_off);
-            size  = new_size;
-            tail  = new_tail;
+            size = new_size;
+            tail = new_tail;
             return;
         }
     }
@@ -573,12 +616,12 @@ struct rrbtree
     {
         auto tail_off = tail_offset();
         if (new_size == 0) {
-            return empty();
+            return {};
         } 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 };
+            return {new_size, shift, root->inc(), new_tail};
         } else {
             using std::get;
             auto l = new_size - 1;
@@ -588,11 +631,11 @@ struct rrbtree
             auto new_root  = get<1>(r);
             auto new_tail  = get<3>(r);
             if (new_root) {
-                assert(new_root->compute_shift() == get<0>(r));
+                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 };
+                return {new_size, new_shift, new_root, new_tail};
             } else {
-                return { new_size, BL, empty().root->inc(), new_tail };
+                return {new_size, BL, empty_root(), new_tail};
             }
         }
     }
@@ -604,27 +647,28 @@ struct rrbtree
         if (elems == 0) {
             return;
         } else if (elems >= size) {
-            *this = empty();
+            *this = {};
         } else if (elems == tail_off) {
             dec_inner(root, shift, tail_off);
             shift = BL;
-            root  = empty().root->inc();
+            root  = empty_root();
             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) {
+            tail   = get<1>(make_leaf_sub_pos(tail, size - tail_off)
+                              .visit(v, elems - tail_off, e));
+            if (tail_off) {
                 dec_inner(root, shift, tail_off);
                 shift = BL;
-                root  = empty().root->inc();
+                root  = empty_root();
             }
             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);
+            auto r =
+                visit_maybe_relaxed_sub(root, shift, tail_off, v, elems, e);
             shift = get<0>(r);
             root  = get<1>(r);
             size -= elems;
@@ -637,28 +681,28 @@ struct rrbtree
         if (elems == 0) {
             return *this;
         } else if (elems >= size) {
-            return empty();
+            return {};
         } else if (elems == tail_offset()) {
-            return { size - elems, BL, empty().root->inc(), tail->inc() };
+            return {size - elems, BL, empty_root(), 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 };
+            auto new_tail =
+                node_t::copy_leaf(tail, elems - tail_off, size - tail_off);
+            return {size - elems, BL, empty_root(), 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 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 {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));
+        assert(r.size + size <= max_size());
         using std::get;
         if (size == 0)
             return r;
@@ -669,57 +713,68 @@ struct rrbtree
             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);
+                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() };
+                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 };
+                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 (...) {
+                auto add_tail =
+                    node_t::copy_leaf(tail, tail_size, r.tail, remaining);
+                IMMER_TRY {
+                    auto new_tail =
+                        node_t::copy_leaf(r.tail, remaining, r.size);
+                    IMMER_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};
+                    }
+                    IMMER_CATCH (...) {
                         node_t::delete_leaf(new_tail, r.size - remaining);
-                        throw;
+                        IMMER_RETHROW;
                     }
-                } catch (...) {
+                }
+                IMMER_CATCH (...) {
                     node_t::delete_leaf(add_tail, branches<BL>);
-                    throw;
+                    IMMER_RETHROW;
                 }
             }
         } 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();
-            assert(new_shift == new_root->compute_shift());
+            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() };
+            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 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();
-            assert(new_shift == new_root->compute_shift());
+            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() };
+            return {size + r.size, new_shift, new_root, r.tail->inc()};
         }
     }
 
@@ -746,80 +801,108 @@ struct rrbtree
                 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);
+                detail::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 {
+                detail::uninitialized_copy(r.tail->leaf(),
+                                           r.tail->leaf() + remaining,
+                                           l.tail->leaf() + tail_size);
+                IMMER_TRY {
+                    auto new_tail =
+                        node_t::copy_leaf_e(el, r.tail, remaining, r.size);
+                    IMMER_TRY {
                         l.push_tail_mut(el, tail_offst, l.tail, branches<BL>);
                         l.tail = new_tail;
                         l.size += r.size;
                         return;
-                    } catch (...) {
+                    }
+                    IMMER_CATCH (...) {
                         node_t::delete_leaf(new_tail, r.size - remaining);
-                        throw;
+                        IMMER_RETHROW;
                     }
-                } catch (...) {
-                    destroy_n(r.tail->leaf() + tail_size, remaining);
-                    throw;
+                }
+                IMMER_CATCH (...) {
+                    detail::destroy_n(r.tail->leaf() + tail_size, remaining);
+                    IMMER_RETHROW;
                 }
             }
         } 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());
-                assert(concated.shift() == concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(), l.size + r.tail_offset()));
+                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());
                 l.size += r.size;
                 l.shift = concated.shift();
                 l.root  = concated.node();
                 l.tail  = r.tail;
+                assert(l.check_tree());
             } 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() };
+                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()};
+                assert(l.check_tree());
                 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());
-                assert(concated.shift() == concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(), l.size + r.tail_offset()));
+                assert(l.check_tree());
+                assert(r.check_tree());
+                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());
                 l.size += r.size;
                 l.shift = concated.shift();
                 l.root  = concated.node();
                 l.tail  = r.tail;
+                assert(l.check_tree());
             } 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() };
+                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()};
             }
         }
     }
@@ -841,13 +924,12 @@ struct rrbtree
                 // 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);
+                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() };
+                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
@@ -857,83 +939,111 @@ struct rrbtree
                 //
                 // 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 };
+                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 {
+                auto add_tail  = node_t::copy_leaf_e(
+                    er, l.tail, tail_size, r.tail, remaining);
+                IMMER_TRY {
+                    auto new_tail =
+                        node_t::copy_leaf_e(er, r.tail, remaining, r.size);
+                    IMMER_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 };
+                        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 (...) {
+                    }
+                    IMMER_CATCH (...) {
                         node_t::delete_leaf(new_tail, r.size - remaining);
-                        throw;
+                        IMMER_RETHROW;
                     }
-                } catch (...) {
+                }
+                IMMER_CATCH (...) {
                     node_t::delete_leaf(add_tail, branches<BL>);
-                    throw;
+                    IMMER_RETHROW;
                 }
-                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());
-                assert(concated.shift() == concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(), l.size + r.tail_offset()));
+                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());
                 r.size += l.size;
                 r.shift = concated.shift();
                 r.root  = concated.node();
+                assert(r.check_tree());
             } 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() };
+                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());
-                assert(concated.shift() == concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(), l.size + r.tail_offset()));
+                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());
                 r.size += l.size;
                 r.shift = concated.shift();
                 r.root  = concated.node();
+                assert(r.check_tree());
                 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() };
+                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;
             }
         }
@@ -961,12 +1071,12 @@ struct rrbtree
                 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);
+                                               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);
+                    detail::uninitialized_copy(r.tail->leaf(),
+                                               r.tail->leaf() + r.size,
+                                               l.tail->leaf() + tail_size);
                 l.size += r.size;
                 return;
             } else {
@@ -974,75 +1084,103 @@ struct rrbtree
                 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);
+                                               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 {
+                    detail::uninitialized_copy(r.tail->leaf(),
+                                               r.tail->leaf() + remaining,
+                                               l.tail->leaf() + tail_size);
+                IMMER_TRY {
+                    auto new_tail =
+                        node_t::copy_leaf_e(el, r.tail, remaining, r.size);
+                    IMMER_TRY {
                         l.push_tail_mut(el, tail_offst, l.tail, branches<BL>);
                         l.tail = new_tail;
                         l.size += r.size;
                         return;
-                    } catch (...) {
+                    }
+                    IMMER_CATCH (...) {
                         node_t::delete_leaf(new_tail, r.size - remaining);
-                        throw;
+                        IMMER_RETHROW;
                     }
-                } catch (...) {
-                    destroy_n(r.tail->leaf() + tail_size, remaining);
-                    throw;
+                }
+                IMMER_CATCH (...) {
+                    detail::destroy_n(r.tail->leaf() + tail_size, remaining);
+                    IMMER_RETHROW;
                 }
             }
         } 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());
-                assert(concated.shift() == concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(), l.size + r.tail_offset()));
+                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());
                 l.size += r.size;
                 l.shift = concated.shift();
                 l.root  = concated.node();
                 l.tail  = r.tail;
+                assert(l.check_tree());
                 r.hard_reset();
+                return;
             } 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() };
+                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());
-                assert(concated.shift() == concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(), l.size + r.tail_offset()));
+                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());
                 l.size += r.size;
                 l.shift = concated.shift();
                 l.root  = concated.node();
                 l.tail  = r.tail;
+                assert(l.check_tree());
                 r.hard_reset();
+                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());
-                l = { l.size + r.size, concated.shift(),
-                      concated.node(), r.tail->inc() };
+                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()};
+                return;
             }
         }
     }
@@ -1064,10 +1202,9 @@ struct rrbtree
                 // 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() };
+                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
@@ -1077,85 +1214,116 @@ struct rrbtree
                 //
                 // 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 };
+                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 {
+                auto add_tail  = node_t::copy_leaf_e(
+                    er, l.tail, tail_size, r.tail, remaining);
+                IMMER_TRY {
+                    auto new_tail =
+                        node_t::copy_leaf_e(er, r.tail, remaining, r.size);
+                    IMMER_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 };
+                        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 (...) {
+                    }
+                    IMMER_CATCH (...) {
                         node_t::delete_leaf(new_tail, r.size - remaining);
-                        throw;
+                        IMMER_RETHROW;
                     }
-                } catch (...) {
+                }
+                IMMER_CATCH (...) {
                     node_t::delete_leaf(add_tail, branches<BL>);
-                    throw;
+                    IMMER_RETHROW;
                 }
-                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());
-                assert(concated.shift() == concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(), l.size + r.tail_offset()));
+                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();
+                assert(r.check_tree());
                 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() };
+                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());
-                assert(concated.shift() == concated.node()->compute_shift());
-                assert(concated.node()->check(concated.shift(), l.size + r.tail_offset()));
+                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();
+                assert(r.check_tree());
                 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() };
+                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()};
             }
         }
     }
@@ -1163,11 +1331,10 @@ struct rrbtree
     void hard_reset()
     {
         assert(supports_transient_concat);
-        auto&& empty_ = empty();
-        size = empty_.size;
-        shift = empty_.shift;
-        root = empty_.root;
-        tail = empty_.tail;
+        size  = 0;
+        shift = BL;
+        root  = empty_root();
+        tail  = empty_tail();
     }
 
     bool check_tree() const
@@ -1198,7 +1365,7 @@ struct rrbtree
         if (tail_offset() > 0)
             assert(root->check(shift, tail_offset()));
         else {
-            assert(root->kind() == node_t::kind_t::inner);
+            IMMER_ASSERT_TAGGED(root->kind() == node_t::kind_t::inner);
             assert(shift == BL);
         }
 #endif
@@ -1208,8 +1375,7 @@ struct rrbtree
 #if IMMER_DEBUG_PRINT
     void debug_print(std::ostream& out) const
     {
-        out
-            << "--" << std::endl
+        out << "--" << std::endl
             << "{" << std::endl
             << "  size  = " << size << std::endl
             << "  shift = " << shift << std::endl
@@ -1222,7 +1388,7 @@ struct rrbtree
 
     void debug_print_indent(std::ostream& out, unsigned indent) const
     {
-        while (indent --> 0)
+        while (indent-- > 0)
             out << ' ';
     }
 
@@ -1237,14 +1403,12 @@ struct rrbtree
         if (shift == endshift<B, BL>) {
             debug_print_indent(out, indent);
             out << "- {" << size << "} "
-                << pretty_print_array(node->leaf(), size)
-                << std::endl;
+                << 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;
+                << 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,
@@ -1257,8 +1421,8 @@ struct rrbtree
         } else {
             debug_print_indent(out, indent);
             out << "+ {" << size << "}" << std::endl;
-            auto count = (size >> shift)
-                + (size - ((size >> shift) << shift) > 0);
+            auto count =
+                (size >> shift) + (size - ((size >> shift) << shift) > 0);
             if (count) {
                 for (auto i = count_t{}; i < count - 1; ++i)
                     debug_print_node(out,
diff --git a/src/immer/detail/rbts/rrbtree_iterator.hpp b/src/immer/detail/rbts/rrbtree_iterator.hpp
index a40f7f486d..af967774e7 100644
--- a/src/immer/detail/rbts/rrbtree_iterator.hpp
+++ b/src/immer/detail/rbts/rrbtree_iterator.hpp
@@ -8,8 +8,8 @@
 
 #pragma once
 
-#include <immer/detail/rbts/rrbtree.hpp>
 #include <immer/detail/iterator_facade.hpp>
+#include <immer/detail/rbts/rrbtree.hpp>
 
 namespace immer {
 namespace detail {
@@ -27,7 +27,8 @@ struct rrbtree_iterator
     using tree_t   = rrbtree<T, MP, B, BL>;
     using region_t = std::tuple<const T*, size_t, size_t>;
 
-    struct end_t {};
+    struct end_t
+    {};
 
     const tree_t& impl() const { return *v_; }
     size_t index() const { return i_; }
@@ -35,23 +36,22 @@ struct rrbtree_iterator
     rrbtree_iterator() = default;
 
     rrbtree_iterator(const tree_t& v)
-        : v_    { &v }
-        , i_    { 0 }
-        , curr_ { nullptr, ~size_t{}, ~size_t{} }
-    {
-    }
+        : v_{&v}
+        , i_{0}
+        , curr_{nullptr, ~size_t{}, ~size_t{}}
+    {}
 
     rrbtree_iterator(const tree_t& v, end_t)
-        : v_    { &v }
-        , i_    { v.size }
-        , curr_ { nullptr, ~size_t{}, ~size_t{} }
+        : v_{&v}
+        , i_{v.size}
+        , curr_{nullptr, ~size_t{}, ~size_t{}}
     {}
 
 private:
     friend iterator_core_access;
 
     const tree_t* v_;
-    size_t   i_;
+    size_t i_;
     mutable region_t curr_;
 
     void increment()
@@ -76,16 +76,12 @@ struct rrbtree_iterator
         i_ += n;
     }
 
-    bool equal(const rrbtree_iterator& other) const
-    {
-        return i_ == other.i_;
-    }
+    bool equal(const rrbtree_iterator& other) const { return i_ == other.i_; }
 
     std::ptrdiff_t distance_to(const rrbtree_iterator& other) const
     {
-        return other.i_ > i_
-            ?   static_cast<std::ptrdiff_t>(other.i_ - i_)
-            : - static_cast<std::ptrdiff_t>(i_ - other.i_);
+        return other.i_ > i_ ? static_cast<std::ptrdiff_t>(other.i_ - i_)
+                             : -static_cast<std::ptrdiff_t>(i_ - other.i_);
     }
 
     const T& dereference() const
diff --git a/src/immer/detail/rbts/visitor.hpp b/src/immer/detail/rbts/visitor.hpp
index eaccfcdca4..38cd030c4a 100644
--- a/src/immer/detail/rbts/visitor.hpp
+++ b/src/immer/detail/rbts/visitor.hpp
@@ -21,31 +21,31 @@ template <typename Deriv>
 struct visitor_base
 {
     template <typename... Args>
-    static decltype(auto) visit_node(Args&& ...args)
+    static decltype(auto) visit_node(Args&&... args)
     {
         IMMER_UNREACHABLE;
     }
 
     template <typename... Args>
-    static decltype(auto) visit_relaxed(Args&& ...args)
+    static decltype(auto) visit_relaxed(Args&&... args)
     {
         return Deriv::visit_inner(std::forward<Args>(args)...);
     }
 
     template <typename... Args>
-    static decltype(auto) visit_regular(Args&& ...args)
+    static decltype(auto) visit_regular(Args&&... args)
     {
         return Deriv::visit_inner(std::forward<Args>(args)...);
     }
 
     template <typename... Args>
-    static decltype(auto) visit_inner(Args&& ...args)
+    static decltype(auto) visit_inner(Args&&... args)
     {
         return Deriv::visit_node(std::forward<Args>(args)...);
     }
 
     template <typename... Args>
-    static decltype(auto) visit_leaf(Args&& ...args)
+    static decltype(auto) visit_leaf(Args&&... args)
     {
         return Deriv::visit_node(std::forward<Args>(args)...);
     }
diff --git a/src/immer/detail/ref_count_base.hpp b/src/immer/detail/ref_count_base.hpp
index a20428b13f..28f2e46a99 100644
--- a/src/immer/detail/ref_count_base.hpp
+++ b/src/immer/detail/ref_count_base.hpp
@@ -16,7 +16,7 @@ namespace detail {
 template <typename Deriv>
 struct ref_count_base
 {
-    mutable std::atomic<int> ref_count { 0 };
+    mutable std::atomic<int> ref_count{0};
 
     friend void intrusive_ptr_add_ref(const Deriv* x)
     {
diff --git a/src/immer/detail/type_traits.hpp b/src/immer/detail/type_traits.hpp
index 7820e75d7a..091fe04dc2 100644
--- a/src/immer/detail/type_traits.hpp
+++ b/src/immer/detail/type_traits.hpp
@@ -18,67 +18,85 @@ namespace immer {
 namespace detail {
 
 template <typename... Ts>
-struct make_void { using type = void; };
+struct make_void
+{
+    using type = void;
+};
 
 template <typename... Ts>
 using void_t = typename make_void<Ts...>::type;
 
 template <typename T, typename = void>
-struct is_dereferenceable : std::false_type {};
+struct is_dereferenceable : std::false_type
+{};
 
 template <typename T>
-struct is_dereferenceable<T, void_t<decltype(*(std::declval<T&>()))>> :
-    std::true_type {};
+struct is_dereferenceable<T, void_t<decltype(*(std::declval<T&>()))>>
+    : std::true_type
+{};
 
 template <typename T>
 constexpr bool is_dereferenceable_v = is_dereferenceable<T>::value;
 
-template<typename T, typename U=T, typename = void>
-struct is_equality_comparable : std::false_type {};
+template <typename T, typename U = T, typename = void>
+struct is_equality_comparable : std::false_type
+{};
 
-template<typename T, typename U>
-struct is_equality_comparable
-<T, U, std::enable_if_t
- <std::is_same
-  <bool, decltype(std::declval<T&>() == std::declval<U&>())>::value>> :
-    std::true_type {};
+template <typename T, typename U>
+struct is_equality_comparable<
+    T,
+    U,
+    std::enable_if_t<std::is_same<bool,
+                                  decltype(std::declval<T&>() ==
+                                           std::declval<U&>())>::value>>
+    : std::true_type
+{};
 
-template<typename T, typename U=T>
+template <typename T, typename U = T>
 constexpr bool is_equality_comparable_v = is_equality_comparable<T, U>::value;
 
-template<typename T, typename U=T, typename = void>
-struct is_inequality_comparable : std::false_type {};
+template <typename T, typename U = T, typename = void>
+struct is_inequality_comparable : std::false_type
+{};
 
-template<typename T, typename U>
-struct is_inequality_comparable
-<T, U, std::enable_if_t
- <std::is_same
-  <bool, decltype(std::declval<T&>() != std::declval<U&>())>::value>> :
-    std::true_type {};
+template <typename T, typename U>
+struct is_inequality_comparable<
+    T,
+    U,
+    std::enable_if_t<std::is_same<bool,
+                                  decltype(std::declval<T&>() !=
+                                           std::declval<U&>())>::value>>
+    : std::true_type
+{};
 
-template<typename T, typename U=T>
+template <typename T, typename U = T>
 constexpr bool is_inequality_comparable_v =
-  is_inequality_comparable<T, U>::value;
+    is_inequality_comparable<T, U>::value;
 
 template <typename T, typename = void>
-struct is_preincrementable : std::false_type {};
+struct is_preincrementable : std::false_type
+{};
 
 template <typename T>
-struct is_preincrementable
-<T, std::enable_if_t
- <std::is_same<T&, decltype(++(std::declval<T&>()))>::value>> :
-    std::true_type {};
+struct is_preincrementable<
+    T,
+    std::enable_if_t<std::is_same<T&, decltype(++(std::declval<T&>()))>::value>>
+    : std::true_type
+{};
 
 template <typename T>
 constexpr bool is_preincrementable_v = is_preincrementable<T>::value;
 
-template <typename T, typename U=T, typename = void>
-struct is_subtractable : std::false_type {};
+template <typename T, typename U = T, typename = void>
+struct is_subtractable : std::false_type
+{};
 
 template <typename T, typename U>
-struct is_subtractable
-<T, U, void_t<decltype(std::declval<T&>() - std::declval<U&>())>> :
-    std::true_type {};
+struct is_subtractable<
+    T,
+    U,
+    void_t<decltype(std::declval<T&>() - std::declval<U&>())>> : std::true_type
+{};
 
 template <typename T, typename U = T>
 constexpr bool is_subtractable_v = is_subtractable<T, U>::value;
@@ -88,104 +106,100 @@ namespace swappable {
 using std::swap;
 
 template <typename T, typename U, typename = void>
-struct with : std::false_type {};
+struct with : std::false_type
+{};
 
 // Does not account for non-referenceable types
 template <typename T, typename U>
-struct with
-<T, U, void_t<decltype(swap(std::declval<T&>(), std::declval<U&>())),
-              decltype(swap(std::declval<U&>(), std::declval<T&>()))>> :
-    std::true_type {};
+struct with<T,
+            U,
+            void_t<decltype(swap(std::declval<T&>(), std::declval<U&>())),
+                   decltype(swap(std::declval<U&>(), std::declval<T&>()))>>
+    : std::true_type
+{};
 
-}
+} // namespace swappable
 
-template<typename T, typename U>
+template <typename T, typename U>
 using is_swappable_with = swappable::with<T, U>;
 
-template<typename T>
+template <typename T>
 using is_swappable = is_swappable_with<T, T>;
 
 template <typename T>
 constexpr bool is_swappable_v = is_swappable_with<T&, T&>::value;
 
 template <typename T, typename = void>
-struct is_iterator : std::false_type {};
+struct is_iterator : std::false_type
+{};
 
 // See http://en.cppreference.com/w/cpp/concept/Iterator
 template <typename T>
-struct is_iterator
-<T, void_t
- <std::enable_if_t
-  <is_preincrementable_v<T>
-   && is_dereferenceable_v<T>
-   // accounts for non-referenceable types
-   && std::is_copy_constructible<T>::value
-   && std::is_copy_assignable<T>::value
-   && std::is_destructible<T>::value
-   && is_swappable_v<T>>,
-  typename std::iterator_traits<T>::value_type,
-  typename std::iterator_traits<T>::difference_type,
-  typename std::iterator_traits<T>::reference,
-  typename std::iterator_traits<T>::pointer,
-  typename std::iterator_traits<T>::iterator_category>> :
-    std::true_type {};
-
-template<typename T>
+struct is_iterator<
+    T,
+    void_t<
+        std::enable_if_t<is_preincrementable_v<T> &&
+                         is_dereferenceable_v<T>
+                         // accounts for non-referenceable types
+                         && std::is_copy_constructible<T>::value &&
+                         std::is_copy_assignable<T>::value &&
+                         std::is_destructible<T>::value && is_swappable_v<T>>,
+        typename std::iterator_traits<T>::value_type,
+        typename std::iterator_traits<T>::difference_type,
+        typename std::iterator_traits<T>::reference,
+        typename std::iterator_traits<T>::pointer,
+        typename std::iterator_traits<T>::iterator_category>> : std::true_type
+{};
+
+template <typename T>
 constexpr bool is_iterator_v = is_iterator<T>::value;
 
-template<typename T, typename U, typename = void>
-struct compatible_sentinel : std::false_type {};
-
-template<typename T, typename U>
-struct compatible_sentinel
-<T, U, std::enable_if_t
- <is_iterator_v<T>
-  && is_equality_comparable_v<T, U>
-  && is_inequality_comparable_v<T, U>>> :
-    std::true_type {};
-
-template<typename T, typename U>
-constexpr bool compatible_sentinel_v = compatible_sentinel<T,U>::value;
-
-template<typename T, typename = void>
-struct is_forward_iterator : std::false_type {};
-
-template<typename T>
-struct is_forward_iterator
-<T, std::enable_if_t
- <is_iterator_v<T> &&
-  std::is_base_of
-  <std::forward_iterator_tag,
-   typename std::iterator_traits<T>::iterator_category>::value>> :
-    std::true_type {};
-
-template<typename T>
-constexpr bool is_forward_iterator_v = is_forward_iterator<T>::value;
+template <typename T, typename U, typename = void>
+struct compatible_sentinel : std::false_type
+{};
 
-template<typename T, typename U, typename = void>
-struct std_distance_supports : std::false_type {};
+template <typename T, typename U>
+struct compatible_sentinel<
+    T,
+    U,
+    std::enable_if_t<is_iterator_v<T> && is_equality_comparable_v<T, U> &&
+                     is_inequality_comparable_v<T, U>>> : std::true_type
+{};
 
-template<typename T, typename U>
-struct std_distance_supports
-<T, U, void_t<decltype(std::distance(std::declval<T>(), std::declval<U>()))>> : 
-  std::true_type {};
+template <typename T, typename U>
+constexpr bool compatible_sentinel_v = compatible_sentinel<T, U>::value;
 
-template<typename T, typename U>
-constexpr bool std_distance_supports_v = std_distance_supports<T, U>::value;
+template <typename T, typename = void>
+struct is_forward_iterator : std::false_type
+{};
 
-template<typename T, typename U, typename V, typename = void>
-struct std_uninitialized_copy_supports : std::false_type {};
+template <typename T>
+struct is_forward_iterator<
+    T,
+    std::enable_if_t<is_iterator_v<T> &&
+                     std::is_base_of<std::forward_iterator_tag,
+                                     typename std::iterator_traits<
+                                         T>::iterator_category>::value>>
+    : std::true_type
+{};
 
-template<typename T, typename U, typename V>
-struct std_uninitialized_copy_supports
-<T, U, V, void_t<decltype(std::uninitialized_copy(std::declval<T>(),
-						  std::declval<U>(),
-						  std::declval<V>()))>> : 
-  std::true_type {};
+template <typename T>
+constexpr bool is_forward_iterator_v = is_forward_iterator<T>::value;
 
-template<typename T, typename U, typename V>
-constexpr bool std_uninitialized_copy_supports_v = 
-  std_uninitialized_copy_supports<T, U, V>::value;
+template <typename T, typename U, typename = void>
+struct std_distance_supports : std::false_type
+{};
+
+template <typename T, typename U>
+struct std_distance_supports<
+    T,
+    U,
+    void_t<decltype(std::distance(std::declval<T>(), std::declval<U>()))>>
+    : std::true_type
+{};
+
+template <typename T, typename U>
+constexpr bool std_distance_supports_v = std_distance_supports<T, U>::value;
 
-}
-}
+} // namespace detail
+} // namespace immer
diff --git a/src/immer/detail/util.hpp b/src/immer/detail/util.hpp
index 854a263d9e..d6ae246b43 100644
--- a/src/immer/detail/util.hpp
+++ b/src/immer/detail/util.hpp
@@ -11,9 +11,9 @@
 #include <immer/config.hpp>
 
 #include <cstddef>
+#include <memory>
 #include <new>
 #include <type_traits>
-#include <memory>
 
 #include <immer/detail/type_traits.hpp>
 
@@ -24,62 +24,171 @@
 namespace immer {
 namespace detail {
 
+template <typename T>
+const T* as_const(T* x)
+{
+    return x;
+}
+
+template <typename T>
+const T& as_const(T& x)
+{
+    return x;
+}
+
 template <typename T>
 using aligned_storage_for =
     typename std::aligned_storage<sizeof(T), alignof(T)>::type;
 
 template <typename T>
-T& auto_const_cast(const T& x) { return const_cast<T&>(x); }
+T& auto_const_cast(const T& x)
+{
+    return const_cast<T&>(x);
+}
 template <typename T>
-T&& auto_const_cast(const T&& x) { return const_cast<T&&>(std::move(x)); }
+T&& auto_const_cast(const T&& x)
+{
+    return const_cast<T&&>(std::move(x));
+}
 
-template <typename Iter1, typename Iter2>
-auto uninitialized_move(Iter1 in1, Iter1 in2, Iter2 out)
+template <class T>
+inline auto destroy_at(T* p) noexcept
+    -> std::enable_if_t<std::is_trivially_destructible<T>::value>
 {
-    return std::uninitialized_copy(std::make_move_iterator(in1),
-                                   std::make_move_iterator(in2),
-                                   out);
+    p->~T();
 }
 
 template <class T>
-void destroy(T* first, T* last)
+inline auto destroy_at(T* p) noexcept
+    -> std::enable_if_t<!std::is_trivially_destructible<T>::value>
+{
+    p->~T();
+}
+
+template <typename Iter1>
+constexpr bool can_trivially_detroy = std::is_trivially_destructible<
+    typename std::iterator_traits<Iter1>::value_type>::value;
+
+template <class Iter>
+auto destroy(Iter, Iter last) noexcept
+    -> std::enable_if_t<can_trivially_detroy<Iter>, Iter>
+{
+    return last;
+}
+template <class Iter>
+auto destroy(Iter first, Iter last) noexcept
+    -> std::enable_if_t<!can_trivially_detroy<Iter>, Iter>
 {
     for (; first != last; ++first)
-        first->~T();
+        detail::destroy_at(std::addressof(*first));
+    return first;
+}
+
+template <class Iter, class Size>
+auto destroy_n(Iter first, Size n) noexcept
+    -> std::enable_if_t<can_trivially_detroy<Iter>, Iter>
+{
+    return first + n;
+}
+template <class Iter, class Size>
+auto destroy_n(Iter first, Size n) noexcept
+    -> std::enable_if_t<!can_trivially_detroy<Iter>, Iter>
+{
+    for (; n > 0; (void) ++first, --n)
+        detail::destroy_at(std::addressof(*first));
+    return first;
+}
+
+template <typename Iter1, typename Iter2>
+constexpr bool can_trivially_copy =
+    std::is_same<typename std::iterator_traits<Iter1>::value_type,
+                 typename std::iterator_traits<Iter2>::value_type>::value&&
+        std::is_trivially_copyable<
+            typename std::iterator_traits<Iter1>::value_type>::value;
+
+template <typename Iter1, typename Iter2>
+auto uninitialized_move(Iter1 first, Iter1 last, Iter2 out) noexcept
+    -> std::enable_if_t<can_trivially_copy<Iter1, Iter2>, Iter2>
+{
+    return std::copy(first, last, out);
+}
+template <typename Iter1, typename Iter2>
+auto uninitialized_move(Iter1 first, Iter1 last, Iter2 out)
+    -> std::enable_if_t<!can_trivially_copy<Iter1, Iter2>, Iter2>
+
+{
+    using value_t = typename std::iterator_traits<Iter2>::value_type;
+    auto current  = out;
+    IMMER_TRY {
+        for (; first != last; ++first, (void) ++current) {
+            ::new (const_cast<void*>(static_cast<const volatile void*>(
+                std::addressof(*current)))) value_t(std::move(*first));
+        }
+        return current;
+    }
+    IMMER_CATCH (...) {
+        detail::destroy(out, current);
+        IMMER_RETHROW;
+    }
 }
 
-template <class T, class Size>
-void destroy_n(T* p, Size n)
+template <typename SourceIter, typename Sent, typename SinkIter>
+auto uninitialized_copy(SourceIter first, Sent last, SinkIter out) noexcept
+    -> std::enable_if_t<can_trivially_copy<SourceIter, SinkIter>, SinkIter>
+{
+    return std::copy(first, last, out);
+}
+template <typename SourceIter, typename Sent, typename SinkIter>
+auto uninitialized_copy(SourceIter first, Sent last, SinkIter out)
+    -> std::enable_if_t<!can_trivially_copy<SourceIter, SinkIter>, SinkIter>
 {
-    auto e = p + n;
-    for (; p != e; ++p)
-        p->~T();
+    using value_t = typename std::iterator_traits<SinkIter>::value_type;
+    auto current  = out;
+    IMMER_TRY {
+        for (; first != last; ++first, (void) ++current) {
+            ::new (const_cast<void*>(static_cast<const volatile void*>(
+                std::addressof(*current)))) value_t(*first);
+        }
+        return current;
+    }
+    IMMER_CATCH (...) {
+        detail::destroy(out, current);
+        IMMER_RETHROW;
+    }
 }
 
 template <typename Heap, typename T, typename... Args>
-T* make(Args&& ...args)
+T* make(Args&&... args)
 {
     auto ptr = Heap::allocate(sizeof(T));
-    try {
+    IMMER_TRY {
         return new (ptr) T{std::forward<Args>(args)...};
-    } catch (...) {
+    }
+    IMMER_CATCH (...) {
         Heap::deallocate(sizeof(T), ptr);
-        throw;
+        IMMER_RETHROW;
     }
 }
 
-struct not_supported_t {};
-struct empty_t {};
+struct not_supported_t
+{};
+struct empty_t
+{};
 
 template <typename T>
 struct exact_t
 {
     T value;
-    exact_t(T v) : value{v} {};
+    exact_t(T v)
+        : value{v} {};
 };
 
 template <typename T>
-inline constexpr auto clz_(T) -> not_supported_t { IMMER_UNREACHABLE; return {}; }
+inline constexpr auto clz_(T) -> not_supported_t
+{
+    IMMER_UNREACHABLE;
+    return {};
+}
 #if defined(_MSC_VER)
 // inline auto clz_(unsigned short x) { return __lzcnt16(x); }
 // inline auto clz_(unsigned int x) { return __lzcnt(x); }
@@ -97,46 +206,63 @@ inline constexpr T log2_aux(T x, T r = 0)
 }
 
 template <typename T>
-inline constexpr auto log2(T x)
-    -> std::enable_if_t<!std::is_same<decltype(clz_(x)), not_supported_t>::value, T>
+inline constexpr auto log2(T x) -> std::
+    enable_if_t<!std::is_same<decltype(clz_(x)), not_supported_t>::value, T>
 {
     return x == 0 ? 0 : sizeof(std::size_t) * 8 - 1 - clz_(x);
 }
 
 template <typename T>
 inline constexpr auto log2(T x)
-    -> std::enable_if_t<std::is_same<decltype(clz_(x)), not_supported_t>::value, T>
+    -> std::enable_if_t<std::is_same<decltype(clz_(x)), not_supported_t>::value,
+                        T>
 {
     return log2_aux(x);
 }
 
+template <typename T>
+constexpr T ipow(T num, unsigned int pow)
+{
+    return pow == 0 ? 1 : num * ipow(num, pow - 1);
+}
+
 template <bool b, typename F>
 auto static_if(F&& f) -> std::enable_if_t<b>
-{ std::forward<F>(f)(empty_t{}); }
+{
+    std::forward<F>(f)(empty_t{});
+}
 template <bool b, typename F>
 auto static_if(F&& f) -> std::enable_if_t<!b>
 {}
 
-template <bool b, typename R=void, typename F1, typename F2>
+template <bool b, typename R = void, typename F1, typename F2>
 auto static_if(F1&& f1, F2&& f2) -> std::enable_if_t<b, R>
-{ return std::forward<F1>(f1)(empty_t{}); }
-template <bool b, typename R=void, typename F1, typename F2>
+{
+    return std::forward<F1>(f1)(empty_t{});
+}
+template <bool b, typename R = void, typename F1, typename F2>
 auto static_if(F1&& f1, F2&& f2) -> std::enable_if_t<!b, R>
-{ return std::forward<F2>(f2)(empty_t{}); }
+{
+    return std::forward<F2>(f2)(empty_t{});
+}
 
 template <typename T, T value>
 struct constantly
 {
     template <typename... Args>
-    T operator() (Args&&...) const { return value; }
+    T operator()(Args&&...) const
+    {
+        return value;
+    }
 };
 
 /*!
  * An alias to `std::distance`
  */
-template <typename Iterator, typename Sentinel,
-          std::enable_if_t
-          <detail::std_distance_supports_v<Iterator,Sentinel>, bool> = true>
+template <typename Iterator,
+          typename Sentinel,
+          std::enable_if_t<detail::std_distance_supports_v<Iterator, Sentinel>,
+                           bool> = true>
 typename std::iterator_traits<Iterator>::difference_type
 distance(Iterator first, Sentinel last)
 {
@@ -147,12 +273,14 @@ distance(Iterator first, Sentinel last)
  * Equivalent of the `std::distance` applied to the sentinel-delimited
  * forward range @f$ [first, last) @f$
  */
-template <typename Iterator, typename Sentinel,
-          std::enable_if_t
-          <(!detail::std_distance_supports_v<Iterator,Sentinel>)
-           && detail::is_forward_iterator_v<Iterator>
-           && detail::compatible_sentinel_v<Iterator,Sentinel>
-           && (!detail::is_subtractable_v<Sentinel, Iterator>), bool> = true>
+template <typename Iterator,
+          typename Sentinel,
+          std::enable_if_t<
+              (!detail::std_distance_supports_v<Iterator, Sentinel>) &&detail::
+                      is_forward_iterator_v<Iterator> &&
+                  detail::compatible_sentinel_v<Iterator, Sentinel> &&
+                  (!detail::is_subtractable_v<Sentinel, Iterator>),
+              bool> = true>
 typename std::iterator_traits<Iterator>::difference_type
 distance(Iterator first, Sentinel last)
 {
@@ -168,58 +296,19 @@ distance(Iterator first, Sentinel last)
  * Equivalent of the `std::distance` applied to the sentinel-delimited
  * random access range @f$ [first, last) @f$
  */
-template <typename Iterator, typename Sentinel,
-          std::enable_if_t
-          <(!detail::std_distance_supports_v<Iterator,Sentinel>)
-           && detail::is_forward_iterator_v<Iterator>
-           && detail::compatible_sentinel_v<Iterator,Sentinel>
-           && detail::is_subtractable_v<Sentinel, Iterator>, bool> = true>
+template <typename Iterator,
+          typename Sentinel,
+          std::enable_if_t<
+              (!detail::std_distance_supports_v<Iterator, Sentinel>) &&detail::
+                      is_forward_iterator_v<Iterator> &&
+                  detail::compatible_sentinel_v<Iterator, Sentinel> &&
+                  detail::is_subtractable_v<Sentinel, Iterator>,
+              bool> = true>
 typename std::iterator_traits<Iterator>::difference_type
 distance(Iterator first, Sentinel last)
 {
     return last - first;
 }
 
-
-
-/*!
- * An alias to `std::uninitialized_copy`
- */
-template <typename Iterator, typename Sentinel, typename SinkIter,
-          std::enable_if_t
-          <detail::std_uninitialized_copy_supports_v
-           <Iterator,Sentinel,SinkIter>, bool> = true>
-SinkIter uninitialized_copy(Iterator first, Sentinel last, SinkIter d_first)
-{
-    return std::uninitialized_copy(first, last, d_first);
-}
-
-/*!
- * Equivalent of the `std::uninitialized_copy` applied to the
- * sentinel-delimited forward range @f$ [first, last) @f$
- */
-template <typename SourceIter, typename Sent, typename SinkIter,
-          std::enable_if_t
-          <(!detail::std_uninitialized_copy_supports_v<SourceIter, Sent, SinkIter>)
-           && detail::compatible_sentinel_v<SourceIter,Sent>
-           && detail::is_forward_iterator_v<SinkIter>, bool> = true>
-SinkIter uninitialized_copy(SourceIter first, Sent last, SinkIter d_first)
-{
-    auto current = d_first;
-    try {
-        while (first != last) {
-            *current++ = *first;
-            ++first;
-        }
-    } catch (...) {
-        using Value = typename std::iterator_traits<SinkIter>::value_type;
-        for (;d_first != current; ++d_first){
-            d_first->~Value();
-        }
-        throw;
-    }
-    return current;
-}
-
 } // namespace detail
 } // namespace immer
diff --git a/src/immer/experimental/detail/dvektor_impl.hpp b/src/immer/experimental/detail/dvektor_impl.hpp
index df273aab71..4f697337a7 100644
--- a/src/immer/experimental/detail/dvektor_impl.hpp
+++ b/src/immer/experimental/detail/dvektor_impl.hpp
@@ -17,6 +17,7 @@
 #include <boost/smart_ptr/intrusive_ref_counter.hpp>
 
 #include <cassert>
+#include <cstddef>
 #include <limits>
 
 namespace immer {
@@ -28,31 +29,32 @@ constexpr auto fast_log2(std::size_t x)
     return x == 0 ? 0 : sizeof(std::size_t) * 8 - 1 - __builtin_clzl(x);
 }
 
-template <int B, typename T=std::size_t>
+template <int B, typename T = std::size_t>
 constexpr T branches = T{1} << B;
 
-template <int B, typename T=std::size_t>
+template <int B, typename T = std::size_t>
 constexpr T mask = branches<B, T> - 1;
 
-template <int B, typename T=std::size_t>
-constexpr auto max_depth =
-    fast_log2(std::numeric_limits<std::size_t>::max()) / B;
+template <int B, typename T = std::size_t>
+constexpr auto
+    max_depth = fast_log2(std::numeric_limits<std::size_t>::max()) / B;
 
 template <typename T, int B, typename MP>
 struct node;
 
 template <typename T, int B, typename MP>
-using node_ptr = boost::intrusive_ptr<node<T, B, MP> >;
+using node_ptr = boost::intrusive_ptr<node<T, B, MP>>;
 
 template <typename T, int B>
-using leaf_node  = std::array<T, 1 << B>;
+using leaf_node = std::array<T, 1 << B>;
 
 template <typename T, int B, typename MP>
 using inner_node = std::array<node_ptr<T, B, MP>, 1 << B>;
 
 template <typename T, int B, typename MP>
-struct node : enable_intrusive_ptr<node<T, B, MP>, typename MP::refcount>
-            , enable_optimized_heap_policy<node<T, B, MP>, typename MP::heap>
+struct node
+    : enable_intrusive_ptr<node<T, B, MP>, typename MP::refcount>
+    , enable_optimized_heap_policy<node<T, B, MP>, typename MP::heap>
 {
     using leaf_node_t  = leaf_node<T, B>;
     using inner_node_t = inner_node<T, B, MP>;
@@ -65,10 +67,14 @@ struct node : enable_intrusive_ptr<node<T, B, MP>, typename MP::refcount>
 
     union data_t
     {
-        leaf_node_t  leaf;
+        leaf_node_t leaf;
         inner_node_t inner;
-        data_t(leaf_node_t n)  : leaf(std::move(n)) {}
-        data_t(inner_node_t n) : inner(std::move(n)) {}
+        data_t(leaf_node_t n)
+            : leaf(std::move(n))
+        {}
+        data_t(inner_node_t n)
+            : inner(std::move(n))
+        {}
         ~data_t() {}
     } data;
 
@@ -94,37 +100,41 @@ struct node : enable_intrusive_ptr<node<T, B, MP>, typename MP::refcount>
         , data{std::move(n)}
     {}
 
-    inner_node_t& inner() & {
+    inner_node_t& inner() &
+    {
         assert(kind == inner_kind);
         return data.inner;
     }
-    const inner_node_t& inner() const& {
+    const inner_node_t& inner() const&
+    {
         assert(kind == inner_kind);
         return data.inner;
     }
-    inner_node_t&& inner() && {
+    inner_node_t&& inner() &&
+    {
         assert(kind == inner_kind);
         return std::move(data.inner);
     }
 
-    leaf_node_t& leaf() & {
+    leaf_node_t& leaf() &
+    {
         assert(kind == leaf_kind);
         return data.leaf;
     }
-    const leaf_node_t& leaf() const& {
+    const leaf_node_t& leaf() const&
+    {
         assert(kind == leaf_kind);
         return data.leaf;
     }
-    leaf_node_t&& leaf() && {
+    leaf_node_t&& leaf() &&
+    {
         assert(kind == leaf_kind);
         return std::move(data.leaf);
     }
 };
 
-template <typename T, int B, typename MP,
-          typename ...Ts>
-auto make_node(Ts&& ...xs)
-    -> boost::intrusive_ptr<node<T, B, MP>>
+template <typename T, int B, typename MP, typename... Ts>
+auto make_node(Ts&&... xs) -> boost::intrusive_ptr<node<T, B, MP>>
 {
     return new node<T, B, MP>(std::forward<Ts>(xs)...);
 }
@@ -140,8 +150,8 @@ struct ref
     unsigned depth;
     std::array<node_ptr_t, max_depth<B>> display;
 
-    template <typename ...Ts>
-    static auto make_node(Ts&& ...xs)
+    template <typename... Ts>
+    static auto make_node(Ts&&... xs)
     {
         return dvektor::make_node<T, B, MP>(std::forward<Ts>(xs)...);
     }
@@ -152,16 +162,16 @@ struct ref
         auto node        = display[display_idx].get();
         auto shift       = display_idx * B;
         while (display_idx--) {
-            node = node->inner() [(index >> shift) & mask<B>].get();
+            node = node->inner()[(index >> shift) & mask<B>].get();
             shift -= B;
         }
-        return node->leaf() [index & mask<B>];
+        return node->leaf()[index & mask<B>];
     }
 
     node_ptr_t null_slot_and_copy_inner(node_ptr_t& node, std::size_t idx)
     {
         auto& n = node->inner();
-        auto x = node_ptr_t{};
+        auto x  = node_ptr_t{};
         x.swap(n[idx]);
         return copy_of_inner(x);
     }
@@ -169,7 +179,7 @@ struct ref
     node_ptr_t null_slot_and_copy_leaf(node_ptr_t& node, std::size_t idx)
     {
         auto& n = node->inner();
-        auto x = node_ptr_t{};
+        auto x  = node_ptr_t{};
         x.swap(n[idx]);
         return copy_of_leaf(x);
     }
@@ -187,11 +197,9 @@ struct ref
     void stabilize(std::size_t index)
     {
         auto shift = B;
-        for (auto i = 1u; i < depth; ++i)
-        {
+        for (auto i = 1u; i < depth; ++i) {
             display[i] = copy_of_inner(display[i]);
-            display[i]->inner() [(index >> shift) & mask<B>]
-                = display[i - 1];
+            display[i]->inner()[(index >> shift) & mask<B>] = display[i - 1];
             shift += B;
         }
     }
@@ -210,13 +218,11 @@ struct ref
             auto shift = B * d;
             while (--d) {
                 display[d] = null_slot_and_copy_inner(
-                    display[d + 1],
-                    (index >> shift) & mask<B>);
+                    display[d + 1], (index >> shift) & mask<B>);
                 shift -= B;
             }
-            display[0] = null_slot_and_copy_leaf(
-                display[1],
-                (index >> B) & mask<B>);
+            display[0] =
+                null_slot_and_copy_leaf(display[1], (index >> B) & mask<B>);
         }
     }
 
@@ -232,19 +238,17 @@ struct ref
             auto shift       = B;
             for (auto i = 1u; i <= display_idx; ++i) {
                 display[i] = copy_of_inner(display[i]);
-                display[i]->inner() [(old_index >> shift) & mask<B>]
-                    = display[i - 1];
+                display[i]->inner()[(old_index >> shift) & mask<B>] =
+                    display[i - 1];
                 shift += B;
             }
             for (auto i = display_idx - 1; i > 0; --i) {
                 shift -= B;
                 display[i] = null_slot_and_copy_inner(
-                    display[i + 1],
-                    (new_index >> shift) & mask<B>);
+                    display[i + 1], (new_index >> shift) & mask<B>);
             }
-            display[0] = null_slot_and_copy_leaf(
-                display[1],
-                (new_index >> B) & mask<B>);
+            display[0] =
+                null_slot_and_copy_leaf(display[1], (new_index >> B) & mask<B>);
         }
     }
 
@@ -254,21 +258,18 @@ struct ref
     {
         auto display_idx = fast_log2(xr) / B;
         if (display_idx > 0) {
-            auto shift       = display_idx * B;
+            auto shift = display_idx * B;
             if (display_idx == depth) {
                 display[display_idx] = make_node(inner_t{});
-                display[display_idx]->inner()
-                    [(old_index >> shift) & mask<B>] =
+                display[display_idx]->inner()[(old_index >> shift) & mask<B>] =
                     display[display_idx - 1];
                 ++depth;
             }
             while (--display_idx) {
-                auto node = display[display_idx + 1]->inner()
-                    [(new_index >> shift) & mask<B>];
-                display[display_idx] = node
-                    ? std::move(node)
-                    : make_node(inner_t{});
-
+                auto node = display[display_idx + 1]
+                                ->inner()[(new_index >> shift) & mask<B>];
+                display[display_idx] =
+                    node ? std::move(node) : make_node(inner_t{});
             }
             display[0] = make_node(leaf_t{});
         }
@@ -285,10 +286,10 @@ struct ref
     void goto_next_block_start(std::size_t index, std::size_t xr)
     {
         auto display_idx = fast_log2(xr) / B;
-        auto shift = display_idx * B;
+        auto shift       = display_idx * B;
         if (display_idx > 0) {
-            display[display_idx - 1] = display[display_idx]->inner()
-                [(index >> shift) & mask<B>];
+            display[display_idx - 1] =
+                display[display_idx]->inner()[(index >> shift) & mask<B>];
             while (--display_idx)
                 display[display_idx - 1] = display[display_idx]->inner()[0];
         }
@@ -297,11 +298,11 @@ struct ref
     void goto_pos(std::size_t index, std::size_t xr)
     {
         auto display_idx = fast_log2(xr) / B;
-        auto shift = display_idx * B;
+        auto shift       = display_idx * B;
         if (display_idx) {
             do {
-                display[display_idx - 1] = display[display_idx]->inner()
-                    [(index >> shift) & mask<B>];
+                display[display_idx - 1] =
+                    display[display_idx]->inner()[(index >> shift) & mask<B>];
                 shift -= B;
             } while (--display_idx);
         }
@@ -319,11 +320,11 @@ struct impl
 
     std::size_t size;
     std::size_t focus;
-    bool        dirty;
-    ref_t       p;
+    bool dirty;
+    ref_t p;
 
-    template <typename ...Ts>
-    static auto make_node(Ts&& ...xs)
+    template <typename... Ts>
+    static auto make_node(Ts&&... xs)
     {
         return dvektor::make_node<T, B, MP>(std::forward<Ts>(xs)...);
     }
@@ -356,23 +357,22 @@ struct impl
     {
         if (size) {
             auto block_index = size & ~mask<B>;
-            auto lo = size & mask<B>;
+            auto lo          = size & mask<B>;
             if (size != block_index) {
-                auto s = impl{ size + 1, block_index, dirty, p };
+                auto s = impl{size + 1, block_index, dirty, p};
                 s.goto_pos_writable(focus, block_index, focus ^ block_index);
-                s.p.display[0]->leaf() [lo] = std::move(value);
+                s.p.display[0]->leaf()[lo] = std::move(value);
                 return s;
             } else {
-                auto s = impl{ size + 1, block_index, dirty, p };
-                s.goto_fresh_pos_writable(focus, block_index, focus ^ block_index);
-                s.p.display[0]->leaf() [lo] = std::move(value);
+                auto s = impl{size + 1, block_index, dirty, p};
+                s.goto_fresh_pos_writable(
+                    focus, block_index, focus ^ block_index);
+                s.p.display[0]->leaf()[lo] = std::move(value);
                 return s;
             }
         } else {
             return impl{
-                1, 0, false,
-                { 1, {{ make_node(leaf_t{{std::move(value)}}) }} }
-            };
+                1, 0, false, {1, {{make_node(leaf_t{{std::move(value)}})}}}};
         }
     }
 
@@ -384,44 +384,38 @@ struct impl
     template <typename FnT>
     impl update(std::size_t idx, FnT&& fn) const
     {
-        auto s = impl{ size, idx, dirty, p };
+        auto s = impl{size, idx, dirty, p};
         s.goto_pos_writable(focus, idx, focus ^ idx);
-        auto& v = s.p.display[0]->leaf() [idx & mask<B>];
-        v = fn(std::move(v));
+        auto& v = s.p.display[0]->leaf()[idx & mask<B>];
+        v       = fn(std::move(v));
         return s;
     }
 
     impl assoc(std::size_t idx, T value) const
     {
-        return update(idx, [&] (auto&&) {
-            return std::move(value);
-        });
+        return update(idx, [&](auto&&) { return std::move(value); });
     }
 };
 
 template <typename T, int B, typename MP>
-const impl<T, B, MP> empty = {
-    0,
-    0,
-    false,
-    ref<T, B, MP> {1, {}}
-};
+const impl<T, B, MP> empty = {0, 0, false, ref<T, B, MP>{1, {}}};
 
 template <typename T, int B, typename MP>
-struct iterator : boost::iterator_facade<
-    iterator<T, B, MP>,
-    T,
-    boost::random_access_traversal_tag,
-    const T&>
+struct iterator
+    : boost::iterator_facade<iterator<T, B, MP>,
+                             T,
+                             boost::random_access_traversal_tag,
+                             const T&>
 {
-    struct end_t {};
+    struct end_t
+    {};
 
     iterator() = default;
 
     iterator(const impl<T, B, MP>& v)
-        : p_{ v.p }
-        , i_{ 0 }
-        , base_{ 0 }
+        : p_{v.p}
+        , i_{0}
+        , base_{0}
     {
         if (v.dirty)
             p_.stabilize(v.focus);
@@ -430,9 +424,9 @@ struct iterator : boost::iterator_facade<
     }
 
     iterator(const impl<T, B, MP>& v, end_t)
-        : p_{ v.p }
-        , i_{ v.size }
-        , base_{ (v.size-1) & ~mask<B> }
+        : p_{v.p}
+        , i_{v.size}
+        , base_{(v.size - 1) & ~mask<B>}
     {
         if (v.dirty)
             p_.stabilize(v.focus);
@@ -445,8 +439,8 @@ struct iterator : boost::iterator_facade<
     using leaf_iterator = typename leaf_node<T, B>::const_iterator;
 
     ref<T, B, MP> p_;
-    std::size_t   i_;
-    std::size_t   base_;
+    std::size_t i_;
+    std::size_t base_;
     leaf_iterator curr_;
 
     void increment()
@@ -489,22 +483,15 @@ struct iterator : boost::iterator_facade<
         }
     }
 
-    bool equal(const iterator& other) const
-    {
-        return i_ == other.i_;
-    }
+    bool equal(const iterator& other) const { return i_ == other.i_; }
 
     std::ptrdiff_t distance_to(const iterator& other) const
     {
-        return other.i_ > i_
-            ?   static_cast<std::ptrdiff_t>(other.i_ - i_)
-            : - static_cast<std::ptrdiff_t>(i_ - other.i_);
+        return other.i_ > i_ ? static_cast<std::ptrdiff_t>(other.i_ - i_)
+                             : -static_cast<std::ptrdiff_t>(i_ - other.i_);
     }
 
-    const T& dereference() const
-    {
-        return *curr_;
-    }
+    const T& dereference() const { return *curr_; }
 };
 
 } /* namespace dvektor */
diff --git a/src/immer/experimental/dvektor.hpp b/src/immer/experimental/dvektor.hpp
index 7aa0bde7b3..a4227d6498 100644
--- a/src/immer/experimental/dvektor.hpp
+++ b/src/immer/experimental/dvektor.hpp
@@ -9,21 +9,22 @@
 #pragma once
 
 #include <immer/experimental/detail/dvektor_impl.hpp>
+
 #include <immer/memory_policy.hpp>
 
+#include <cstddef>
+
 namespace immer {
 
-template <typename T,
-          int B = 5,
-          typename MemoryPolicy = default_memory_policy>
+template <typename T, int B = 5, typename MemoryPolicy = default_memory_policy>
 class dvektor
 {
     using impl_t = detail::dvektor::impl<T, B, MemoryPolicy>;
 
 public:
-    using value_type = T;
-    using reference = const T&;
-    using size_type = std::size_t;
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = std::size_t;
     using difference_type = std::ptrdiff_t;
     using const_reference = const T&;
 
@@ -34,29 +35,36 @@ class dvektor
     dvektor() = default;
 
     iterator begin() const { return {impl_}; }
-    iterator end()   const { return {impl_, typename iterator::end_t{}}; }
+    iterator end() const { return {impl_, typename iterator::end_t{}}; }
 
     reverse_iterator rbegin() const { return reverse_iterator{end()}; }
-    reverse_iterator rend()   const { return reverse_iterator{begin()}; }
+    reverse_iterator rend() const { return reverse_iterator{begin()}; }
 
     std::size_t size() const { return impl_.size; }
     bool empty() const { return impl_.size == 0; }
 
-    reference operator[] (size_type index) const
-    { return impl_.get(index); }
+    reference operator[](size_type index) const { return impl_.get(index); }
 
     dvektor push_back(value_type value) const
-    { return { impl_.push_back(std::move(value)) }; }
+    {
+        return {impl_.push_back(std::move(value))};
+    }
 
     dvektor assoc(std::size_t idx, value_type value) const
-    { return { impl_.assoc(idx, std::move(value)) }; }
+    {
+        return {impl_.assoc(idx, std::move(value))};
+    }
 
     template <typename FnT>
     dvektor update(std::size_t idx, FnT&& fn) const
-    { return { impl_.update(idx, std::forward<FnT>(fn)) }; }
+    {
+        return {impl_.update(idx, std::forward<FnT>(fn))};
+    }
 
 private:
-    dvektor(impl_t impl) : impl_(std::move(impl)) {}
+    dvektor(impl_t impl)
+        : impl_(std::move(impl))
+    {}
     impl_t impl_ = detail::dvektor::empty<T, B, MemoryPolicy>;
 };
 
diff --git a/src/immer/flex_vector.hpp b/src/immer/flex_vector.hpp
index 7ab8fcd31b..f970db910a 100644
--- a/src/immer/flex_vector.hpp
+++ b/src/immer/flex_vector.hpp
@@ -55,9 +55,10 @@ class flex_vector_transient;
  * @endrst
  */
 template <typename T,
-          typename MemoryPolicy   = default_memory_policy,
-          detail::rbts::bits_t B  = default_bits,
-          detail::rbts::bits_t BL = detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
+          typename MemoryPolicy  = default_memory_policy,
+          detail::rbts::bits_t B = default_bits,
+          detail::rbts::bits_t BL =
+              detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
 class flex_vector
 {
     using impl_t = detail::rbts::rrbtree<T, MemoryPolicy, B, BL>;
@@ -66,21 +67,27 @@ class flex_vector
         std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>;
 
 public:
-    static constexpr auto bits = B;
+    static constexpr auto bits      = B;
     static constexpr auto bits_leaf = BL;
-    using memory_policy = MemoryPolicy;
+    using memory_policy             = MemoryPolicy;
 
-    using value_type = T;
-    using reference = const T&;
-    using size_type = detail::rbts::size_t;
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = detail::rbts::size_t;
     using difference_type = std::ptrdiff_t;
     using const_reference = const T&;
 
-    using iterator         = detail::rbts::rrbtree_iterator<T, MemoryPolicy, B, BL>;
+    using iterator = detail::rbts::rrbtree_iterator<T, MemoryPolicy, B, BL>;
     using const_iterator   = iterator;
     using reverse_iterator = std::reverse_iterator<iterator>;
 
-    using transient_type   = flex_vector_transient<T, MemoryPolicy, B, BL>;
+    using transient_type = flex_vector_transient<T, MemoryPolicy, B, BL>;
+
+    /*!
+     * Returns the maximum theoretical size supported by the internal structure
+     * given the current B, BL.
+     */
+    constexpr static size_type max_size() { return impl_t::max_size(); }
 
     /*!
      * Default constructor.  It creates a flex_vector of `size() == 0`.
@@ -99,9 +106,10 @@ class flex_vector
      * Constructs a flex_vector containing the elements in the range
      * defined by the input iterator `first` and range sentinel `last`.
      */
-    template <typename Iter, typename Sent,
-              std::enable_if_t
-              <detail::compatible_sentinel_v<Iter, Sent>, bool> = true>
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
     flex_vector(Iter first, Sent last)
         : impl_{impl_t::from_range(first, last)}
     {}
@@ -120,8 +128,10 @@ class flex_vector
      * @f$ O(1) @f$.
      */
     flex_vector(vector<T, MemoryPolicy, B, BL> v)
-        : impl_ { v.impl_.size, v.impl_.shift,
-                  v.impl_.root->inc(), v.impl_.tail->inc() }
+        : impl_{v.impl_.size,
+                v.impl_.shift,
+                v.impl_.root->inc(),
+                v.impl_.tail->inc()}
     {}
 
     /*!
@@ -129,49 +139,58 @@ class flex_vector
      * collection. It does not allocate memory and its complexity is
      * @f$ O(1) @f$.
      */
-    iterator begin() const { return {impl_}; }
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
 
     /*!
      * Returns an iterator pointing just after the last element of the
      * collection. It does not allocate and its complexity is @f$ O(1) @f$.
      */
-    iterator end()   const { return {impl_, typename iterator::end_t{}}; }
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing at the first element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rbegin() const { return reverse_iterator{end()}; }
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing after the last element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rend()   const { return reverse_iterator{begin()}; }
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
 
     /*!
      * Returns the number of elements in the container.  It does
      * not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    size_type size() const { return impl_.size; }
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
 
     /*!
      * Returns `true` if there are no elements in the container.  It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    bool empty() const { return impl_.size == 0; }
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
 
     /*!
      * Access the last element.
      */
-    const T& back() const { return impl_.back(); }
+    IMMER_NODISCARD const T& back() const { return impl_.back(); }
 
     /*!
      * Access the first element.
      */
-    const T& front() const { return impl_.front(); }
+    IMMER_NODISCARD const T& front() const { return impl_.front(); }
 
     /*!
      * Returns a `const` reference to the element at position `index`.
@@ -179,8 +198,10 @@ class flex_vector
      * allocate memory and its complexity is *effectively* @f$ O(1)
      * @f$.
      */
-    reference operator[] (size_type index) const
-    { return impl_.get(index); }
+    IMMER_NODISCARD reference operator[](size_type index) const
+    {
+        return impl_.get(index);
+    }
 
     /*!
      * Returns a `const` reference to the element at position
@@ -188,16 +209,19 @@ class flex_vector
      * index \geq size() @f$.  It does not allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
-    reference at(size_type index) const
-    { return impl_.get_check(index); }
+    reference at(size_type index) const { return impl_.get_check(index); }
 
     /*!
      * Returns whether the vectors are equal.
      */
-    bool operator==(const flex_vector& other) const
-    { return impl_.equals(other.impl_); }
-    bool operator!=(const flex_vector& other) const
-    { return !(*this == other); }
+    IMMER_NODISCARD bool operator==(const flex_vector& other) const
+    {
+        return impl_.equals(other.impl_);
+    }
+    IMMER_NODISCARD bool operator!=(const flex_vector& other) const
+    {
+        return !(*this == other);
+    }
 
     /*!
      * Returns a flex_vector with `value` inserted at the end.  It may
@@ -214,14 +238,18 @@ class flex_vector
      *
      * @endrst
      */
-    flex_vector push_back(value_type value) const&
-    { return impl_.push_back(std::move(value)); }
+    IMMER_NODISCARD flex_vector push_back(value_type value) const&
+    {
+        return impl_.push_back(std::move(value));
+    }
 
-    decltype(auto) push_back(value_type value) &&
-    { return push_back_move(move_t{}, std::move(value)); }
+    IMMER_NODISCARD decltype(auto) push_back(value_type value) &&
+    {
+        return push_back_move(move_t{}, std::move(value));
+    }
 
     /*!
-     * Returns a flex_vector with `value` inserted at the frony.  It may
+     * Returns a flex_vector with `value` inserted at the front.  It may
      * allocate memory and its complexity is @f$ O(log(size)) @f$.
      *
      * @rst
@@ -235,8 +263,10 @@ class flex_vector
      *
      * @endrst
      */
-    flex_vector push_front(value_type value) const
-    { return flex_vector{}.push_back(value) + *this; }
+    IMMER_NODISCARD flex_vector push_front(value_type value) const
+    {
+        return flex_vector{}.push_back(value) + *this;
+    }
 
     /*!
      * Returns a flex_vector containing value `value` at position `index`.
@@ -255,11 +285,15 @@ class flex_vector
      *
      * @endrst
      */
-    flex_vector set(size_type index, value_type value) const&
-    { return impl_.assoc(index, std::move(value)); }
+    IMMER_NODISCARD flex_vector set(size_type index, value_type value) const&
+    {
+        return impl_.assoc(index, std::move(value));
+    }
 
-    decltype(auto) set(size_type index, value_type value) &&
-    { return set_move(move_t{}, index, std::move(value)); }
+    IMMER_NODISCARD decltype(auto) set(size_type index, value_type value) &&
+    {
+        return set_move(move_t{}, index, std::move(value));
+    }
 
     /*!
      * Returns a vector containing the result of the expression
@@ -281,12 +315,16 @@ class flex_vector
 
      */
     template <typename FnT>
-    flex_vector update(size_type index, FnT&& fn) const&
-    { return impl_.update(index, std::forward<FnT>(fn)); }
+    IMMER_NODISCARD flex_vector update(size_type index, FnT&& fn) const&
+    {
+        return impl_.update(index, std::forward<FnT>(fn));
+    }
 
     template <typename FnT>
-    decltype(auto) update(size_type index, FnT&& fn) &&
-    { return update_move(move_t{}, index, std::forward<FnT>(fn)); }
+    IMMER_NODISCARD decltype(auto) update(size_type index, FnT&& fn) &&
+    {
+        return update_move(move_t{}, index, std::forward<FnT>(fn));
+    }
 
     /*!
      * Returns a vector containing only the first `min(elems, size())`
@@ -304,11 +342,15 @@ class flex_vector
      *
      * @endrst
      */
-    flex_vector take(size_type elems) const&
-    { return impl_.take(elems); }
+    IMMER_NODISCARD flex_vector take(size_type elems) const&
+    {
+        return impl_.take(elems);
+    }
 
-    decltype(auto) take(size_type elems) &&
-    { return take_move(move_t{}, elems); }
+    IMMER_NODISCARD decltype(auto) take(size_type elems) &&
+    {
+        return take_move(move_t{}, elems);
+    }
 
     /*!
      * Returns a vector without the first `min(elems, size())`
@@ -326,11 +368,15 @@ class flex_vector
      *
      * @endrst
      */
-    flex_vector drop(size_type elems) const&
-    { return impl_.drop(elems); }
+    IMMER_NODISCARD flex_vector drop(size_type elems) const&
+    {
+        return impl_.drop(elems);
+    }
 
-    decltype(auto) drop(size_type elems) &&
-    { return drop_move(move_t{}, elems); }
+    IMMER_NODISCARD decltype(auto) drop(size_type elems) &&
+    {
+        return drop_move(move_t{}, elems);
+    }
 
     /*!
      * Concatenation operator. Returns a flex_vector with the contents
@@ -348,17 +394,29 @@ class flex_vector
      *
      * @endrst
      */
-    friend flex_vector operator+ (const flex_vector& l, const flex_vector& r)
-    { return l.impl_.concat(r.impl_); }
+    IMMER_NODISCARD friend flex_vector operator+(const flex_vector& l,
+                                                 const flex_vector& r)
+    {
+        return l.impl_.concat(r.impl_);
+    }
 
-    friend decltype(auto) operator+ (flex_vector&& l, const flex_vector& r)
-    { return concat_move(move_t{}, std::move(l), r); }
+    IMMER_NODISCARD friend decltype(auto) operator+(flex_vector&& l,
+                                                    const flex_vector& r)
+    {
+        return concat_move(move_t{}, std::move(l), r);
+    }
 
-    friend decltype(auto) operator+ (const flex_vector& l, flex_vector&& r)
-    { return concat_move(move_t{}, l, std::move(r)); }
+    IMMER_NODISCARD friend decltype(auto) operator+(const flex_vector& l,
+                                                    flex_vector&& r)
+    {
+        return concat_move(move_t{}, l, std::move(r));
+    }
 
-    friend decltype(auto) operator+ (flex_vector&& l, flex_vector&& r)
-    { return concat_move(move_t{}, std::move(l), std::move(r)); }
+    IMMER_NODISCARD friend decltype(auto) operator+(flex_vector&& l,
+                                                    flex_vector&& r)
+    {
+        return concat_move(move_t{}, std::move(l), std::move(r));
+    }
 
     /*!
      * Returns a flex_vector with the `value` inserted at index
@@ -376,19 +434,23 @@ class flex_vector
      *
      * @endrst
      */
-    flex_vector insert(size_type pos, T value) const&
-    { return take(pos).push_back(std::move(value)) + drop(pos); }
-    decltype(auto) insert(size_type pos, T value) &&
+    IMMER_NODISCARD flex_vector insert(size_type pos, T value) const&
+    {
+        return take(pos).push_back(std::move(value)) + drop(pos);
+    }
+    IMMER_NODISCARD decltype(auto) insert(size_type pos, T value) &&
     {
         using std::move;
         auto rs = drop(pos);
-        return std::move(*this).take(pos).push_back(
-            std::move(value)) + std::move(rs);
+        return std::move(*this).take(pos).push_back(std::move(value)) +
+               std::move(rs);
     }
 
-    flex_vector insert(size_type pos, flex_vector value) const&
-    { return take(pos) + std::move(value) + drop(pos); }
-    decltype(auto) insert(size_type pos, flex_vector value) &&
+    IMMER_NODISCARD flex_vector insert(size_type pos, flex_vector value) const&
+    {
+        return take(pos) + std::move(value) + drop(pos);
+    }
+    IMMER_NODISCARD decltype(auto) insert(size_type pos, flex_vector value) &&
     {
         using std::move;
         auto rs = drop(pos);
@@ -410,17 +472,21 @@ class flex_vector
      *
      * @endrst
      */
-    flex_vector erase(size_type pos) const&
-    { return take(pos) + drop(pos + 1); }
-    decltype(auto) erase(size_type pos) &&
+    IMMER_NODISCARD flex_vector erase(size_type pos) const&
+    {
+        return take(pos) + drop(pos + 1);
+    }
+    IMMER_NODISCARD decltype(auto) erase(size_type pos) &&
     {
         auto rs = drop(pos + 1);
         return std::move(*this).take(pos) + std::move(rs);
     }
 
-    flex_vector erase(size_type pos, size_type lpos) const&
-    { return lpos > pos ? take(pos) + drop(lpos) : *this; }
-    decltype(auto) erase(size_type pos, size_type lpos) &&
+    IMMER_NODISCARD flex_vector erase(size_type pos, size_type lpos) const&
+    {
+        return lpos > pos ? take(pos) + drop(lpos) : *this;
+    }
+    IMMER_NODISCARD decltype(auto) erase(size_type pos, size_type lpos) &&
     {
         if (lpos > pos) {
             auto rs = drop(lpos);
@@ -434,17 +500,28 @@ class flex_vector
      * Returns an @a transient form of this container, an
      * `immer::flex_vector_transient`.
      */
-    transient_type transient() const&
-    { return transient_type{ impl_ }; }
-    transient_type transient() &&
-    { return transient_type{ std::move(impl_) }; }
+    IMMER_NODISCARD transient_type transient() const& { return impl_; }
+    IMMER_NODISCARD transient_type transient() && { return std::move(impl_); }
+
+    /*!
+     * Returns a value that can be used as identity for the container.  If two
+     * values have the same identity, they are guaranteed to be equal and to
+     * contain the same objects.  However, two equal containers are not
+     * guaranteed to have the same identity.
+     */
+    std::pair<void*, void*> identity() const
+    {
+        return {impl_.root, impl_.tail};
+    }
 
     // Semi-private
     const impl_t& impl() const { return impl_; }
 
 #if IMMER_DEBUG_PRINT
-    void debug_print(std::ostream& out=std::cerr) const
-    { impl_.debug_print(out); }
+    void debug_print(std::ostream& out = std::cerr) const
+    {
+        impl_.debug_print(out);
+    }
 #endif
 
 private:
@@ -456,47 +533,92 @@ class flex_vector
 #if IMMER_DEBUG_PRINT
         // force the compiler to generate debug_print, so we can call
         // it from a debugger
-        [](volatile auto){}(&flex_vector::debug_print);
+        [](volatile auto) {}(&flex_vector::debug_print);
 #endif
     }
 
     flex_vector&& push_back_move(std::true_type, value_type value)
-    { impl_.push_back_mut({}, std::move(value)); return std::move(*this); }
+    {
+        impl_.push_back_mut({}, std::move(value));
+        return std::move(*this);
+    }
     flex_vector push_back_move(std::false_type, value_type value)
-    { return impl_.push_back(std::move(value)); }
+    {
+        return impl_.push_back(std::move(value));
+    }
 
     flex_vector&& set_move(std::true_type, size_type index, value_type value)
-    { impl_.assoc_mut({}, index, std::move(value)); return std::move(*this); }
+    {
+        impl_.assoc_mut({}, index, std::move(value));
+        return std::move(*this);
+    }
     flex_vector set_move(std::false_type, size_type index, value_type value)
-    { return impl_.assoc(index, std::move(value)); }
+    {
+        return impl_.assoc(index, std::move(value));
+    }
 
     template <typename Fn>
     flex_vector&& update_move(std::true_type, size_type index, Fn&& fn)
-    { impl_.update_mut({}, index, std::forward<Fn>(fn)); return std::move(*this); }
+    {
+        impl_.update_mut({}, index, std::forward<Fn>(fn));
+        return std::move(*this);
+    }
     template <typename Fn>
     flex_vector update_move(std::false_type, size_type index, Fn&& fn)
-    { return impl_.update(index, std::forward<Fn>(fn)); }
+    {
+        return impl_.update(index, std::forward<Fn>(fn));
+    }
 
     flex_vector&& take_move(std::true_type, size_type elems)
-    { impl_.take_mut({}, elems); return std::move(*this); }
+    {
+        impl_.take_mut({}, elems);
+        return std::move(*this);
+    }
     flex_vector take_move(std::false_type, size_type elems)
-    { return impl_.take(elems); }
+    {
+        return impl_.take(elems);
+    }
 
     flex_vector&& drop_move(std::true_type, size_type elems)
-    { impl_.drop_mut({}, elems); return std::move(*this); }
+    {
+        impl_.drop_mut({}, elems);
+        return std::move(*this);
+    }
     flex_vector drop_move(std::false_type, size_type elems)
-    { return impl_.drop(elems); }
-
-    static flex_vector&& concat_move(std::true_type, flex_vector&& l, const flex_vector& r)
-    { concat_mut_l(l.impl_, {}, r.impl_); return std::move(l); }
-    static flex_vector&& concat_move(std::true_type, const flex_vector& l, flex_vector&& r)
-    { concat_mut_r(l.impl_, r.impl_, {}); return std::move(r); }
-    static flex_vector&& concat_move(std::true_type, flex_vector&& l, flex_vector&& r)
-    { concat_mut_lr_l(l.impl_, {}, r.impl_, {}); return std::move(l); }
-    static flex_vector concat_move(std::false_type, const flex_vector& l, const flex_vector& r)
-    { return l.impl_.concat(r.impl_); }
-
-    impl_t impl_ = impl_t::empty();
+    {
+        return impl_.drop(elems);
+    }
+
+    static flex_vector&&
+    concat_move(std::true_type, flex_vector&& l, const flex_vector& r)
+    {
+        concat_mut_l(l.impl_, {}, r.impl_);
+        return std::move(l);
+    }
+    static flex_vector&&
+    concat_move(std::true_type, const flex_vector& l, flex_vector&& r)
+    {
+        concat_mut_r(l.impl_, r.impl_, {});
+        return std::move(r);
+    }
+    static flex_vector&&
+    concat_move(std::true_type, flex_vector&& l, flex_vector&& r)
+    {
+        concat_mut_lr_l(l.impl_, {}, r.impl_, {});
+        return std::move(l);
+    }
+    static flex_vector
+    concat_move(std::false_type, const flex_vector& l, const flex_vector& r)
+    {
+        return l.impl_.concat(r.impl_);
+    }
+
+    impl_t impl_ = {};
 };
 
+static_assert(std::is_nothrow_move_constructible<flex_vector<int>>::value,
+              "flex_vector is not nothrow move constructible");
+static_assert(std::is_nothrow_move_assignable<flex_vector<int>>::value,
+              "flex_vector is not nothrow move assignable");
+
 } // namespace immer
diff --git a/src/immer/flex_vector_transient.hpp b/src/immer/flex_vector_transient.hpp
index 0d6e50aae1..5c419ae02b 100644
--- a/src/immer/flex_vector_transient.hpp
+++ b/src/immer/flex_vector_transient.hpp
@@ -37,32 +37,32 @@ class vector_transient;
  * @endrst
  */
 template <typename T,
-          typename MemoryPolicy   = default_memory_policy,
-          detail::rbts::bits_t B  = default_bits,
-          detail::rbts::bits_t BL = detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
-class flex_vector_transient
-    : MemoryPolicy::transience_t::owner
+          typename MemoryPolicy  = default_memory_policy,
+          detail::rbts::bits_t B = default_bits,
+          detail::rbts::bits_t BL =
+              detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
+class flex_vector_transient : MemoryPolicy::transience_t::owner
 {
-    using impl_t = detail::rbts::rrbtree<T, MemoryPolicy, B, BL>;
-    using base_t = typename MemoryPolicy::transience_t::owner;
+    using impl_t  = detail::rbts::rrbtree<T, MemoryPolicy, B, BL>;
+    using base_t  = typename MemoryPolicy::transience_t::owner;
     using owner_t = typename MemoryPolicy::transience_t::owner;
 
 public:
-    static constexpr auto bits = B;
+    static constexpr auto bits      = B;
     static constexpr auto bits_leaf = BL;
-    using memory_policy = MemoryPolicy;
+    using memory_policy             = MemoryPolicy;
 
-    using value_type = T;
-    using reference = const T&;
-    using size_type = detail::rbts::size_t;
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = detail::rbts::size_t;
     using difference_type = std::ptrdiff_t;
     using const_reference = const T&;
 
-    using iterator         = detail::rbts::rrbtree_iterator<T, MemoryPolicy, B, BL>;
+    using iterator = detail::rbts::rrbtree_iterator<T, MemoryPolicy, B, BL>;
     using const_iterator   = iterator;
     using reverse_iterator = std::reverse_iterator<iterator>;
 
-    using persistent_type  = flex_vector<T, MemoryPolicy, B, BL>;
+    using persistent_type = flex_vector<T, MemoryPolicy, B, BL>;
 
     /*!
      * Default constructor.  It creates a flex_vector of `size() == 0`.  It
@@ -76,9 +76,11 @@ class flex_vector_transient
      * @f$ O(1) @f$.
      */
     flex_vector_transient(vector_transient<T, MemoryPolicy, B, BL> v)
-        : base_t { std::move(static_cast<base_t&>(v)) }
-        , impl_ { v.impl_.size, v.impl_.shift,
-                  v.impl_.root->inc(), v.impl_.tail->inc() }
+        : base_t{std::move(static_cast<base_t&>(v))}
+        , impl_{v.impl_.size,
+                v.impl_.shift,
+                v.impl_.root->inc(),
+                v.impl_.tail->inc()}
     {}
 
     /*!
@@ -86,39 +88,48 @@ class flex_vector_transient
      * collection. It does not allocate memory and its complexity is
      * @f$ O(1) @f$.
      */
-    iterator begin() const { return {impl_}; }
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
 
     /*!
      * Returns an iterator pointing just after the last element of the
      * collection. It does not allocate and its complexity is @f$ O(1) @f$.
      */
-    iterator end()   const { return {impl_, typename iterator::end_t{}}; }
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing at the first element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rbegin() const { return reverse_iterator{end()}; }
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing after the last element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rend()   const { return reverse_iterator{begin()}; }
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
 
     /*!
      * Returns the number of elements in the container.  It does
      * not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    size_type size() const { return impl_.size; }
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
 
     /*!
      * Returns `true` if there are no elements in the container.  It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    bool empty() const { return impl_.size == 0; }
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
 
     /*!
      * Returns a `const` reference to the element at position `index`.
@@ -126,8 +137,7 @@ class flex_vector_transient
      * allocate memory and its complexity is *effectively* @f$ O(1)
      * @f$.
      */
-    reference operator[] (size_type index) const
-    { return impl_.get(index); }
+    reference operator[](size_type index) const { return impl_.get(index); }
 
     /*!
      * Returns a `const` reference to the element at position
@@ -135,15 +145,16 @@ class flex_vector_transient
      * index \geq size() @f$.  It does not allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
-    reference at(size_type index) const
-    { return impl_.get_check(index); }
+    reference at(size_type index) const { return impl_.get_check(index); }
 
     /*!
      * Inserts `value` at the end.  It may allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
     void push_back(value_type value)
-    { impl_.push_back_mut(*this, std::move(value)); }
+    {
+        impl_.push_back_mut(*this, std::move(value));
+    }
 
     /*!
      * Sets to the value `value` at position `idx`.
@@ -152,7 +163,9 @@ class flex_vector_transient
      * *effectively* @f$ O(1) @f$.
      */
     void set(size_type index, value_type value)
-    { impl_.assoc_mut(*this, index, std::move(value)); }
+    {
+        impl_.assoc_mut(*this, index, std::move(value));
+    }
 
     /*!
      * Updates the vector to contain the result of the expression
@@ -163,35 +176,23 @@ class flex_vector_transient
      */
     template <typename FnT>
     void update(size_type index, FnT&& fn)
-    { impl_.update_mut(*this, index, std::forward<FnT>(fn)); }
+    {
+        impl_.update_mut(*this, index, std::forward<FnT>(fn));
+    }
 
     /*!
      * Resizes the vector to only contain the first `min(elems, size())`
      * elements. It may allocate memory and its complexity is
      * *effectively* @f$ O(1) @f$.
      */
-    void take(size_type elems)
-    { impl_.take_mut(*this, elems); }
+    void take(size_type elems) { impl_.take_mut(*this, elems); }
 
     /*!
      * Removes the first the first `min(elems, size())`
      * elements. It may allocate memory and its complexity is
      * *effectively* @f$ O(1) @f$.
      */
-    void drop(size_type elems)
-    { impl_.drop_mut(*this, elems); }
-
-    /*!
-     * Returns an @a immutable form of this container, an
-     * `immer::flex_vector`.
-     */
-    persistent_type persistent() &
-    {
-        this->owner_t::operator=(owner_t{});
-        return persistent_type{ impl_ };
-    }
-    persistent_type persistent() &&
-    { return persistent_type{ std::move(impl_) }; }
+    void drop(size_type elems) { impl_.drop_mut(*this, elems); }
 
     /*!
      * Appends the contents of the `r` at the end.  It may allocate
@@ -204,7 +205,9 @@ class flex_vector_transient
         concat_mut_l(impl_, *this, r.impl_);
     }
     void append(flex_vector_transient&& r)
-    { concat_mut_lr_l(impl_, *this, r.impl_, r); }
+    {
+        concat_mut_lr_l(impl_, *this, r.impl_, r);
+    }
 
     /*!
      * Prepends the contents of the `l` at the beginning.  It may
@@ -217,7 +220,20 @@ class flex_vector_transient
         concat_mut_r(l.impl_, impl_, *this);
     }
     void prepend(flex_vector_transient&& l)
-    { concat_mut_lr_r(l.impl_, l, impl_, *this); }
+    {
+        concat_mut_lr_r(l.impl_, l, impl_, *this);
+    }
+
+    /*!
+     * Returns an @a immutable form of this container, an
+     * `immer::flex_vector`.
+     */
+    IMMER_NODISCARD persistent_type persistent() &
+    {
+        this->owner_t::operator=(owner_t{});
+        return impl_;
+    }
+    IMMER_NODISCARD persistent_type persistent() && { return std::move(impl_); }
 
 private:
     friend persistent_type;
@@ -226,7 +242,7 @@ class flex_vector_transient
         : impl_(std::move(impl))
     {}
 
-    impl_t  impl_  = impl_t::empty();
+    impl_t impl_ = {};
 };
 
 } // namespace immer
diff --git a/src/immer/heap/cpp_heap.hpp b/src/immer/heap/cpp_heap.hpp
index cd129b406b..3789754821 100644
--- a/src/immer/heap/cpp_heap.hpp
+++ b/src/immer/heap/cpp_heap.hpp
@@ -8,6 +8,7 @@
 
 #pragma once
 
+#include <cstddef>
 #include <memory>
 
 namespace immer {
@@ -32,7 +33,7 @@ struct cpp_heap
      * `allocate`.  One must not use nor deallocate again a memory
      * region that once it has been deallocated.
      */
-    static void deallocate(std::size_t size, void* data)
+    static void deallocate(std::size_t, void* data)
     {
         ::operator delete(data);
     }
diff --git a/src/immer/heap/debug_size_heap.hpp b/src/immer/heap/debug_size_heap.hpp
index 5cfb74ca8f..d5288c646f 100644
--- a/src/immer/heap/debug_size_heap.hpp
+++ b/src/immer/heap/debug_size_heap.hpp
@@ -8,11 +8,14 @@
 
 #pragma once
 
-#include <cassert>
-#include <cstddef>
 #include <immer/config.hpp>
 #include <immer/heap/identity_heap.hpp>
 
+#include <cassert>
+#include <cstddef>
+#include <type_traits>
+#include <memory>
+
 namespace immer {
 
 #if IMMER_ENABLE_DEBUG_SIZE_HEAP
@@ -24,15 +27,27 @@ namespace immer {
 template <typename Base>
 struct debug_size_heap
 {
-    // temporary fix until https://github.com/arximboldi/immer/issues/78 is fixed
-    constexpr static auto extra_size = sizeof(void*) * 2; //alignof(std::max_align_t);
+#if defined(__MINGW32__) && !defined(__MINGW64__)
+    // There is a bug in MinGW 32bit:
+    // https://sourceforge.net/p/mingw-w64/bugs/778/ It causes different
+    // versions of std::max_align_t to be defined, depending on inclusion order
+    // of stddef.h and stdint.h. As we have no control over the inclusion order
+    // here (as it might be set in stone by the outside world), we can't easily
+    // pin it to one of both versions of std::max_align_t. This means, we have
+    // to hardcode extra_size for MinGW 32bit builds until the mentioned bug is
+    // fixed.
+    constexpr static auto extra_size = 8;
+#else
+    constexpr static auto extra_size = sizeof(
+        std::aligned_storage_t<sizeof(std::size_t), alignof(std::max_align_t)>);
+#endif
 
     template <typename... Tags>
     static void* allocate(std::size_t size, Tags... tags)
     {
         auto p = (std::size_t*) Base::allocate(size + extra_size, tags...);
-        *p = size;
-        return ((char*)p) + extra_size;
+        new (p) std::size_t{size};
+        return ((char*) p) + extra_size;
     }
 
     template <typename... Tags>
diff --git a/src/immer/heap/free_list_heap.hpp b/src/immer/heap/free_list_heap.hpp
index d82d0967b0..15ae01d60f 100644
--- a/src/immer/heap/free_list_heap.hpp
+++ b/src/immer/heap/free_list_heap.hpp
@@ -8,11 +8,12 @@
 
 #pragma once
 
-#include <immer/heap/with_data.hpp>
 #include <immer/heap/free_list_node.hpp>
+#include <immer/heap/with_data.hpp>
 
 #include <atomic>
 #include <cassert>
+#include <cstddef>
 
 namespace immer {
 
diff --git a/src/immer/heap/free_list_node.hpp b/src/immer/heap/free_list_node.hpp
index 5c2f5b7529..acab4779aa 100644
--- a/src/immer/heap/free_list_node.hpp
+++ b/src/immer/heap/free_list_node.hpp
@@ -18,8 +18,7 @@ struct free_list_node
 };
 
 template <typename Base>
-struct with_free_list_node
-    : with_data<free_list_node, Base>
+struct with_free_list_node : with_data<free_list_node, Base>
 {};
 
 } // namespace immer
diff --git a/src/immer/heap/gc_heap.hpp b/src/immer/heap/gc_heap.hpp
index f37da2f6e3..8fc7754e5e 100644
--- a/src/immer/heap/gc_heap.hpp
+++ b/src/immer/heap/gc_heap.hpp
@@ -17,30 +17,34 @@
 #error "Using garbage collection requires libgc"
 #endif
 
+#include <cassert>
 #include <cstdlib>
+#include <exception>
 #include <memory>
 
 namespace immer {
 
-#ifdef __APPLE__
-#define IMMER_GC_REQUIRE_INIT 1
+#ifdef IMMER_GC_REQUIRE_INIT
+#define IMMER_GC_REQUIRE_INIT_ IMMER_GC_REQUIRE_INIT
+#elifdef __APPLE__
+#define IMMER_GC_REQUIRE_INIT_ 1
 #else
-#define IMMER_GC_REQUIRE_INIT 0
+#define IMMER_GC_REQUIRE_INIT_ 0
 #endif
 
-#if IMMER_GC_REQUIRE_INIT
+#if IMMER_GC_REQUIRE_INIT_
 
 namespace detail {
 
-template <int Dummy=0>
+template <int Dummy = 0>
 struct gc_initializer
 {
-    gc_initializer() { GC_init(); }
+    gc_initializer() { GC_INIT(); }
     static gc_initializer init;
 };
 
 template <int D>
-gc_initializer<D> gc_initializer<D>::init {};
+gc_initializer<D> gc_initializer<D>::init{};
 
 inline void gc_initializer_guard()
 {
@@ -56,7 +60,7 @@ inline void gc_initializer_guard()
 
 #define IMMER_GC_INIT_GUARD_
 
-#endif // IMMER_GC_REQUIRE_INIT
+#endif // IMMER_GC_REQUIRE_INIT_
 
 /*!
  * Heap that uses a tracing garbage collector.
@@ -103,7 +107,7 @@ class gc_heap
         IMMER_GC_INIT_GUARD_;
         auto p = GC_malloc(n);
         if (IMMER_UNLIKELY(!p))
-            throw std::bad_alloc{};
+            IMMER_THROW(std::bad_alloc{});
         return p;
     }
 
@@ -112,14 +116,11 @@ class gc_heap
         IMMER_GC_INIT_GUARD_;
         auto p = GC_malloc_atomic(n);
         if (IMMER_UNLIKELY(!p))
-            throw std::bad_alloc{};
+            IMMER_THROW(std::bad_alloc{});
         return p;
     }
 
-    static void deallocate(std::size_t, void* data)
-    {
-        GC_free(data);
-    }
+    static void deallocate(std::size_t, void* data) { GC_free(data); }
 
     static void deallocate(std::size_t, void* data, norefs_tag)
     {
diff --git a/src/immer/heap/heap_policy.hpp b/src/immer/heap/heap_policy.hpp
index a0723cbcf2..1a858c5e12 100644
--- a/src/immer/heap/heap_policy.hpp
+++ b/src/immer/heap/heap_policy.hpp
@@ -8,14 +8,14 @@
 
 #pragma once
 
+#include <immer/config.hpp>
 #include <immer/heap/debug_size_heap.hpp>
 #include <immer/heap/free_list_heap.hpp>
 #include <immer/heap/split_heap.hpp>
 #include <immer/heap/thread_local_free_list_heap.hpp>
-#include <immer/config.hpp>
 
-#include <cstdlib>
 #include <algorithm>
+#include <cstdlib>
 
 namespace immer {
 
@@ -37,18 +37,18 @@ struct heap_policy
 template <typename Deriv, typename HeapPolicy>
 struct enable_optimized_heap_policy
 {
-    static void* operator new (std::size_t size)
+    static void* operator new(std::size_t size)
     {
-        using heap_type = typename HeapPolicy
-            ::template optimized<sizeof(Deriv)>::type;
+        using heap_type =
+            typename HeapPolicy ::template optimized<sizeof(Deriv)>::type;
 
         return heap_type::allocate(size);
     }
 
-    static void operator delete (void* data, std::size_t size)
+    static void operator delete(void* data, std::size_t size)
     {
-        using heap_type = typename HeapPolicy
-            ::template optimized<sizeof(Deriv)>::type;
+        using heap_type =
+            typename HeapPolicy ::template optimized<sizeof(Deriv)>::type;
 
         heap_type::deallocate(size, data);
     }
@@ -85,8 +85,7 @@ struct enable_optimized_heap_policy
  * @rst
  *
  * .. tip:: For many applications that use immutable data structures
- *    significantly, this is actually the best heap policy, and it
- *    might become the default in the future.
+ *    significantly, this is actually the best heap policy.
  *
  *    Note that most our data structures internally use trees with the
  *    same big branching factors.  This means that all *vectors*,
@@ -99,8 +98,7 @@ struct enable_optimized_heap_policy
  *
  * @endrst
  */
-template <typename Heap,
-          std::size_t Limit = default_free_list_size>
+template <typename Heap, std::size_t Limit = default_free_list_size>
 struct free_list_heap_policy
 {
     using type = debug_size_heap<Heap>;
@@ -108,16 +106,13 @@ struct free_list_heap_policy
     template <std::size_t Size>
     struct optimized
     {
-        using type = split_heap<
-            Size,
-            with_free_list_node<
-                thread_local_free_list_heap<
-                    Size,
-                    Limit,
-                    free_list_heap<
-                        Size, Limit,
-                        debug_size_heap<Heap>>>>,
-            debug_size_heap<Heap>>;
+        using type =
+            split_heap<Size,
+                       with_free_list_node<thread_local_free_list_heap<
+                           Size,
+                           Limit,
+                           free_list_heap<Size, Limit, debug_size_heap<Heap>>>>,
+                       debug_size_heap<Heap>>;
     };
 };
 
@@ -126,8 +121,7 @@ struct free_list_heap_policy
  * multi-threading, so a single global free list with no concurrency
  * checks is used.
  */
-template <typename Heap,
-          std::size_t Limit = default_free_list_size>
+template <typename Heap, std::size_t Limit = default_free_list_size>
 struct unsafe_free_list_heap_policy
 {
     using type = Heap;
@@ -138,9 +132,7 @@ struct unsafe_free_list_heap_policy
         using type = split_heap<
             Size,
             with_free_list_node<
-                unsafe_free_list_heap<
-                    Size, Limit,
-                    debug_size_heap<Heap>>>,
+                unsafe_free_list_heap<Size, Limit, debug_size_heap<Heap>>>,
             debug_size_heap<Heap>>;
     };
 };
diff --git a/src/immer/heap/malloc_heap.hpp b/src/immer/heap/malloc_heap.hpp
index 73909058de..af9e983fae 100644
--- a/src/immer/heap/malloc_heap.hpp
+++ b/src/immer/heap/malloc_heap.hpp
@@ -10,8 +10,10 @@
 
 #include <immer/config.hpp>
 
-#include <memory>
+#include <cassert>
 #include <cstdlib>
+#include <exception>
+#include <memory>
 
 namespace immer {
 
@@ -29,7 +31,7 @@ struct malloc_heap
     {
         auto p = std::malloc(size);
         if (IMMER_UNLIKELY(!p))
-            throw std::bad_alloc{};
+            IMMER_THROW(std::bad_alloc{});
         return p;
     }
 
@@ -38,10 +40,7 @@ struct malloc_heap
      * `allocate`.  One must not use nor deallocate again a memory
      * region that once it has been deallocated.
      */
-    static void deallocate(std::size_t, void* data)
-    {
-        std::free(data);
-    }
+    static void deallocate(std::size_t, void* data) { std::free(data); }
 };
 
 } // namespace immer
diff --git a/src/immer/heap/split_heap.hpp b/src/immer/heap/split_heap.hpp
index 8ce210815f..18ae684bfd 100644
--- a/src/immer/heap/split_heap.hpp
+++ b/src/immer/heap/split_heap.hpp
@@ -10,6 +10,7 @@
 
 #include <atomic>
 #include <cassert>
+#include <cstddef>
 
 namespace immer {
 
@@ -23,9 +24,8 @@ struct split_heap
     template <typename... Tags>
     static void* allocate(std::size_t size, Tags... tags)
     {
-        return size <= Size
-            ? SmallHeap::allocate(size, tags...)
-            : BigHeap::allocate(size, tags...);
+        return size <= Size ? SmallHeap::allocate(size, tags...)
+                            : BigHeap::allocate(size, tags...);
     }
 
     template <typename... Tags>
diff --git a/src/immer/heap/tags.hpp b/src/immer/heap/tags.hpp
index a3012bd3ef..d1ce48d05c 100644
--- a/src/immer/heap/tags.hpp
+++ b/src/immer/heap/tags.hpp
@@ -10,6 +10,7 @@
 
 namespace immer {
 
-struct norefs_tag {};
+struct norefs_tag
+{};
 
 } // namespace immer
diff --git a/src/immer/heap/thread_local_free_list_heap.hpp b/src/immer/heap/thread_local_free_list_heap.hpp
index 2539ce73c1..b97a7f5082 100644
--- a/src/immer/heap/thread_local_free_list_heap.hpp
+++ b/src/immer/heap/thread_local_free_list_heap.hpp
@@ -10,6 +10,8 @@
 
 #include <immer/heap/unsafe_free_list_heap.hpp>
 
+#include <cstddef>
+
 namespace immer {
 namespace detail {
 
@@ -45,11 +47,11 @@ struct thread_local_free_list_storage
  * @tparam Base  Type of the parent heap.
  */
 template <std::size_t Size, std::size_t Limit, typename Base>
-struct thread_local_free_list_heap : detail::unsafe_free_list_heap_impl<
-    detail::thread_local_free_list_storage,
-    Size,
-    Limit,
-    Base>
+struct thread_local_free_list_heap
+    : detail::unsafe_free_list_heap_impl<detail::thread_local_free_list_storage,
+                                         Size,
+                                         Limit,
+                                         Base>
 {};
 
 } // namespace immer
diff --git a/src/immer/heap/unsafe_free_list_heap.hpp b/src/immer/heap/unsafe_free_list_heap.hpp
index 9a1fdd73e4..9b41dfcb37 100644
--- a/src/immer/heap/unsafe_free_list_heap.hpp
+++ b/src/immer/heap/unsafe_free_list_heap.hpp
@@ -10,7 +10,9 @@
 
 #include <immer/config.hpp>
 #include <immer/heap/free_list_node.hpp>
+
 #include <cassert>
+#include <cstddef>
 
 namespace immer {
 namespace detail {
@@ -26,12 +28,12 @@ struct unsafe_free_list_storage
 
     static head_t& head()
     {
-        static head_t head_ {nullptr, 0};
+        static head_t head_{nullptr, 0};
         return head_;
     }
 };
 
-template <template<class>class Storage,
+template <template <class> class Storage,
           std::size_t Size,
           std::size_t Limit,
           typename Base>
@@ -67,8 +69,8 @@ class unsafe_free_list_heap_impl : Base
         if (storage::head().count >= Limit)
             base_t::deallocate(Size + sizeof(free_list_node), data);
         else {
-            auto n = static_cast<free_list_node*>(data);
-            n->next = storage::head().data;
+            auto n               = static_cast<free_list_node*>(data);
+            n->next              = storage::head().data;
             storage::head().data = n;
             ++storage::head().count;
         }
@@ -78,7 +80,8 @@ class unsafe_free_list_heap_impl : Base
     {
         while (storage::head().data) {
             auto n = storage::head().data->next;
-            base_t::deallocate(Size + sizeof(free_list_node), storage::head().data);
+            base_t::deallocate(Size + sizeof(free_list_node),
+                               storage::head().data);
             storage::head().data = n;
             --storage::head().count;
         }
@@ -98,11 +101,11 @@ class unsafe_free_list_heap_impl : Base
  * @tparam Base  Type of the parent heap.
  */
 template <std::size_t Size, std::size_t Limit, typename Base>
-struct unsafe_free_list_heap : detail::unsafe_free_list_heap_impl<
-    detail::unsafe_free_list_storage,
-    Size,
-    Limit,
-    Base>
+struct unsafe_free_list_heap
+    : detail::unsafe_free_list_heap_impl<detail::unsafe_free_list_storage,
+                                         Size,
+                                         Limit,
+                                         Base>
 {};
 
 } // namespace immer
diff --git a/src/immer/lock/no_lock_policy.hpp b/src/immer/lock/no_lock_policy.hpp
new file mode 100644
index 0000000000..6374699065
--- /dev/null
+++ b/src/immer/lock/no_lock_policy.hpp
@@ -0,0 +1,25 @@
+//
+// 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
+
+namespace immer {
+
+struct no_lock_policy
+{
+    bool try_lock() { return true; }
+    void lock() {}
+    void unlock() {}
+
+    struct scoped_lock
+    {
+        scoped_lock(no_lock_policy&) {}
+    };
+};
+
+} // namespace immer
diff --git a/src/immer/lock/spinlock_policy.hpp b/src/immer/lock/spinlock_policy.hpp
new file mode 100644
index 0000000000..cebe8a533c
--- /dev/null
+++ b/src/immer/lock/spinlock_policy.hpp
@@ -0,0 +1,68 @@
+//
+// 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 <atomic>
+#include <thread>
+
+// This has been shamelessly copied from boost...
+#if defined(_MSC_VER) && _MSC_VER >= 1310 &&                                   \
+    (defined(_M_IX86) || defined(_M_X64)) && !defined(__c2__)
+extern "C" void _mm_pause();
+#define IMMER_SMT_PAUSE _mm_pause()
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+#define IMMER_SMT_PAUSE __asm__ __volatile__("rep; nop" : : : "memory")
+#endif
+
+namespace immer {
+
+// This is an atomic spinlock similar to the one used by boost to provide
+// "atomic" shared_ptr operations.  It also does not differ much from the one
+// from libc++ or libstdc++...
+struct spinlock_policy
+{
+    std::atomic_flag v_{};
+
+    bool try_lock() { return !v_.test_and_set(std::memory_order_acquire); }
+
+    void lock()
+    {
+        for (auto k = 0u; !try_lock(); ++k) {
+            if (k < 4)
+                continue;
+#ifdef IMMER_SMT_PAUSE
+            else if (k < 16)
+                IMMER_SMT_PAUSE;
+#endif
+            else
+                std::this_thread::yield();
+        }
+    }
+
+    void unlock() { v_.clear(std::memory_order_release); }
+
+    struct scoped_lock
+    {
+        scoped_lock(const scoped_lock&) = delete;
+        scoped_lock& operator=(const scoped_lock&) = delete;
+
+        explicit scoped_lock(spinlock_policy& sp)
+            : sp_{sp}
+        {
+            sp.lock();
+        }
+
+        ~scoped_lock() { sp_.unlock(); }
+
+    private:
+        spinlock_policy& sp_;
+    };
+};
+
+} // namespace immer
diff --git a/src/immer/map.hpp b/src/immer/map.hpp
index 99e483a2d4..cab8e5d5d8 100644
--- a/src/immer/map.hpp
+++ b/src/immer/map.hpp
@@ -8,11 +8,14 @@
 
 #pragma once
 
-#include <immer/memory_policy.hpp>
+#include <immer/config.hpp>
 #include <immer/detail/hamts/champ.hpp>
 #include <immer/detail/hamts/champ_iterator.hpp>
+#include <immer/memory_policy.hpp>
 
+#include <cassert>
 #include <functional>
+#include <stdexcept>
 
 namespace immer {
 
@@ -38,7 +41,7 @@ class map_transient;
  *
  * @rst
  *
- * This cotainer provides a good trade-off between cache locality,
+ * This container provides a good trade-off between cache locality,
  * search, update performance and structural sharing.  It does so by
  * storing the data in contiguous chunks of :math:`2^{B}` elements.
  * When storing big objects, the size of these contiguous chunks can
@@ -65,17 +68,24 @@ class map
 {
     using value_t = std::pair<K, T>;
 
+    using move_t =
+        std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>;
+
     struct project_value
     {
-        const T& operator() (const value_t& v) const noexcept
+        const T& operator()(const value_t& v) const noexcept
         {
             return v.second;
         }
+        T&& operator()(value_t&& v) const noexcept
+        {
+            return std::move(v.second);
+        }
     };
 
     struct project_value_ptr
     {
-        const T* operator() (const value_t& v) const noexcept
+        const T* operator()(const value_t& v) const noexcept
         {
             return &v.second;
         }
@@ -84,15 +94,15 @@ class map
     struct combine_value
     {
         template <typename Kf, typename Tf>
-        value_t operator() (Kf&& k, Tf&& v) const
+        value_t operator()(Kf&& k, Tf&& v) const
         {
-            return { std::forward<Kf>(k), std::forward<Tf>(v) };
+            return {std::forward<Kf>(k), std::forward<Tf>(v)};
         }
     };
 
     struct default_value
     {
-        const T& operator() () const
+        const T& operator()() const
         {
             static T v{};
             return v;
@@ -101,58 +111,88 @@ class map
 
     struct error_value
     {
-        const T& operator() () const
+        const T& operator()() const
         {
-            throw std::out_of_range{"key not found"};
+            IMMER_THROW(std::out_of_range{"key not found"});
         }
     };
 
     struct hash_key
     {
-        auto operator() (const value_t& v)
-        { return Hash{}(v.first); }
+        auto operator()(const value_t& v) { return Hash{}(v.first); }
 
-        auto operator() (const K& v)
-        { return Hash{}(v); }
+        template <typename Key>
+        auto operator()(const Key& v)
+        {
+            return Hash{}(v);
+        }
     };
 
     struct equal_key
     {
-        auto operator() (const value_t& a, const value_t& b)
-        { return Equal{}(a.first, b.first); }
+        auto operator()(const value_t& a, const value_t& b)
+        {
+            return Equal{}(a.first, b.first);
+        }
 
-        auto operator() (const value_t& a, const K& b)
-        { return Equal{}(a.first, b); }
+        template <typename Key>
+        auto operator()(const value_t& a, const Key& b)
+        {
+            return Equal{}(a.first, b);
+        }
     };
 
     struct equal_value
     {
-        auto operator() (const value_t& a, const value_t& b)
-        { return Equal{}(a.first, b.first) && a.second == b.second; }
+        auto operator()(const value_t& a, const value_t& b)
+        {
+            return Equal{}(a.first, b.first) && a.second == b.second;
+        }
     };
 
-    using impl_t = detail::hamts::champ<
-        value_t, hash_key, equal_key, MemoryPolicy, B>;
+    using impl_t =
+        detail::hamts::champ<value_t, hash_key, equal_key, MemoryPolicy, B>;
 
 public:
-    using key_type = K;
-    using mapped_type = T;
-    using value_type = std::pair<K, T>;
-    using size_type = detail::hamts::size_t;
-    using diference_type = std::ptrdiff_t;
-    using hasher = Hash;
-    using key_equal = Equal;
-    using reference = const value_type&;
+    using key_type        = K;
+    using mapped_type     = T;
+    using value_type      = std::pair<K, T>;
+    using size_type       = detail::hamts::size_t;
+    using diference_type  = std::ptrdiff_t;
+    using hasher          = Hash;
+    using key_equal       = Equal;
+    using reference       = const value_type&;
     using const_reference = const value_type&;
 
-    using iterator         = detail::hamts::champ_iterator<
-        value_t, hash_key, equal_key, MemoryPolicy, B>;
-    using const_iterator   = iterator;
+    using iterator = detail::hamts::
+        champ_iterator<value_t, hash_key, equal_key, MemoryPolicy, B>;
+    using const_iterator = iterator;
+
+    using transient_type = map_transient<K, T, Hash, Equal, MemoryPolicy, B>;
+
+    using memory_policy_type = MemoryPolicy;
+
+    /*!
+     * Constructs a map containing the elements in `values`.
+     */
+    map(std::initializer_list<value_type> values)
+        : impl_{impl_t::from_initializer_list(values)}
+    {}
 
-    using transient_type   = map_transient<K, T, Hash, Equal, MemoryPolicy, B>;
+    /*!
+     * Constructs a map containing the elements in the range
+     * defined by the input iterator `first` and range sentinel `last`.
+     */
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
+    map(Iter first, Sent last)
+        : impl_{impl_t::from_range(first, last)}
+    {}
 
     /*!
-     * Default constructor.  It creates a set of `size() == 0`.  It
+     * Default constructor.  It creates a map of `size() == 0`.  It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
     map() = default;
@@ -162,28 +202,73 @@ class map
      * collection. It does not allocate memory and its complexity is
      * @f$ O(1) @f$.
      */
-    iterator begin() const { return {impl_}; }
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
 
     /*!
      * Returns an iterator pointing just after the last element of the
      * collection. It does not allocate and its complexity is @f$ O(1) @f$.
      */
-    iterator end() const { return {impl_, typename iterator::end_t{}}; }
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
 
     /*!
      * Returns the number of elements in the container.  It does
      * not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    size_type size() const { return impl_.size; }
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
 
     /*!
      * Returns `1` when the key `k` is contained in the map or `0`
      * otherwise. It won't allocate memory and its complexity is
      * *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
      */
-    size_type count(const K& k) const
-    { return impl_.template get<detail::constantly<size_type, 1>,
-                                detail::constantly<size_type, 0>>(k); }
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD size_type count(const Key& k) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(k);
+    }
+
+    /*!
+     * Returns `1` when the key `k` is contained in the map or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type count(const K& k) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If the key is not contained in the map, it returns a
+     * default constructed value.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD const T& operator[](const Key& k) const
+    {
+        return impl_.template get<project_value, default_value>(k);
+    }
 
     /*!
      * Returns a `const` reference to the values associated to the key
@@ -191,8 +276,10 @@ class map
      * default constructed value.  It does not allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
-    const T& operator[] (const K& k) const
-    { return impl_.template get<project_value, default_value>(k); }
+    IMMER_NODISCARD const T& operator[](const K& k) const
+    {
+        return impl_.template get<project_value, default_value>(k);
+    }
 
     /*!
      * Returns a `const` reference to the values associated to the key
@@ -200,9 +287,27 @@ class map
      * `std::out_of_range` error.  It does not allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
-    const T& at(const K& k) const
-    { return impl_.template get<project_value, error_value>(k); }
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    const T& at(const Key& k) const
+    {
+        return impl_.template get<project_value, error_value>(k);
+    }
 
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If the key is not contained in the map, throws an
+     * `std::out_of_range` error.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    const T& at(const K& k) const
+    {
+        return impl_.template get<project_value, error_value>(k);
+    }
 
     /*!
      * Returns a pointer to the value associated with the key `k`.  If
@@ -229,21 +334,45 @@ class map
      *   ``std::optional<const T&>`` but this construction is not valid
      *   in any current standard.  As a compromise we return a
      *   pointer, which has similar syntactic properties yet it is
-     *   unfortunatelly unnecessarily unrestricted.
+     *   unfortunately unnecessarily unrestricted.
      *
      * @endrst
      */
-    const T* find(const K& k) const
-    { return impl_.template get<project_value_ptr,
-                                detail::constantly<const T*, nullptr>>(k); }
+    IMMER_NODISCARD const T* find(const K& k) const
+    {
+        return impl_.template get<project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(k);
+    }
 
     /*!
-     * Returns whether the sets are equal.
+     * Returns a pointer to the value associated with the key `k`.  If
+     * the key is not contained in the map, a `nullptr` is returned.
+     * It does not allocate memory and its complexity is *effectively*
+     * @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
      */
-    bool operator==(const map& other) const
-    { return impl_.template equals<equal_value>(other.impl_); }
-    bool operator!=(const map& other) const
-    { return !(*this == other); }
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD const T* find(const Key& k) const
+    {
+        return impl_.template get<project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(k);
+    }
+
+    /*!
+     * Returns whether the maps are equal.
+     */
+    IMMER_NODISCARD bool operator==(const map& other) const
+    {
+        return impl_.template equals<equal_value>(other.impl_);
+    }
+    IMMER_NODISCARD bool operator!=(const map& other) const
+    {
+        return !(*this == other);
+    }
 
     /*!
      * Returns a map containing the association `value`.  If the key is
@@ -251,8 +380,14 @@ class map
      * It may allocate memory and its complexity is *effectively* @f$
      * O(1) @f$.
      */
-    map insert(value_type value) const
-    { return impl_.add(std::move(value)); }
+    IMMER_NODISCARD map insert(value_type value) const&
+    {
+        return impl_.add(std::move(value));
+    }
+    IMMER_NODISCARD decltype(auto) insert(value_type value) &&
+    {
+        return insert_move(move_t{}, std::move(value));
+    }
 
     /*!
      * Returns a map containing the association `(k, v)`.  If the key
@@ -260,8 +395,14 @@ class map
      * It may allocate memory and its complexity is *effectively* @f$
      * O(1) @f$.
      */
-    map set(key_type k, mapped_type v) const
-    { return impl_.add({std::move(k), std::move(v)}); }
+    IMMER_NODISCARD map set(key_type k, mapped_type v) const&
+    {
+        return impl_.add({std::move(k), std::move(v)});
+    }
+    IMMER_NODISCARD decltype(auto) set(key_type k, mapped_type v) &&
+    {
+        return set_move(move_t{}, std::move(k), std::move(v));
+    }
 
     /*!
      * Returns a map replacing the association `(k, v)` by the
@@ -271,29 +412,68 @@ class map
      * and its complexity is *effectively* @f$ O(1) @f$.
      */
     template <typename Fn>
-    map update(key_type k, Fn&& fn) const
+    IMMER_NODISCARD map update(key_type k, Fn&& fn) const&
     {
         return impl_
             .template update<project_value, default_value, combine_value>(
                 std::move(k), std::forward<Fn>(fn));
     }
+    template <typename Fn>
+    IMMER_NODISCARD decltype(auto) update(key_type k, Fn&& fn) &&
+    {
+        return update_move(move_t{}, std::move(k), std::forward<Fn>(fn));
+    }
+
+    /*!
+     * Returns a map replacing the association `(k, v)` by the association new
+     * association `(k, fn(v))`, where `v` is the currently associated value for
+     * `k` in the map.  It does nothing if `k` is not present in the map. It
+     * may allocate memory and its complexity is *effectively* @f$ O(1) @f$.
+     */
+    template <typename Fn>
+    IMMER_NODISCARD map update_if_exists(key_type k, Fn&& fn) const&
+    {
+        return impl_.template update_if_exists<project_value, combine_value>(
+            std::move(k), std::forward<Fn>(fn));
+    }
+    template <typename Fn>
+    IMMER_NODISCARD decltype(auto) update_if_exists(key_type k, Fn&& fn) &&
+    {
+        return update_if_exists_move(
+            move_t{}, std::move(k), std::forward<Fn>(fn));
+    }
 
     /*!
      * Returns a map without the key `k`.  If the key is not
      * associated in the map it returns the same map.  It may allocate
      * memory and its complexity is *effectively* @f$ O(1) @f$.
      */
-    map erase(const K& k) const
-    { return impl_.sub(k); }
+    IMMER_NODISCARD map erase(const K& k) const& { return impl_.sub(k); }
+    IMMER_NODISCARD decltype(auto) erase(const K& k) &&
+    {
+        return erase_move(move_t{}, k);
+    }
 
     /*!
-     * Returns an @a transient form of this container, a
+     * Returns a @a transient form of this container, an
      * `immer::map_transient`.
      */
-    transient_type transient() const&
-    { return transient_type{ impl_ }; }
-    transient_type transient() &&
-    { return transient_type{ std::move(impl_) }; }
+    IMMER_NODISCARD transient_type transient() const&
+    {
+        return transient_type{impl_};
+    }
+    IMMER_NODISCARD transient_type transient() &&
+    {
+        return transient_type{std::move(impl_)};
+    }
+
+    /*!
+     * Returns a value that can be used as identity for the container.  If two
+     * values have the same identity, they are guaranteed to be equal and to
+     * contain the same objects.  However, two equal containers are not
+     * guaranteed to have the same identity.
+     */
+    void* identity() const { return impl_.root; }
 
     // Semi-private
     const impl_t& impl() const { return impl_; }
@@ -301,6 +481,65 @@ class map
 private:
     friend transient_type;
 
+    map&& insert_move(std::true_type, value_type value)
+    {
+        impl_.add_mut({}, std::move(value));
+        return std::move(*this);
+    }
+    map insert_move(std::false_type, value_type value)
+    {
+        return impl_.add(std::move(value));
+    }
+
+    map&& set_move(std::true_type, key_type k, mapped_type m)
+    {
+        impl_.add_mut({}, {std::move(k), std::move(m)});
+        return std::move(*this);
+    }
+    map set_move(std::false_type, key_type k, mapped_type m)
+    {
+        return impl_.add({std::move(k), std::move(m)});
+    }
+
+    template <typename Fn>
+    map&& update_move(std::true_type, key_type k, Fn&& fn)
+    {
+        impl_.template update_mut<project_value, default_value, combine_value>(
+            {}, std::move(k), std::forward<Fn>(fn));
+        return std::move(*this);
+    }
+    template <typename Fn>
+    map update_move(std::false_type, key_type k, Fn&& fn)
+    {
+        return impl_
+            .template update<project_value, default_value, combine_value>(
+                std::move(k), std::forward<Fn>(fn));
+    }
+
+    template <typename Fn>
+    map&& update_if_exists_move(std::true_type, key_type k, Fn&& fn)
+    {
+        impl_.template update_if_exists_mut<project_value, combine_value>(
+            {}, std::move(k), std::forward<Fn>(fn));
+        return std::move(*this);
+    }
+    template <typename Fn>
+    map update_if_exists_move(std::false_type, key_type k, Fn&& fn)
+    {
+        return impl_.template update_if_exists<project_value, combine_value>(
+            std::move(k), std::forward<Fn>(fn));
+    }
+
+    map&& erase_move(std::true_type, const key_type& value)
+    {
+        impl_.sub_mut({}, value);
+        return std::move(*this);
+    }
+    map erase_move(std::false_type, const key_type& value)
+    {
+        return impl_.sub(value);
+    }
+
     map(impl_t impl)
         : impl_(std::move(impl))
     {}
diff --git a/src/immer/map_transient.hpp b/src/immer/map_transient.hpp
index 542dac1d5d..935f48e2b0 100644
--- a/src/immer/map_transient.hpp
+++ b/src/immer/map_transient.hpp
@@ -8,22 +8,311 @@
 
 #pragma once
 
-#include <immer/memory_policy.hpp>
 #include <immer/detail/hamts/champ.hpp>
+#include <immer/memory_policy.hpp>
 
 #include <functional>
 
 namespace immer {
 
+template <typename K,
+          typename T,
+          typename Hash,
+          typename Equal,
+          typename MemoryPolicy,
+          detail::hamts::bits_t B>
+class map;
+
 /*!
- * **WORK IN PROGRESS**
+ * Mutable version of `immer::map`.
+ *
+ * @rst
+ *
+ * Refer to :doc:`transients` to learn more about when and how to use
+ * the mutable versions of immutable containers.
+ *
+ * @endrst
  */
 template <typename K,
           typename T,
-          typename Hash          = std::hash<K>,
-          typename Equal         = std::equal_to<K>,
-          typename MemoryPolicy  = default_memory_policy,
+          typename Hash           = std::hash<K>,
+          typename Equal          = std::equal_to<K>,
+          typename MemoryPolicy   = default_memory_policy,
           detail::hamts::bits_t B = default_bits>
-class map_transient;
+class map_transient : MemoryPolicy::transience_t::owner
+{
+    using base_t  = typename MemoryPolicy::transience_t::owner;
+    using owner_t = base_t;
+
+public:
+    using persistent_type = map<K, T, Hash, Equal, MemoryPolicy, B>;
+
+    using key_type        = K;
+    using mapped_type     = T;
+    using value_type      = std::pair<K, T>;
+    using size_type       = detail::hamts::size_t;
+    using diference_type  = std::ptrdiff_t;
+    using hasher          = Hash;
+    using key_equal       = Equal;
+    using reference       = const value_type&;
+    using const_reference = const value_type&;
+
+    using iterator       = typename persistent_type::iterator;
+    using const_iterator = iterator;
+
+    /*!
+     * Default constructor.  It creates a map of `size() == 0`.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    map_transient() = default;
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Returns `1` when the key `k` is contained in the map or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD size_type count(const Key& k) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(k);
+    }
+
+    /*!
+     * Returns `1` when the key `k` is contained in the map or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type count(const K& k) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If the key is not contained in the map, it returns a
+     * default constructed value.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD const T& operator[](const Key& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value,
+                                  typename persistent_type::default_value>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If the key is not contained in the map, it returns a
+     * default constructed value.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD const T& operator[](const K& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value,
+                                  typename persistent_type::default_value>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If the key is not contained in the map, throws an
+     * `std::out_of_range` error.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    const T& at(const Key& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value,
+                                  typename persistent_type::error_value>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If the key is not contained in the map, throws an
+     * `std::out_of_range` error.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    const T& at(const K& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value,
+                                  typename persistent_type::error_value>(k);
+    }
+
+    /*!
+     * Returns a pointer to the value associated with the key `k`.  If
+     * the key is not contained in the map, a `nullptr` is returned.
+     * It does not allocate memory and its complexity is *effectively*
+     * @f$ O(1) @f$.
+     *
+     * @rst
+     *
+     * .. admonition:: Why doesn't this function return an iterator?
+     *
+     *   Associative containers from the C++ standard library provide a
+     *   ``find`` method that returns an iterator pointing to the
+     *   element in the container or ``end()`` when the key is missing.
+     *   In the case of an unordered container, the only meaningful
+     *   thing one may do with it is to compare it with the end, to
+     *   test if the find was succesfull, and dereference it.  This
+     *   comparison is cumbersome compared to testing for a non-empty
+     *   optional value.  Furthermore, for an immutable container,
+     *   returning an iterator would have some additional performance
+     *   cost, with no benefits otherwise.
+     *
+     *   In our opinion, this function should return a
+     *   ``std::optional<const T&>`` but this construction is not valid
+     *   in any current standard.  As a compromise we return a
+     *   pointer, which has similar syntactic properties yet it is
+     *   unfortunately unnecessarily unrestricted.
+     *
+     * @endrst
+     */
+    IMMER_NODISCARD const T* find(const K& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(k);
+    }
+
+    /*!
+     * Returns a pointer to the value associated with the key `k`.  If
+     * the key is not contained in the map, a `nullptr` is returned.
+     * It does not allocate memory and its complexity is *effectively*
+     * @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD const T* find(const Key& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(k);
+    }
+
+    /*!
+     * Inserts the association `value`.  If the key is already in the map, it
+     * replaces its association in the map.  It may allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    void insert(value_type value) { impl_.add_mut(*this, std::move(value)); }
+
+    /*!
+     * Inserts the association `(k, v)`.  If the key is already in the map, it
+     * replaces its association in the map.  It may allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    void set(key_type k, mapped_type v)
+    {
+        impl_.add_mut(*this, {std::move(k), std::move(v)});
+    }
+
+    /*!
+     * Replaces the association `(k, v)` by the association new association `(k,
+     * fn(v))`, where `v` is the currently associated value for `k` in the map
+     * or a default constructed value otherwise. It may allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    template <typename Fn>
+    void update(key_type k, Fn&& fn)
+    {
+        impl_.template update_mut<typename persistent_type::project_value,
+                                  typename persistent_type::default_value,
+                                  typename persistent_type::combine_value>(
+            *this, std::move(k), std::forward<Fn>(fn));
+    }
+
+    /*!
+     * Replaces the association `(k, v)` by the association new association `(k,
+     * fn(v))`, where `v` is the currently associated value for `k` in the map
+     * or does nothing if `k` is not present in the map. It may allocate memory
+     * and its complexity is *effectively* @f$ O(1) @f$.
+     */
+    template <typename Fn>
+    void update_if_exists(key_type k, Fn&& fn)
+    {
+        impl_.template update_if_exists_mut<
+            typename persistent_type::project_value,
+            typename persistent_type::combine_value>(
+            *this, std::move(k), std::forward<Fn>(fn));
+    }
+
+    /*!
+     * Removes the key `k` from the k.  Does nothing if the key is not
+     * associated in the map.  It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    void erase(const K& k) { impl_.sub_mut(*this, k); }
+
+    /*!
+     * Returns an @a immutable form of this container, an
+     * `immer::map`.
+     */
+    IMMER_NODISCARD persistent_type persistent() &
+    {
+        this->owner_t::operator=(owner_t{});
+        return impl_;
+    }
+    IMMER_NODISCARD persistent_type persistent() && { return std::move(impl_); }
+
+private:
+    friend persistent_type;
+    using impl_t = typename persistent_type::impl_t;
+
+    map_transient(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+
+    impl_t impl_ = impl_t::empty();
+
+public:
+    // Semi-private
+    const impl_t& impl() const { return impl_; }
+};
 
 } // namespace immer
diff --git a/src/immer/memory_policy.hpp b/src/immer/memory_policy.hpp
index 9d95f56a0e..d8556b9432 100644
--- a/src/immer/memory_policy.hpp
+++ b/src/immer/memory_policy.hpp
@@ -10,11 +10,13 @@
 
 #include <immer/heap/cpp_heap.hpp>
 #include <immer/heap/heap_policy.hpp>
+#include <immer/lock/no_lock_policy.hpp>
+#include <immer/lock/spinlock_policy.hpp>
+#include <immer/refcount/no_refcount_policy.hpp>
 #include <immer/refcount/refcount_policy.hpp>
 #include <immer/refcount/unsafe_refcount_policy.hpp>
-#include <immer/refcount/no_refcount_policy.hpp>
-#include <immer/transience/no_transience_policy.hpp>
 #include <immer/transience/gc_transience_policy.hpp>
+#include <immer/transience/no_transience_policy.hpp>
 #include <type_traits>
 
 namespace immer {
@@ -25,8 +27,7 @@ namespace immer {
  */
 template <typename RefcountPolicy>
 struct get_transience_policy
-    : std::conditional<std::is_same<RefcountPolicy,
-                                    no_refcount_policy>::value,
+    : std::conditional<std::is_same<RefcountPolicy, no_refcount_policy>::value,
                        gc_transience_policy,
                        no_transience_policy>
 {};
@@ -40,11 +41,9 @@ using get_transience_policy_t = typename get_transience_policy<T>::type;
  */
 template <typename HeapPolicy>
 struct get_prefer_fewer_bigger_objects
-    : std::integral_constant<bool,
-                             std::is_same<
-                                 HeapPolicy,
-                                 heap_policy<cpp_heap>
-                                 >::value>
+    : std::integral_constant<
+          bool,
+          std::is_same<HeapPolicy, heap_policy<cpp_heap>>::value>
 {};
 
 template <typename T>
@@ -57,14 +56,14 @@ constexpr auto get_prefer_fewer_bigger_objects_v =
  */
 template <typename RefcountPolicy>
 struct get_use_transient_rvalues
-    : std::integral_constant<bool,
-                             !std::is_same<
-                                 RefcountPolicy,
-                                 no_refcount_policy>::value>
+    : std::integral_constant<
+          bool,
+          !std::is_same<RefcountPolicy, no_refcount_policy>::value>
 {};
 
 template <typename T>
-constexpr auto get_use_transient_rvalues_v = get_use_transient_rvalues<T>::value;
+constexpr auto get_use_transient_rvalues_v =
+    get_use_transient_rvalues<T>::value;
 
 /*!
  * This is a default implementation of a *memory policy*.  A memory
@@ -77,7 +76,7 @@ constexpr auto get_use_transient_rvalues_v = get_use_transient_rvalues<T>::value
  * @tparam TransiencePolicy A *transience policy*, for example,
  *         @ref no_transience_policy.
  * @tparam PreferFewerBiggerObjects Boolean flag indicating whether
- *         the user should prefer to allocate memory in bigger chungs
+ *         the user should prefer to allocate memory in bigger chunks
  *         --e.g. by putting various objects in the same memory
  *         region-- or not.
  * @tparam UseTransientRValues Boolean flag indicating whether
@@ -86,20 +85,23 @@ constexpr auto get_use_transient_rvalues_v = get_use_transient_rvalues<T>::value
  */
 template <typename HeapPolicy,
           typename RefcountPolicy,
-          typename TransiencePolicy     = get_transience_policy_t<RefcountPolicy>,
-          bool PreferFewerBiggerObjects = get_prefer_fewer_bigger_objects_v<HeapPolicy>,
-          bool UseTransientRValues      = get_use_transient_rvalues_v<RefcountPolicy>>
+          typename LockPolicy,
+          typename TransiencePolicy = get_transience_policy_t<RefcountPolicy>,
+          bool PreferFewerBiggerObjects =
+              get_prefer_fewer_bigger_objects_v<HeapPolicy>,
+          bool UseTransientRValues =
+              get_use_transient_rvalues_v<RefcountPolicy>>
 struct memory_policy
 {
     using heap       = HeapPolicy;
     using refcount   = RefcountPolicy;
     using transience = TransiencePolicy;
+    using lock       = LockPolicy;
 
     static constexpr bool prefer_fewer_bigger_objects =
         PreferFewerBiggerObjects;
 
-    static constexpr bool use_transient_rvalues =
-        UseTransientRValues;
+    static constexpr bool use_transient_rvalues = UseTransientRValues;
 
     using transience_t = typename transience::template apply<heap>::type;
 };
@@ -113,7 +115,7 @@ struct memory_policy
 using default_heap_policy = heap_policy<debug_size_heap<cpp_heap>>;
 #else
 #if IMMER_NO_THREAD_SAFETY
-using default_heap_policy = unsafe_free_list_heap_policy<cpp_heap>;
+using default_heap_policy     = unsafe_free_list_heap_policy<cpp_heap>;
 #else
 using default_heap_policy = free_list_heap_policy<cpp_heap>;
 #endif
@@ -124,15 +126,17 @@ using default_heap_policy = free_list_heap_policy<cpp_heap>;
  */
 #if IMMER_NO_THREAD_SAFETY
 using default_refcount_policy = unsafe_refcount_policy;
+using default_lock_policy     = no_lock_policy;
 #else
 using default_refcount_policy = refcount_policy;
+using default_lock_policy     = spinlock_policy;
 #endif
 
 /*!
  * The default memory policy.
  */
-using default_memory_policy = memory_policy<
-    default_heap_policy,
-    default_refcount_policy>;
+using default_memory_policy = memory_policy<default_heap_policy,
+                                            default_refcount_policy,
+                                            default_lock_policy>;
 
 } // namespace immer
diff --git a/src/immer/refcount/no_refcount_policy.hpp b/src/immer/refcount/no_refcount_policy.hpp
index 95663368f1..5706858b12 100644
--- a/src/immer/refcount/no_refcount_policy.hpp
+++ b/src/immer/refcount/no_refcount_policy.hpp
@@ -10,19 +10,8 @@
 
 namespace immer {
 
-struct disowned {};
-
-struct no_spinlock
-{
-    bool try_lock() { return true; }
-    void lock() {}
-    void unlock() {}
-
-    struct scoped_lock
-    {
-        scoped_lock(no_spinlock&) {}
-    };
-};
+struct disowned
+{};
 
 /*!
  * Disables reference counting, to be used with an alternative garbage
@@ -30,14 +19,11 @@ struct no_spinlock
  */
 struct no_refcount_policy
 {
-    using spinlock_type = no_spinlock;
-
-    no_refcount_policy() {};
+    no_refcount_policy(){};
     no_refcount_policy(disowned) {}
 
     void inc() {}
     bool dec() { return false; }
-    void dec_unsafe() {}
     bool unique() { return false; }
 };
 
diff --git a/src/immer/refcount/refcount_policy.hpp b/src/immer/refcount/refcount_policy.hpp
index 68ba209c8c..52b516e859 100644
--- a/src/immer/refcount/refcount_policy.hpp
+++ b/src/immer/refcount/refcount_policy.hpp
@@ -11,101 +11,30 @@
 #include <immer/refcount/no_refcount_policy.hpp>
 
 #include <atomic>
-#include <utility>
 #include <cassert>
-#include <thread>
-
-// This has been shamelessly copied from boost...
-#if defined(_MSC_VER) && _MSC_VER >= 1310 && (defined(_M_IX86) || defined(_M_X64)) && !defined(__c2__)
-extern "C" void _mm_pause();
-#  define IMMER_SMT_PAUSE _mm_pause()
-#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-#  define IMMER_SMT_PAUSE __asm__ __volatile__( "rep; nop" : : : "memory" )
-#endif
+#include <utility>
 
 namespace immer {
 
-// This is an atomic spinlock similar to the one used by boost to provide
-// "atomic" shared_ptr operations.  It also does not differ much from the one
-// from libc++ or libstdc++...
-struct spinlock
-{
-    std::atomic_flag v_{};
-
-    bool try_lock()
-    {
-        return !v_.test_and_set(std::memory_order_acquire);
-    }
-
-    void lock()
-    {
-        for (auto k = 0u; !try_lock(); ++k) {
-            if (k < 4)
-                continue;
-#ifdef IMMER_SMT_PAUSE
-            else if (k < 16)
-                IMMER_SMT_PAUSE;
-#endif
-            else
-                std::this_thread::yield();
-        }
-    }
-
-    void unlock()
-    {
-        v_.clear(std::memory_order_release);
-    }
-
-    struct scoped_lock
-    {
-        scoped_lock(const scoped_lock&) = delete;
-        scoped_lock& operator=(const scoped_lock& ) = delete;
-
-        explicit scoped_lock(spinlock& sp)
-            : sp_{sp}
-        { sp.lock(); }
-
-        ~scoped_lock()
-        { sp_.unlock();}
-
-    private:
-        spinlock& sp_;
-    };
-};
-
 /*!
  * A reference counting policy implemented using an *atomic* `int`
  * count.  It is **thread-safe**.
  */
 struct refcount_policy
 {
-    using spinlock_type = spinlock;
-
     mutable std::atomic<int> refcount;
 
-    refcount_policy() : refcount{1} {};
-    refcount_policy(disowned) : refcount{0} {}
-
-    void inc()
-    {
-        refcount.fetch_add(1, std::memory_order_relaxed);
-    }
+    refcount_policy()
+        : refcount{1} {};
+    refcount_policy(disowned)
+        : refcount{0}
+    {}
 
-    bool dec()
-    {
-        return 1 == refcount.fetch_sub(1, std::memory_order_acq_rel);
-    }
+    void inc() { refcount.fetch_add(1, std::memory_order_relaxed); }
 
-    void dec_unsafe()
-    {
-        assert(refcount.load() > 1);
-        refcount.fetch_sub(1, std::memory_order_relaxed);
-    }
+    bool dec() { return 1 == refcount.fetch_sub(1, std::memory_order_acq_rel); }
 
-    bool unique()
-    {
-        return refcount == 1;
-    }
+    bool unique() { return refcount == 1; }
 };
 
 } // namespace immer
diff --git a/src/immer/refcount/unsafe_refcount_policy.hpp b/src/immer/refcount/unsafe_refcount_policy.hpp
index 2a170b7573..df65019b42 100644
--- a/src/immer/refcount/unsafe_refcount_policy.hpp
+++ b/src/immer/refcount/unsafe_refcount_policy.hpp
@@ -21,16 +21,16 @@ namespace immer {
  */
 struct unsafe_refcount_policy
 {
-    using spinlock_type = no_spinlock;
-
     mutable int refcount;
 
-    unsafe_refcount_policy() : refcount{1} {};
-    unsafe_refcount_policy(disowned) : refcount{0} {}
+    unsafe_refcount_policy()
+        : refcount{1} {};
+    unsafe_refcount_policy(disowned)
+        : refcount{0}
+    {}
 
     void inc() { ++refcount; }
     bool dec() { return --refcount == 0; }
-    void dec_unsafe() { --refcount; }
     bool unique() { return refcount == 1; }
 };
 
diff --git a/src/immer/set.hpp b/src/immer/set.hpp
index 5d9eed8ca6..df4192f510 100644
--- a/src/immer/set.hpp
+++ b/src/immer/set.hpp
@@ -8,9 +8,9 @@
 
 #pragma once
 
-#include <immer/memory_policy.hpp>
 #include <immer/detail/hamts/champ.hpp>
 #include <immer/detail/hamts/champ_iterator.hpp>
+#include <immer/memory_policy.hpp>
 
 #include <functional>
 
@@ -36,7 +36,7 @@ class set_transient;
  *
  * @rst
  *
- * This cotainer provides a good trade-off between cache locality,
+ * This container provides a good trade-off between cache locality,
  * membership checks, update performance and structural sharing.  It
  * does so by storing the data in contiguous chunks of :math:`2^{B}`
  * elements.  When storing big objects, the size of these contiguous
@@ -62,21 +62,30 @@ class set
 {
     using impl_t = detail::hamts::champ<T, Hash, Equal, MemoryPolicy, B>;
 
+    using move_t =
+        std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>;
+
+    struct project_value_ptr
+    {
+        const T* operator()(const T& v) const noexcept { return &v; }
+    };
+
 public:
-    using key_type = T;
-    using value_type = T;
-    using size_type = detail::hamts::size_t;
-    using diference_type = std::ptrdiff_t;
-    using hasher = Hash;
-    using key_equal = Equal;
-    using reference = const T&;
+    using value_type      = T;
+    using size_type       = detail::hamts::size_t;
+    using diference_type  = std::ptrdiff_t;
+    using hasher          = Hash;
+    using key_equal       = Equal;
+    using reference       = const T&;
     using const_reference = const T&;
 
-    using iterator         = detail::hamts::champ_iterator<T, Hash, Equal,
-                                                         MemoryPolicy, B>;
-    using const_iterator   = iterator;
+    using iterator =
+        detail::hamts::champ_iterator<T, Hash, Equal, MemoryPolicy, B>;
+    using const_iterator = iterator;
+
+    using transient_type = set_transient<T, Hash, Equal, MemoryPolicy, B>;
 
-    using transient_type   = set_transient<T, Hash, Equal, MemoryPolicy, B>;
+    using memory_policy_type = MemoryPolicy;
 
     /*!
      * Default constructor.  It creates a set of `size() == 0`.  It
@@ -84,66 +93,171 @@ class set
      */
     set() = default;
 
+    /*!
+     * Constructs a set containing the elements in `values`.
+     */
+    set(std::initializer_list<value_type> values)
+        : impl_{impl_t::from_initializer_list(values)}
+    {}
+
+    /*!
+     * Constructs a set containing the elements in the range
+     * defined by the input iterator `first` and range sentinel `last`.
+     */
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
+    set(Iter first, Sent last)
+        : impl_{impl_t::from_range(first, last)}
+    {}
+
     /*!
      * Returns an iterator pointing at the first element of the
      * collection. It does not allocate memory and its complexity is
      * @f$ O(1) @f$.
      */
-    iterator begin() const { return {impl_}; }
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
 
     /*!
      * Returns an iterator pointing just after the last element of the
      * collection. It does not allocate and its complexity is @f$ O(1) @f$.
      */
-    iterator end() const { return {impl_, typename iterator::end_t{}}; }
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
 
     /*!
      * Returns the number of elements in the container.  It does
      * not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    size_type size() const { return impl_.size; }
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Returns `1` when `value` is contained in the set or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename K,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD size_type count(const K& value) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(value);
+    }
 
     /*!
      * Returns `1` when `value` is contained in the set or `0`
      * otherwise. It won't allocate memory and its complexity is
      * *effectively* @f$ O(1) @f$.
      */
-    size_type count(const T& value) const
-    { return impl_.template get<detail::constantly<size_type, 1>,
-                                detail::constantly<size_type, 0>>(value); }
+    IMMER_NODISCARD size_type count(const T& value) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(value);
+    }
+
+    /*!
+     * Returns a pointer to the value if `value` is contained in the
+     * set, or nullptr otherwise.
+     * It does not allocate memory and its complexity is *effectively*
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD const T* find(const T& value) const
+    {
+        return impl_.template get<project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(value);
+    }
+
+    /*!
+     * Returns a pointer to the value if `value` is contained in the
+     * set, or nullptr otherwise.
+     * It does not allocate memory and its complexity is *effectively*
+     * @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename K,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD const T* find(const K& value) const
+    {
+        return impl_.template get<project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(value);
+    }
 
     /*!
      * Returns whether the sets are equal.
      */
-    bool operator==(const set& other) const
-    { return impl_.equals(other.impl_); }
-    bool operator!=(const set& other) const
-    { return !(*this == other); }
+    IMMER_NODISCARD bool operator==(const set& other) const
+    {
+        return impl_.equals(other.impl_);
+    }
+    IMMER_NODISCARD bool operator!=(const set& other) const
+    {
+        return !(*this == other);
+    }
 
     /*!
      * Returns a set containing `value`.  If the `value` is already in
      * the set, it returns the same set.  It may allocate memory and
      * its complexity is *effectively* @f$ O(1) @f$.
      */
-    set insert(T value) const
-    { return impl_.add(std::move(value)); }
+    IMMER_NODISCARD set insert(T value) const&
+    {
+        return impl_.add(std::move(value));
+    }
+    IMMER_NODISCARD decltype(auto) insert(T value) &&
+    {
+        return insert_move(move_t{}, std::move(value));
+    }
 
     /*!
      * Returns a set without `value`.  If the `value` is not in the
      * set it returns the same set.  It may allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
-    set erase(const T& value) const
-    { return impl_.sub(value); }
+    IMMER_NODISCARD set erase(const T& value) const&
+    {
+        return impl_.sub(value);
+    }
+    IMMER_NODISCARD decltype(auto) erase(const T& value) &&
+    {
+        return erase_move(move_t{}, value);
+    }
 
     /*!
      * Returns an @a transient form of this container, a
      * `immer::set_transient`.
      */
-    transient_type transient() const&
-    { return transient_type{ impl_ }; }
-    transient_type transient() &&
-    { return transient_type{ std::move(impl_) }; }
+    IMMER_NODISCARD transient_type transient() const&
+    {
+        return transient_type{impl_};
+    }
+    IMMER_NODISCARD transient_type transient() &&
+    {
+        return transient_type{std::move(impl_)};
+    }
+
+    /*!
+     * Returns a value that can be used as identity for the container.  If two
+     * values have the same identity, they are guaranteed to be equal and to
+     * contain the same objects.  However, two equal containers are not
+     * guaranteed to have the same identity.
+     */
+    void* identity() const { return impl_.root; }
 
     // Semi-private
     const impl_t& impl() const { return impl_; }
@@ -151,6 +265,26 @@ class set
 private:
     friend transient_type;
 
+    set&& insert_move(std::true_type, value_type value)
+    {
+        impl_.add_mut({}, std::move(value));
+        return std::move(*this);
+    }
+    set insert_move(std::false_type, value_type value)
+    {
+        return impl_.add(std::move(value));
+    }
+
+    set&& erase_move(std::true_type, const value_type& value)
+    {
+        impl_.sub_mut({}, value);
+        return std::move(*this);
+    }
+    set erase_move(std::false_type, const value_type& value)
+    {
+        return impl_.sub(value);
+    }
+
     set(impl_t impl)
         : impl_(std::move(impl))
     {}
diff --git a/src/immer/set_transient.hpp b/src/immer/set_transient.hpp
index cba41601c6..e07668e292 100644
--- a/src/immer/set_transient.hpp
+++ b/src/immer/set_transient.hpp
@@ -8,21 +8,177 @@
 
 #pragma once
 
-#include <immer/memory_policy.hpp>
 #include <immer/detail/hamts/champ.hpp>
+#include <immer/memory_policy.hpp>
 
 #include <functional>
 
 namespace immer {
 
 /*!
- * **WORK IN PROGRESS**
+ * Mutable version of `immer::set`.
+ *
+ * @rst
+ *
+ * Refer to :doc:`transients` to learn more about when and how to use
+ * the mutable versions of immutable containers.
+ *
+ * @endrst
  */
 template <typename T,
-          typename Hash          = std::hash<T>,
-          typename Equal         = std::equal_to<T>,
-          typename MemoryPolicy  = default_memory_policy,
+          typename Hash           = std::hash<T>,
+          typename Equal          = std::equal_to<T>,
+          typename MemoryPolicy   = default_memory_policy,
           detail::hamts::bits_t B = default_bits>
-class set_transient;
+class set_transient : MemoryPolicy::transience_t::owner
+{
+    using base_t  = typename MemoryPolicy::transience_t::owner;
+    using owner_t = base_t;
+
+public:
+    using persistent_type = set<T, Hash, Equal, MemoryPolicy, B>;
+
+    using value_type      = T;
+    using size_type       = detail::hamts::size_t;
+    using diference_type  = std::ptrdiff_t;
+    using hasher          = Hash;
+    using key_equal       = Equal;
+    using reference       = const T&;
+    using const_reference = const T&;
+
+    using iterator       = typename persistent_type::iterator;
+    using const_iterator = iterator;
+
+    /*!
+     * Default constructor.  It creates a set of `size() == 0`.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    set_transient() = default;
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Returns `1` when `value` is contained in the set or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename K,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD size_type count(const K& value) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(value);
+    }
+
+    /*!
+     * Returns `1` when `value` is contained in the set or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type count(const T& value) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(value);
+    }
+
+    /*!
+     * Returns a pointer to the value if `value` is contained in the
+     * set, or nullptr otherwise.
+     * It does not allocate memory and its complexity is *effectively*
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD const T* find(const T& value) const
+    {
+        return impl_.template get<typename persistent_type::project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(value);
+    }
+
+    /*!
+     * Returns a pointer to the value if `value` is contained in the
+     * set, or nullptr otherwise.
+     * It does not allocate memory and its complexity is *effectively*
+     * @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename K,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD const T* find(const K& value) const
+    {
+        return impl_.template get<typename persistent_type::project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(value);
+    }
+
+    /*!
+     * Inserts `value` into the set, and does nothing if the value is already
+     * there  It may allocate memory and its complexity is *effectively* @f$
+     * O(1) @f$.
+     */
+    void insert(T value) { impl_.add_mut(*this, std::move(value)); }
+
+    /*!
+     * Removes the `value` from the set, doing nothing if the value is not in
+     * the set.  It may allocate memory and its complexity is *effectively* @f$
+     * O(1) @f$.
+     */
+    void erase(const T& value) { impl_.sub_mut(*this, value); }
+
+    /*!
+     * Returns an @a immutable form of this container, an
+     * `immer::set`.
+     */
+    IMMER_NODISCARD persistent_type persistent() &
+    {
+        this->owner_t::operator=(owner_t{});
+        return impl_;
+    }
+    IMMER_NODISCARD persistent_type persistent() && { return std::move(impl_); }
+
+private:
+    friend persistent_type;
+    using impl_t = typename persistent_type::impl_t;
+
+    set_transient(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+
+    impl_t impl_ = impl_t::empty();
+
+public:
+    // Semi-private
+    const impl_t& impl() const { return impl_; }
+};
 
 } // namespace immer
diff --git a/src/immer/table.hpp b/src/immer/table.hpp
new file mode 100644
index 0000000000..0e977c6a23
--- /dev/null
+++ b/src/immer/table.hpp
@@ -0,0 +1,547 @@
+#pragma once
+
+#include <immer/config.hpp>
+#include <immer/detail/hamts/champ.hpp>
+#include <immer/detail/hamts/champ_iterator.hpp>
+#include <immer/memory_policy.hpp>
+#include <type_traits>
+
+namespace immer {
+
+template <typename T,
+          typename KeyFn,
+          typename Hash,
+          typename Equal,
+          typename MemoryPolicy,
+          detail::hamts::bits_t B>
+class table_transient;
+
+/*!
+ * Function template to get the key in `immer::table_key_fn`.
+ * It assumes the key is `id` class member.
+ */
+template <typename T>
+auto get_table_key(T const& x) -> decltype(x.id)
+{
+    return x.id;
+}
+
+/*!
+ * Function template to set the key in `immer::table_key_fn`.
+ * It assumes the key is `id` class member.
+ */
+template <typename T, typename K>
+auto set_table_key(T x, K&& k) -> T
+{
+    x.id = std::forward<K>(k);
+    return x;
+}
+
+/*!
+ * Default value for `KeyFn` in `immer::table`.
+ * It assumes the key is `id` class member.
+ */
+struct table_key_fn
+{
+    template <typename T>
+    decltype(auto) operator()(T&& x) const
+    {
+        return get_table_key(std::forward<T>(x));
+    }
+
+    template <typename T, typename K>
+    auto operator()(T&& x, K&& k) const
+    {
+        return set_table_key(std::forward<T>(x), std::forward<K>(k));
+    }
+};
+
+template <typename KeyFn, typename T>
+using table_key_t = std::decay_t<decltype(KeyFn{}(std::declval<T>()))>;
+
+/*!
+ * Immutable unordered set of values of type `T`. Values are indexed via
+ * `operator()(const T&)` from `KeyFn` template parameter.
+ * By default, key is `&T::id`.
+ *
+ * @tparam T The type of the values to be stored in the container.
+ * @tparam KeyFn Type which implements `operator()(const T&)`
+ * @tparam Hash  The type of a function object capable of hashing
+ *               values of type `T`.
+ * @tparam Equal The type of a function object capable of comparing
+ *               values of type `T`.
+ * @tparam MemoryPolicy Memory management policy. See @ref
+ *              memory_policy.
+ *
+ * @rst
+ *
+ * This container is based on the `immer::map` underlying data structure.
+ *
+ * This container provides a good trade-off between cache locality,
+ * search, update performance and structural sharing.  It does so by
+ * storing the data in contiguous chunks of :math:`2^{B}` elements.
+ * When storing big objects, the size of these contiguous chunks can
+ * become too big, damaging performance.  If this is measured to be
+ * problematic for a specific use-case, it can be solved by using a
+ * `immer::box` to wrap the type `T`.
+ *
+ * **Example**
+ *   .. literalinclude:: ../example/table/intro.cpp
+ *      :language: c++
+ *      :start-after: intro/start
+ *      :end-before:  intro/end
+ *
+ * @endrst
+ *
+ */
+template <typename T,
+          typename KeyFn          = table_key_fn,
+          typename Hash           = std::hash<table_key_t<KeyFn, T>>,
+          typename Equal          = std::equal_to<table_key_t<KeyFn, T>>,
+          typename MemoryPolicy   = default_memory_policy,
+          detail::hamts::bits_t B = default_bits>
+class table
+{
+    using K       = table_key_t<KeyFn, T>;
+    using value_t = T;
+
+    using move_t =
+        std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>;
+
+    struct project_value
+    {
+        const T& operator()(const value_t& v) const noexcept { return v; }
+        T&& operator()(value_t&& v) const noexcept { return std::move(v); }
+    };
+
+    struct project_value_ptr
+    {
+        const T* operator()(const value_t& v) const noexcept
+        {
+            return std::addressof(v);
+        }
+    };
+
+    struct combine_value
+    {
+        template <typename Kf, typename Tf>
+        auto operator()(Kf&& k, Tf&& v) const
+        {
+            return KeyFn{}(std::forward<Tf>(v), std::forward<Kf>(k));
+        }
+    };
+
+    struct default_value
+    {
+        const T& operator()() const
+        {
+            static T v{};
+            return v;
+        }
+    };
+
+    struct error_value
+    {
+        const T& operator()() const
+        {
+            IMMER_THROW(std::out_of_range{"key not found"});
+        }
+    };
+
+    struct hash_key
+    {
+        std::size_t operator()(const value_t& v) const
+        {
+            return Hash{}(KeyFn{}(v));
+        }
+
+        template <typename Key>
+        std::size_t operator()(const Key& v) const
+        {
+            return Hash{}(v);
+        }
+    };
+
+    struct equal_key
+    {
+        bool operator()(const value_t& a, const value_t& b) const
+        {
+            auto ke = KeyFn{};
+            return Equal{}(ke(a), ke(b));
+        }
+
+        template <typename Key>
+        bool operator()(const value_t& a, const Key& b) const
+        {
+            return Equal{}(KeyFn{}(a), b);
+        }
+    };
+
+    struct equal_value
+    {
+        bool operator()(const value_t& a, const value_t& b) const
+        {
+            return a == b;
+        }
+    };
+
+    using impl_t =
+        detail::hamts::champ<value_t, hash_key, equal_key, MemoryPolicy, B>;
+
+public:
+    using key_type        = K;
+    using mapped_type     = T;
+    using value_type      = T;
+    using size_type       = detail::hamts::size_t;
+    using diference_type  = std::ptrdiff_t;
+    using hasher          = Hash;
+    using key_equal       = Equal;
+    using reference       = const value_type&;
+    using const_reference = const value_type&;
+
+    using iterator = detail::hamts::
+        champ_iterator<value_t, hash_key, equal_key, MemoryPolicy, B>;
+    using const_iterator = iterator;
+
+    using transient_type =
+        table_transient<T, KeyFn, Hash, Equal, MemoryPolicy, B>;
+
+    using memory_policy_type = MemoryPolicy;
+
+    /*!
+     * Constructs a table containing the elements in `values`.
+     */
+    table(std::initializer_list<value_type> values)
+        : impl_{impl_t::from_initializer_list(values)}
+    {}
+
+    /*!
+     * Constructs a table containing the elements in the range
+     * defined by the input iterator `first` and range sentinel `last`.
+     */
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
+    table(Iter first, Sent last)
+        : impl_{impl_t::from_range(first, last)}
+    {}
+
+    /*!
+     * Default constructor.  It creates a table of `size() == 0`. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    table() = default;
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Returns `1` when the key `k` is contained in the table or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD size_type count(const Key& k) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(k);
+    }
+
+    /*!
+     * Returns `1` when the key `k` is contained in the table or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type count(const K& k) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If there is no entry with such a key in the table, it returns a
+     * default constructed value.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD const T& operator[](const Key& k) const
+    {
+        return impl_.template get<project_value, default_value>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If there is no entry with such a key in the table, it returns a
+     * default constructed value.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD const T& operator[](const K& k) const
+    {
+        return impl_.template get<project_value, default_value>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`. If there is no entry with such a key in the table, throws an
+     * `std::out_of_range` error.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    const T& at(const Key& k) const
+    {
+        return impl_.template get<project_value, error_value>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`. If there is no entry with such a key in the table, throws an
+     * `std::out_of_range` error.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    const T& at(const K& k) const
+    {
+        return impl_.template get<project_value, error_value>(k);
+    }
+
+    /*!
+     * Returns a pointer to the value associated with the key `k`.
+     * If there is no entry with such a key in the table,
+     * a `nullptr` is returned. It does not allocate memory and
+     * its complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD const T* find(const K& k) const
+    {
+        return impl_.template get<project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(k);
+    }
+
+    /*!
+     * Returns a pointer to the value associated with the key `k`.
+     * If there is no entry with such a key in the table,
+     * a `nullptr` is returned. It does not allocate memory and
+     * its complexity is *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD const T* find(const Key& k) const
+    {
+        return impl_.template get<project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(k);
+    }
+
+    IMMER_NODISCARD bool operator==(const table& other) const
+    {
+        return impl_.template equals<equal_value>(other.impl_);
+    }
+
+    IMMER_NODISCARD bool operator!=(const table& other) const
+    {
+        return !(*this == other);
+    }
+
+    /*!
+     * Returns a table containing the `value`.
+     * If there is an entry with its key is already,
+     * it replaces this entry by `value`.
+     * It may allocate memory and its complexity is *effectively* @f$
+     * O(1) @f$.
+     */
+    IMMER_NODISCARD table insert(value_type value) const&
+    {
+        return impl_.add(std::move(value));
+    }
+
+    /*!
+     * Returns a table containing the `value`.
+     * If there is an entry with its key is already,
+     * it replaces this entry by `value`.
+     * It may allocate memory and its complexity is *effectively* @f$
+     * O(1) @f$.
+     */
+    IMMER_NODISCARD decltype(auto) insert(value_type value) &&
+    {
+        return insert_move(move_t{}, std::move(value));
+    }
+
+    /*!
+     * Returns `this->insert(fn((*this)[k]))`. In particular, `fn` maps
+     * `T` to `T`. The key `k` will be replaced inside the value returned by
+     * `fn`. It may allocate memory and its complexity is *effectively* @f$ O(1)
+     * @f$.
+     */
+    template <typename Fn>
+    IMMER_NODISCARD table update(key_type k, Fn&& fn) const&
+    {
+        return impl_
+            .template update<project_value, default_value, combine_value>(
+                std::move(k), std::forward<Fn>(fn));
+    }
+    template <typename Fn>
+    IMMER_NODISCARD decltype(auto) update(key_type k, Fn&& fn) &&
+    {
+        return update_move(move_t{}, std::move(k), std::forward<Fn>(fn));
+    }
+
+    /*!
+     * Returns `this.count(k) ? this->insert(fn((*this)[k])) : *this`. In
+     * particular, `fn` maps `T` to `T`. The key `k` will be replaced inside the
+     * value returned by `fn`.  It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    template <typename Fn>
+    IMMER_NODISCARD table update_if_exists(key_type k, Fn&& fn) const&
+    {
+        return impl_.template update_if_exists<project_value, combine_value>(
+            std::move(k), std::forward<Fn>(fn));
+    }
+    template <typename Fn>
+    IMMER_NODISCARD decltype(auto) update_if_exists(key_type k, Fn&& fn) &&
+    {
+        return update_if_exists_move(
+            move_t{}, std::move(k), std::forward<Fn>(fn));
+    }
+
+    /*!
+     * Returns a table without entries with given key `k`. If the key is not
+     * present it returns `*this`. It may allocate
+     * memory and its complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD table erase(const K& k) const& { return impl_.sub(k); }
+
+    /*!
+     * Returns a table without entries with given key `k`. If the key is not
+     * present it returns `*this`. It may allocate
+     * memory and its complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD decltype(auto) erase(const K& k) &&
+    {
+        return erase_move(move_t{}, k);
+    }
+
+    /*!
+     * Returns a @a transient form of this container, an
+     * `immer::table_transient`.
+     */
+    IMMER_NODISCARD transient_type transient() const&
+    {
+        return transient_type{impl_};
+    }
+
+    /*!
+     * Returns a @a transient form of this container, an
+     * `immer::table_transient`.
+     */
+    IMMER_NODISCARD transient_type transient() &&
+    {
+        return transient_type{std::move(impl_)};
+    }
+
+    // Semi-private
+    const impl_t& impl() const { return impl_; }
+
+private:
+    friend transient_type;
+
+    table&& insert_move(std::true_type, value_type value)
+    {
+        impl_.add_mut({}, std::move(value));
+        return std::move(*this);
+    }
+    table insert_move(std::false_type, value_type value)
+    {
+        return impl_.add(std::move(value));
+    }
+
+    template <typename Fn>
+    table&& update_move(std::true_type, key_type k, Fn&& fn)
+    {
+        impl_.template update_mut<project_value, default_value, combine_value>(
+            {}, std::move(k), std::forward<Fn>(fn));
+        return std::move(*this);
+    }
+    template <typename Fn>
+    table update_move(std::false_type, key_type k, Fn&& fn)
+    {
+        return impl_
+            .template update<project_value, default_value, combine_value>(
+                std::move(k), std::forward<Fn>(fn));
+    }
+
+    template <typename Fn>
+    table&& update_if_exists_move(std::true_type, key_type k, Fn&& fn)
+    {
+        impl_.template update_if_exists_mut<project_value, combine_value>(
+            {}, std::move(k), std::forward<Fn>(fn));
+        return std::move(*this);
+    }
+    template <typename Fn>
+    table update_if_exists_move(std::false_type, key_type k, Fn&& fn)
+    {
+        return impl_.template update_if_exists<project_value, combine_value>(
+            std::move(k), std::forward<Fn>(fn));
+    }
+
+    table&& erase_move(std::true_type, const key_type& value)
+    {
+        impl_.sub_mut({}, value);
+        return std::move(*this);
+    }
+    table erase_move(std::false_type, const key_type& value)
+    {
+        return impl_.sub(value);
+    }
+
+    table(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+
+    impl_t impl_ = impl_t::empty();
+};
+
+} // namespace immer
diff --git a/src/immer/table_transient.hpp b/src/immer/table_transient.hpp
new file mode 100644
index 0000000000..30b0a8c689
--- /dev/null
+++ b/src/immer/table_transient.hpp
@@ -0,0 +1,281 @@
+#pragma once
+
+#include <immer/detail/hamts/champ.hpp>
+#include <immer/memory_policy.hpp>
+#include <type_traits>
+
+namespace immer {
+
+template <typename T,
+          typename KeyFn,
+          typename Hash,
+          typename Equal,
+          typename MemoryPolicy,
+          detail::hamts::bits_t B>
+class table;
+
+/*!
+ * Mutable version of `immer::table`.
+ *
+ * @rst
+ *
+ * Refer to :doc:`transients` to learn more about when and how to use
+ * the mutable versions of immutable containers.
+ *
+ * @endrst
+ */
+template <typename T,
+          typename KeyFn,
+          typename Hash,
+          typename Equal,
+          typename MemoryPolicy,
+          detail::hamts::bits_t B>
+class table_transient : MemoryPolicy::transience_t::owner
+{
+    using K       = std::decay_t<decltype(KeyFn{}(std::declval<T>()))>;
+    using base_t  = typename MemoryPolicy::transience_t::owner;
+    using owner_t = base_t;
+
+public:
+    using persistent_type = table<T, KeyFn, Hash, Equal, MemoryPolicy, B>;
+    using key_type        = K;
+    using mapped_type     = T;
+    using value_type      = T;
+    using size_type       = detail::hamts::size_t;
+    using diference_type  = std::ptrdiff_t;
+    using hasher          = Hash;
+    using key_equal       = Equal;
+    using reference       = const value_type&;
+    using const_reference = const value_type&;
+
+    using iterator       = typename persistent_type::iterator;
+    using const_iterator = iterator;
+
+    /*!
+     * Default constructor.  It creates a table of `size() == 0`. It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    table_transient() = default;
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Returns `1` when the key `k` is contained in the table or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD size_type count(const Key& k) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(k);
+    }
+
+    /*!
+     * Returns `1` when the key `k` is contained in the table or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type count(const K& k) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If there is no entry with such a key in the table, it returns a
+     * default constructed value.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD const T& operator[](const Key& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value,
+                                  typename persistent_type::default_value>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`.  If there is no entry with such a key in the table, it returns a
+     * default constructed value.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD const T& operator[](const K& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value,
+                                  typename persistent_type::default_value>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`. If there is no entry with such a key in the table, throws an
+     * `std::out_of_range` error.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    const T& at(const Key& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value,
+                                  typename persistent_type::error_value>(k);
+    }
+
+    /*!
+     * Returns a `const` reference to the values associated to the key
+     * `k`. If there is no entry with such a key in the table, throws an
+     * `std::out_of_range` error.  It does not allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    const T& at(const K& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value,
+                                  typename persistent_type::error_value>(k);
+    }
+
+    /*!
+     * Returns a pointer to the value associated with the key `k`.
+     * If there is no entry with such a key in the table,
+     * a `nullptr` is returned. It does not allocate memory and
+     * its complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD const T* find(const K& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(k);
+    }
+
+    /*!
+     * Returns a pointer to the value associated with the key `k`.
+     * If there is no entry with such a key in the table,
+     * a `nullptr` is returned. It does not allocate memory and
+     * its complexity is *effectively* @f$ O(1) @f$.
+     *
+     * This overload participates in overload resolution only if
+     * `Hash::is_transparent` is valid and denotes a type.
+     */
+    template <typename Key,
+              typename U = Hash,
+              typename   = typename U::is_transparent>
+    IMMER_NODISCARD const T* find(const Key& k) const
+    {
+        return impl_.template get<typename persistent_type::project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(k);
+    }
+
+    /*!
+     * Inserts `value` to the table.
+     * If there is an entry with its key is already,
+     * it replaces this entry by `value`.
+     * It may allocate memory and its complexity is *effectively* @f$
+     * O(1) @f$.
+     */
+    void insert(value_type value) { impl_.add_mut(*this, std::move(value)); }
+
+    /*!
+     * Returns `this->insert(fn((*this)[k]))`. In particular, `fn` maps `T` to
+     * `T`. The key `k` will be set into the value returned bu `fn`.  It may
+     * allocate memory and its complexity is *effectively* @f$ O(1) @f$.
+     */
+    template <typename Fn>
+    void update(key_type k, Fn&& fn)
+    {
+        impl_.template update_mut<typename persistent_type::project_value,
+                                  typename persistent_type::default_value,
+                                  typename persistent_type::combine_value>(
+            *this, std::move(k), std::forward<Fn>(fn));
+    }
+
+    /*!
+     * Returns `this->insert(fn((*this)[k]))` when `this->count(k) > 0`. In
+     * particular, `fn` maps `T` to `T`. The key `k` will be replaced into the
+     * value returned by `fn`.  It may allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    template <typename Fn>
+    void update_if_exists(key_type k, Fn&& fn)
+    {
+        impl_.template update_if_exists_mut<
+            typename persistent_type::project_value,
+            typename persistent_type::combine_value>(
+            *this, std::move(k), std::forward<Fn>(fn));
+    }
+
+    /*!
+     * Removes table entry by given key `k` if there is any. It may allocate
+     * memory and its complexity is *effectively* @f$ O(1) @f$.
+     */
+    void erase(const K& k) { impl_.sub_mut(*this, k); }
+
+    /*!
+     * Returns an @a immutable form of this container, an
+     * `immer::table`.
+     */
+    IMMER_NODISCARD persistent_type persistent() &
+    {
+        this->owner_t::operator=(owner_t{});
+        return impl_;
+    }
+
+    /*!
+     * Returns an @a immutable form of this container, an
+     * `immer::table`.
+     */
+    IMMER_NODISCARD persistent_type persistent() && { return std::move(impl_); }
+
+private:
+    friend persistent_type;
+    using impl_t = typename persistent_type::impl_t;
+
+    table_transient(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+
+    impl_t impl_ = impl_t::empty();
+
+public:
+    // Semi-private
+    const impl_t& impl() const { return impl_; }
+};
+
+} // namespace immer
diff --git a/src/immer/transience/gc_transience_policy.hpp b/src/immer/transience/gc_transience_policy.hpp
index 7137bed839..a569ed8b42 100644
--- a/src/immer/transience/gc_transience_policy.hpp
+++ b/src/immer/transience/gc_transience_policy.hpp
@@ -10,9 +10,9 @@
 
 #include <immer/heap/tags.hpp>
 
+#include <atomic>
 #include <memory>
 #include <utility>
-#include <atomic>
 
 namespace immer {
 
@@ -40,6 +40,9 @@ struct gc_transience_policy
             struct edit
             {
                 void* v;
+                edit(void* v_)
+                    : v{v_}
+                {}
                 edit() = delete;
                 bool operator==(edit x) const { return v == x.v; }
                 bool operator!=(edit x) const { return v != x.v; }
@@ -54,7 +57,7 @@ struct gc_transience_policy
 
                 mutable std::atomic<void*> token_;
 
-                operator edit () { return { token_ }; }
+                operator edit() { return {token_}; }
 
                 owner()
                     : token_{make_token_()}
@@ -82,7 +85,7 @@ struct gc_transience_policy
 
             struct ownee
             {
-                edit token_ {nullptr};
+                edit token_{nullptr};
 
                 ownee& operator=(edit e)
                 {
@@ -105,6 +108,6 @@ struct gc_transience_policy
 
 template <typename HP>
 typename gc_transience_policy::apply<HP>::type::owner
-gc_transience_policy::apply<HP>::type::noone = {};
+    gc_transience_policy::apply<HP>::type::noone = {};
 
 } // namespace immer
diff --git a/src/immer/transience/no_transience_policy.hpp b/src/immer/transience/no_transience_policy.hpp
index aa3d44ed42..2f87df7a39 100644
--- a/src/immer/transience/no_transience_policy.hpp
+++ b/src/immer/transience/no_transience_policy.hpp
@@ -21,12 +21,12 @@ struct no_transience_policy
     {
         struct type
         {
-            struct edit {};
+            struct edit
+            {};
 
             struct owner
             {
-                operator edit () const { return {}; }
-                owner& operator=(const owner&) { return *this; };
+                operator edit() const { return {}; }
             };
 
             struct ownee
@@ -43,6 +43,6 @@ struct no_transience_policy
 
 template <typename HP>
 typename no_transience_policy::apply<HP>::type::owner
-no_transience_policy::apply<HP>::type::noone = {};
+    no_transience_policy::apply<HP>::type::noone = {};
 
 } // namespace immer
diff --git a/src/immer/vector.hpp b/src/immer/vector.hpp
index b9c9ef48f6..8c8ab05d69 100644
--- a/src/immer/vector.hpp
+++ b/src/immer/vector.hpp
@@ -40,7 +40,7 @@ class vector_transient;
  *
  * @rst
  *
- * This cotainer provides a good trade-off between cache locality,
+ * This container provides a good trade-off between cache locality,
  * random access, update performance and structural sharing.  It does
  * so by storing the data in contiguous chunks of :math:`2^{BL}`
  * elements.  By default, when ``sizeof(T) == sizeof(void*)`` then
@@ -68,9 +68,10 @@ class vector_transient;
  * @endrst
  */
 template <typename T,
-          typename MemoryPolicy   = default_memory_policy,
-          detail::rbts::bits_t B  = default_bits,
-          detail::rbts::bits_t BL = detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
+          typename MemoryPolicy  = default_memory_policy,
+          detail::rbts::bits_t B = default_bits,
+          detail::rbts::bits_t BL =
+              detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
 class vector
 {
     using impl_t = detail::rbts::rbtree<T, MemoryPolicy, B, BL>;
@@ -80,21 +81,27 @@ class vector
         std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>;
 
 public:
-    static constexpr auto bits = B;
+    static constexpr auto bits      = B;
     static constexpr auto bits_leaf = BL;
-    using memory_policy = MemoryPolicy;
+    using memory_policy             = MemoryPolicy;
 
-    using value_type = T;
-    using reference = const T&;
-    using size_type = detail::rbts::size_t;
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = detail::rbts::size_t;
     using difference_type = std::ptrdiff_t;
     using const_reference = const T&;
 
-    using iterator         = detail::rbts::rbtree_iterator<T, MemoryPolicy, B, BL>;
+    using iterator = detail::rbts::rbtree_iterator<T, MemoryPolicy, B, BL>;
     using const_iterator   = iterator;
     using reverse_iterator = std::reverse_iterator<iterator>;
 
-    using transient_type   = vector_transient<T, MemoryPolicy, B, BL>;
+    using transient_type = vector_transient<T, MemoryPolicy, B, BL>;
+
+    /*!
+     * Returns the maximum theoretical size supported by the internal structure
+     * given the current B, BL.
+     */
+    constexpr static size_type max_size() { return impl_t::max_size(); }
 
     /*!
      * Default constructor.  It creates a vector of `size() == 0`.  It
@@ -113,9 +120,10 @@ class vector
      * Constructs a vector containing the elements in the range
      * defined by the input iterator `first` and range sentinel `last`.
      */
-    template <typename Iter, typename Sent,
-              std::enable_if_t
-              <detail::compatible_sentinel_v<Iter, Sent>, bool> = true>
+    template <typename Iter,
+              typename Sent,
+              std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>,
+                               bool> = true>
     vector(Iter first, Sent last)
         : impl_{impl_t::from_range(first, last)}
     {}
@@ -133,49 +141,58 @@ class vector
      * collection. It does not allocate memory and its complexity is
      * @f$ O(1) @f$.
      */
-    iterator begin() const { return {impl_}; }
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
 
     /*!
      * Returns an iterator pointing just after the last element of the
      * collection. It does not allocate and its complexity is @f$ O(1) @f$.
      */
-    iterator end() const { return {impl_, typename iterator::end_t{}}; }
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing at the first element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rbegin() const { return reverse_iterator{end()}; }
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing after the last element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rend()   const { return reverse_iterator{begin()}; }
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
 
     /*!
      * Returns the number of elements in the container.  It does
      * not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    size_type size() const { return impl_.size; }
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
 
     /*!
      * Returns `true` if there are no elements in the container.  It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    bool empty() const { return impl_.size == 0; }
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
 
     /*!
      * Access the last element.
      */
-    const T& back() const { return impl_.back(); }
+    IMMER_NODISCARD const T& back() const { return impl_.back(); }
 
     /*!
      * Access the first element.
      */
-    const T& front() const { return impl_.front(); }
+    IMMER_NODISCARD const T& front() const { return impl_.front(); }
 
     /*!
      * Returns a `const` reference to the element at position `index`.
@@ -183,8 +200,10 @@ class vector
      * allocate memory and its complexity is *effectively* @f$ O(1)
      * @f$.
      */
-    reference operator[] (size_type index) const
-    { return impl_.get(index); }
+    IMMER_NODISCARD reference operator[](size_type index) const
+    {
+        return impl_.get(index);
+    }
 
     /*!
      * Returns a `const` reference to the element at position
@@ -192,16 +211,19 @@ class vector
      * index \geq size() @f$.  It does not allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
-    reference at(size_type index) const
-    { return impl_.get_check(index); }
+    reference at(size_type index) const { return impl_.get_check(index); }
 
     /*!
      * Returns whether the vectors are equal.
      */
-    bool operator==(const vector& other) const
-    { return impl_.equals(other.impl_); }
-    bool operator!=(const vector& other) const
-    { return !(*this == other); }
+    IMMER_NODISCARD bool operator==(const vector& other) const
+    {
+        return impl_.equals(other.impl_);
+    }
+    IMMER_NODISCARD bool operator!=(const vector& other) const
+    {
+        return !(*this == other);
+    }
 
     /*!
      * Returns a vector with `value` inserted at the end.  It may
@@ -218,11 +240,15 @@ class vector
      *
      * @endrst
      */
-    vector push_back(value_type value) const&
-    { return impl_.push_back(std::move(value)); }
+    IMMER_NODISCARD vector push_back(value_type value) const&
+    {
+        return impl_.push_back(std::move(value));
+    }
 
-    decltype(auto) push_back(value_type value) &&
-    { return push_back_move(move_t{}, std::move(value)); }
+    IMMER_NODISCARD decltype(auto) push_back(value_type value) &&
+    {
+        return push_back_move(move_t{}, std::move(value));
+    }
 
     /*!
      * Returns a vector containing value `value` at position `idx`.
@@ -241,11 +267,15 @@ class vector
      *
      * @endrst
      */
-    vector set(size_type index, value_type value) const&
-    { return impl_.assoc(index, std::move(value)); }
+    IMMER_NODISCARD vector set(size_type index, value_type value) const&
+    {
+        return impl_.assoc(index, std::move(value));
+    }
 
-    decltype(auto) set(size_type index, value_type value) &&
-    { return set_move(move_t{}, index, std::move(value)); }
+    IMMER_NODISCARD decltype(auto) set(size_type index, value_type value) &&
+    {
+        return set_move(move_t{}, index, std::move(value));
+    }
 
     /*!
      * Returns a vector containing the result of the expression
@@ -266,12 +296,16 @@ class vector
      * @endrst
      */
     template <typename FnT>
-    vector update(size_type index, FnT&& fn) const&
-    { return impl_.update(index, std::forward<FnT>(fn)); }
+    IMMER_NODISCARD vector update(size_type index, FnT&& fn) const&
+    {
+        return impl_.update(index, std::forward<FnT>(fn));
+    }
 
     template <typename FnT>
-    decltype(auto) update(size_type index, FnT&& fn) &&
-    { return update_move(move_t{}, index, std::forward<FnT>(fn)); }
+    IMMER_NODISCARD decltype(auto) update(size_type index, FnT&& fn) &&
+    {
+        return update_move(move_t{}, index, std::forward<FnT>(fn));
+    }
 
     /*!
      * Returns a vector containing only the first `min(elems, size())`
@@ -289,27 +323,42 @@ class vector
      *
      * @endrst
      */
-    vector take(size_type elems) const&
-    { return impl_.take(elems); }
+    IMMER_NODISCARD vector take(size_type elems) const&
+    {
+        return impl_.take(elems);
+    }
 
-    decltype(auto) take(size_type elems) &&
-    { return take_move(move_t{}, elems); }
+    IMMER_NODISCARD decltype(auto) take(size_type elems) &&
+    {
+        return take_move(move_t{}, elems);
+    }
 
     /*!
      * Returns an @a transient form of this container, an
      * `immer::vector_transient`.
      */
-    transient_type transient() const&
-    { return transient_type{ impl_ }; }
-    transient_type transient() &&
-    { return transient_type{ std::move(impl_) }; }
+    IMMER_NODISCARD transient_type transient() const& { return impl_; }
+    IMMER_NODISCARD transient_type transient() && { return std::move(impl_); }
+
+    /*!
+     * Returns a value that can be used as identity for the container.  If two
+     * values have the same identity, they are guaranteed to be equal and to
+     * contain the same objects.  However, two equal containers are not
+     * guaranteed to have the same identity.
+     */
+    std::pair<void*, void*> identity() const
+    {
+        return {impl_.root, impl_.tail};
+    }
 
     // Semi-private
     const impl_t& impl() const { return impl_; }
 
 #if IMMER_DEBUG_PRINT
-    void debug_print(std::ostream& out=std::cerr) const
-    { flex_t{*this}.debug_print(out); }
+    void debug_print(std::ostream& out = std::cerr) const
+    {
+        flex_t{*this}.debug_print(out);
+    }
 #endif
 
 private:
@@ -322,33 +371,53 @@ class vector
 #if IMMER_DEBUG_PRINT
         // force the compiler to generate debug_print, so we can call
         // it from a debugger
-        [](volatile auto){}(&vector::debug_print);
+        [](volatile auto) {}(&vector::debug_print);
 #endif
     }
 
     vector&& push_back_move(std::true_type, value_type value)
-    { impl_.push_back_mut({}, std::move(value)); return std::move(*this); }
+    {
+        impl_.push_back_mut({}, std::move(value));
+        return std::move(*this);
+    }
     vector push_back_move(std::false_type, value_type value)
-    { return impl_.push_back(std::move(value)); }
+    {
+        return impl_.push_back(std::move(value));
+    }
 
     vector&& set_move(std::true_type, size_type index, value_type value)
-    { impl_.assoc_mut({}, index, std::move(value)); return std::move(*this); }
+    {
+        impl_.assoc_mut({}, index, std::move(value));
+        return std::move(*this);
+    }
     vector set_move(std::false_type, size_type index, value_type value)
-    { return impl_.assoc(index, std::move(value)); }
+    {
+        return impl_.assoc(index, std::move(value));
+    }
 
     template <typename Fn>
     vector&& update_move(std::true_type, size_type index, Fn&& fn)
-    { impl_.update_mut({}, index, std::forward<Fn>(fn)); return std::move(*this); }
+    {
+        impl_.update_mut({}, index, std::forward<Fn>(fn));
+        return std::move(*this);
+    }
     template <typename Fn>
     vector update_move(std::false_type, size_type index, Fn&& fn)
-    { return impl_.update(index, std::forward<Fn>(fn)); }
+    {
+        return impl_.update(index, std::forward<Fn>(fn));
+    }
 
     vector&& take_move(std::true_type, size_type elems)
-    { impl_.take_mut({}, elems); return std::move(*this); }
+    {
+        impl_.take_mut({}, elems);
+        return std::move(*this);
+    }
     vector take_move(std::false_type, size_type elems)
-    { return impl_.take(elems); }
+    {
+        return impl_.take(elems);
+    }
 
-    impl_t impl_ = impl_t::empty();
+    impl_t impl_ = {};
 };
 
 } // namespace immer
diff --git a/src/immer/vector_transient.hpp b/src/immer/vector_transient.hpp
index 4cf81f0711..edf7668fe7 100644
--- a/src/immer/vector_transient.hpp
+++ b/src/immer/vector_transient.hpp
@@ -37,32 +37,32 @@ class flex_vector_transient;
  * @endrst
  */
 template <typename T,
-          typename MemoryPolicy   = default_memory_policy,
-          detail::rbts::bits_t B  = default_bits,
-          detail::rbts::bits_t BL = detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
-class vector_transient
-    : MemoryPolicy::transience_t::owner
+          typename MemoryPolicy  = default_memory_policy,
+          detail::rbts::bits_t B = default_bits,
+          detail::rbts::bits_t BL =
+              detail::rbts::derive_bits_leaf<T, MemoryPolicy, B>>
+class vector_transient : MemoryPolicy::transience_t::owner
 {
-    using impl_t = detail::rbts::rbtree<T, MemoryPolicy, B, BL>;
-    using flex_t = flex_vector_transient<T, MemoryPolicy, B, BL>;
+    using impl_t  = detail::rbts::rbtree<T, MemoryPolicy, B, BL>;
+    using flex_t  = flex_vector_transient<T, MemoryPolicy, B, BL>;
     using owner_t = typename MemoryPolicy::transience_t::owner;
 
 public:
-    static constexpr auto bits = B;
+    static constexpr auto bits      = B;
     static constexpr auto bits_leaf = BL;
-    using memory_policy = MemoryPolicy;
+    using memory_policy             = MemoryPolicy;
 
-    using value_type = T;
-    using reference = const T&;
-    using size_type = detail::rbts::size_t;
+    using value_type      = T;
+    using reference       = const T&;
+    using size_type       = detail::rbts::size_t;
     using difference_type = std::ptrdiff_t;
     using const_reference = const T&;
 
-    using iterator         = detail::rbts::rbtree_iterator<T, MemoryPolicy, B, BL>;
+    using iterator = detail::rbts::rbtree_iterator<T, MemoryPolicy, B, BL>;
     using const_iterator   = iterator;
     using reverse_iterator = std::reverse_iterator<iterator>;
 
-    using persistent_type  = vector<T, MemoryPolicy, B, BL>;
+    using persistent_type = vector<T, MemoryPolicy, B, BL>;
 
     /*!
      * Default constructor.  It creates a mutable vector of `size() ==
@@ -76,39 +76,48 @@ class vector_transient
      * collection. It does not allocate memory and its complexity is
      * @f$ O(1) @f$.
      */
-    iterator begin() const { return {impl_}; }
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
 
     /*!
      * Returns an iterator pointing just after the last element of the
      * collection. It does not allocate and its complexity is @f$ O(1) @f$.
      */
-    iterator end()   const { return {impl_, typename iterator::end_t{}}; }
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing at the first element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rbegin() const { return reverse_iterator{end()}; }
+    IMMER_NODISCARD reverse_iterator rbegin() const
+    {
+        return reverse_iterator{end()};
+    }
 
     /*!
      * Returns an iterator that traverses the collection backwards,
      * pointing after the last element of the reversed collection. It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    reverse_iterator rend()   const { return reverse_iterator{begin()}; }
+    IMMER_NODISCARD reverse_iterator rend() const
+    {
+        return reverse_iterator{begin()};
+    }
 
     /*!
      * Returns the number of elements in the container.  It does
      * not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    size_type size() const { return impl_.size; }
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
 
     /*!
      * Returns `true` if there are no elements in the container.  It
      * does not allocate memory and its complexity is @f$ O(1) @f$.
      */
-    bool empty() const { return impl_.size == 0; }
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
 
     /*!
      * Returns a `const` reference to the element at position `index`.
@@ -116,8 +125,7 @@ class vector_transient
      * allocate memory and its complexity is *effectively* @f$ O(1)
      * @f$.
      */
-    reference operator[] (size_type index) const
-    { return impl_.get(index); }
+    reference operator[](size_type index) const { return impl_.get(index); }
 
     /*!
      * Returns a `const` reference to the element at position
@@ -125,15 +133,16 @@ class vector_transient
      * index \geq size() @f$.  It does not allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
-    reference at(size_type index) const
-    { return impl_.get_check(index); }
+    reference at(size_type index) const { return impl_.get_check(index); }
 
     /*!
      * Inserts `value` at the end.  It may allocate memory and its
      * complexity is *effectively* @f$ O(1) @f$.
      */
     void push_back(value_type value)
-    { impl_.push_back_mut(*this, std::move(value)); }
+    {
+        impl_.push_back_mut(*this, std::move(value));
+    }
 
     /*!
      * Sets to the value `value` at position `idx`.
@@ -142,7 +151,9 @@ class vector_transient
      * *effectively* @f$ O(1) @f$.
      */
     void set(size_type index, value_type value)
-    { impl_.assoc_mut(*this, index, std::move(value)); }
+    {
+        impl_.assoc_mut(*this, index, std::move(value));
+    }
 
     /*!
      * Updates the vector to contain the result of the expression
@@ -153,27 +164,27 @@ class vector_transient
      */
     template <typename FnT>
     void update(size_type index, FnT&& fn)
-    { impl_.update_mut(*this, index, std::forward<FnT>(fn)); }
+    {
+        impl_.update_mut(*this, index, std::forward<FnT>(fn));
+    }
 
     /*!
      * Resizes the vector to only contain the first `min(elems, size())`
      * elements. It may allocate memory and its complexity is
      * *effectively* @f$ O(1) @f$.
      */
-    void take(size_type elems)
-    { impl_.take_mut(*this, elems); }
+    void take(size_type elems) { impl_.take_mut(*this, elems); }
 
     /*!
      * Returns an @a immutable form of this container, an
      * `immer::vector`.
      */
-    persistent_type persistent() &
+    IMMER_NODISCARD persistent_type persistent() &
     {
         this->owner_t::operator=(owner_t{});
-        return persistent_type{ impl_ };
+        return impl_;
     }
-    persistent_type persistent() &&
-    { return persistent_type{ std::move(impl_) }; }
+    IMMER_NODISCARD persistent_type persistent() && { return std::move(impl_); }
 
 private:
     friend flex_t;
@@ -183,7 +194,7 @@ class vector_transient
         : impl_(std::move(impl))
     {}
 
-    impl_t  impl_  = impl_t::empty();
+    impl_t impl_ = {};
 };
 
 } // namespace immer

From 65c7034e26edbf7e40f1a0ad5718a67f13e19d4b Mon Sep 17 00:00:00 2001
From: aleflm <aleflm@proton.me>
Date: Thu, 11 Apr 2024 13:38:48 +0000
Subject: [PATCH 2/2] Fixed warnings in Linux / GCC. Fixed warnings in Linux /
 Clang. Fixed warnings in Win cross-compile. no-gui build only.

---
 src/compat/glibcxx_sanity.cpp         | 2 +-
 src/libspark/bpplus.cpp               | 2 +-
 src/rpc/net.cpp                       | 2 +-
 src/rpc/protocol.cpp                  | 2 +-
 src/script/ismine.cpp                 | 2 ++
 src/secp256k1/src/ecmult.h            | 1 +
 src/secp256k1/src/ecmult_const_impl.h | 7 -------
 src/secp256k1/src/ecmult_impl.h       | 1 -
 src/secp256k1/src/scratch_impl.h      | 1 +
 src/util.cpp                          | 4 ++--
 src/wallet/wallet.cpp                 | 6 ++++--
 11 files changed, 14 insertions(+), 16 deletions(-)

diff --git a/src/compat/glibcxx_sanity.cpp b/src/compat/glibcxx_sanity.cpp
index cee8a98c7f..5c4e5e6378 100644
--- a/src/compat/glibcxx_sanity.cpp
+++ b/src/compat/glibcxx_sanity.cpp
@@ -47,7 +47,7 @@ bool sanity_test_range_fmt()
 {
     std::string test;
     try {
-        test.at(1);
+        (void) test.at(1);
     } catch (const std::out_of_range&) {
         return true;
     } catch (...) {
diff --git a/src/libspark/bpplus.cpp b/src/libspark/bpplus.cpp
index 0241045795..c9822a543d 100644
--- a/src/libspark/bpplus.cpp
+++ b/src/libspark/bpplus.cpp
@@ -315,7 +315,7 @@ bool BPPlus::verify(const std::vector<std::vector<GroupElement>>& unpadded_C, co
             return false;
         }
         if (!is_nonzero_power_of_2(M)) {
-            M = 1 << log2(unpadded_M) + 1;
+            M = 1 << (log2(unpadded_M) + 1);
         }
 
         // Track the maximum value
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index d9505c8724..8cb73ad7ac 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -151,7 +151,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
         obj.push_back(Pair("timeoffset", stats.nTimeOffset));
         if (stats.dPingTime > 0.0)
             obj.push_back(Pair("pingtime", stats.dPingTime));
-        if (stats.dMinPing < std::numeric_limits<int64_t>::max()/1e6)
+        if (stats.dMinPing < static_cast<double>(std::numeric_limits<int64_t>::max())/1e6)
             obj.push_back(Pair("minping", stats.dMinPing));
         if (stats.dPingWait > 0.0)
             obj.push_back(Pair("pingwait", stats.dPingWait));
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
index 2be1edb5a6..2d4f0d9211 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/protocol.cpp
@@ -69,7 +69,7 @@ static const std::string COOKIEAUTH_FILE = ".cookie";
 boost::filesystem::path GetAuthCookieFile()
 {
     boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE));
-    if (!path.is_complete()) path = GetDataDir() / path;
+    if (!path.is_absolute()) path = GetDataDir() / path;
     return path;
 }
 
diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp
index 670bf8a1d3..13b9d622a0 100644
--- a/src/script/ismine.cpp
+++ b/src/script/ismine.cpp
@@ -148,6 +148,8 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool&
             return ISMINE_SPENDABLE;
         break;
     }
+    case TX_SPARKMINT: {}
+    case TX_SPARKSMINT: {}
     }
 
     if (keystore.HaveWatchOnly(scriptPubKey)) {
diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h
index e90c4103fe..b0eda32e2f 100644
--- a/src/secp256k1/src/ecmult.h
+++ b/src/secp256k1/src/ecmult.h
@@ -11,6 +11,7 @@
 #include "group.h"
 #include "scalar.h"
 #include "scratch.h"
+#include "scratch_impl.h"
 
 typedef struct {
     /* For accelerating the computation of a*P + b*G: */
diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index 0db314c48e..bf1aaa7f21 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -12,13 +12,6 @@
 #include "ecmult_const.h"
 #include "ecmult_impl.h"
 
-#ifdef USE_ENDOMORPHISM
-    #define WNAF_BITS 128
-#else
-    #define WNAF_BITS 256
-#endif
-#define WNAF_SIZE(w) ((WNAF_BITS + (w) - 1) / (w))
-
 /* This is like `ECMULT_TABLE_GET_GE` but is constant time */
 #define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \
     int m; \
diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h
index ab1167816d..4bb3d29151 100644
--- a/src/secp256k1/src/ecmult_impl.h
+++ b/src/secp256k1/src/ecmult_impl.h
@@ -748,7 +748,6 @@ static int secp256k1_pippenger_bucket_window(size_t n) {
  * Returns the maximum optimal number of points for a bucket_window.
  */
 static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) {
-    int i;
 #ifdef USE_ENDOMORPHISM
     int size[11]= {1, 4, 20, 57, 136, 235, 1260, 1260, 4420, 7880, 16050};
 #else
diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h
index abed713b21..c8a77e362f 100644
--- a/src/secp256k1/src/scratch_impl.h
+++ b/src/secp256k1/src/scratch_impl.h
@@ -8,6 +8,7 @@
 #define _SECP256K1_SCRATCH_IMPL_H_
 
 #include "scratch.h"
+#include <string.h>
 
 /* Using 16 bytes alignment because common architectures never have alignment
  * requirements above 8 for any of the types we care about. In addition we
diff --git a/src/util.cpp b/src/util.cpp
index 41d549f678..3c5296b2d3 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -612,7 +612,7 @@ void ClearDatadirCache()
 boost::filesystem::path GetConfigFile(const std::string& confPath)
 {
     boost::filesystem::path pathConfigFile(confPath);
-    if (!pathConfigFile.is_complete()) {
+    if (!pathConfigFile.is_absolute()) {
         boost::filesystem::path dataDir = GetDataDir(false);
 
         // upgrade heuristics: if dataDir ends with either "zcoin" or ".zcoin" and confPath is set
@@ -694,7 +694,7 @@ bool RenameDirectoriesFromZcoinToFiro()
 boost::filesystem::path GetPidFile()
 {
     boost::filesystem::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME));
-    if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile;
+    if (!pathPidFile.is_absolute()) pathPidFile = GetDataDir() / pathPidFile;
     return pathPidFile;
 }
 
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index fababfae30..9c0f6b11c3 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -4,6 +4,7 @@
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
 #include "wallet.h"
+#include "boost/filesystem/operations.hpp"
 #include "walletexcept.h"
 #include "sigmaspendbuilder.h"
 #include "lelantusjoinsplitbuilder.h"
@@ -671,7 +672,7 @@ bool CWallet::Verify()
     uiInterface.InitMessage(_("Verifying wallet..."));
 
     // Wallet file must be a plain filename without a directory
-    if (walletFile != boost::filesystem::basename(walletFile) + boost::filesystem::extension(walletFile))
+    if (walletFile != boost::filesystem::path(walletFile).stem().string() + boost::filesystem::path(walletFile).extension().string())
         return InitError(strprintf(_("Wallet %s resides outside data directory %s"), walletFile, GetDataDir().string()));
 
     if (!bitdb.Open(GetDataDir()))
@@ -7514,7 +7515,8 @@ bool CWallet::BackupWallet(const std::string& strDest)
 
                 try {
 #if BOOST_VERSION >= 104000
-                    boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists);
+                    const auto copyOptions = boost::filesystem::copy_options::overwrite_existing;
+                    boost::filesystem::copy(pathSrc, pathDest, copyOptions);
 #else
                     boost::filesystem::copy_file(pathSrc, pathDest);
 #endif