// Copyright (C) 2019 T. Zachary Laine
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_STL_INTERFACES_CONTAINER_INTERFACE_HPP
#define BOOST_STL_INTERFACES_CONTAINER_INTERFACE_HPP

#include <boost/stl_interfaces/reverse_iterator.hpp>

#include <boost/assert.hpp>
#include <boost/config.hpp>

#include <algorithm>
#include <stdexcept>
#include <cstddef>


namespace boost { namespace stl_interfaces { namespace detail {

    template<typename T, typename SizeType>
    struct n_iter : iterator_interface<
                        n_iter<T, SizeType>,
                        std::random_access_iterator_tag,
                        T>
    {
        n_iter() : x_(nullptr), n_(0) {}
        n_iter(T const & x, SizeType n) : x_(&x), n_(n) {}

        T const & operator*() const { return *x_; }
        constexpr std::ptrdiff_t operator-(n_iter other) const noexcept
        {
            return std::ptrdiff_t(n_) - std::ptrdiff_t(other.n_);
        }
        n_iter & operator+=(std::ptrdiff_t offset)
        {
            n_ += offset;
            return *this;
        }

    private:
        T const * x_;
        SizeType n_;
    };

    template<typename T, typename SizeType>
    constexpr auto make_n_iter(T const & x, SizeType n) noexcept(
        noexcept(n_iter<T, SizeType>(x, n)))
    {
        using result_type = n_iter<T, SizeType>;
        return result_type(x, SizeType(0));
    }
    template<typename T, typename SizeType>
    constexpr auto make_n_iter_end(T const & x, SizeType n) noexcept(
        noexcept(n_iter<T, SizeType>(x, n)))
    {
        return n_iter<T, SizeType>(x, n);
    }

    template<typename Container>
    std::size_t fake_capacity(Container const & c)
    {
        return SIZE_MAX;
    }
    template<
        typename Container,
        typename Enable = decltype(
            std::size_t() = std::declval<Container const &>().capacity())>
    std::size_t fake_capacity(Container const & c)
    {
        return c.capacity();
    }

}}}

namespace boost { namespace stl_interfaces { BOOST_STL_INTERFACES_NAMESPACE_V1 {

    /** A CRTP template that one may derive from to make it easier to define
        container types.

        The template parameter `D` for `sequence_container_interface` may be
        an incomplete type. Before any member of the resulting specialization
        of `sequence_container_interface` other than special member functions
        is referenced, `D` shall be complete; shall model
        `std::derived_from<sequence_container_interface<D>>`,
        `std::semiregular`, and `std::forward_range`; and shall contain all
        the nested types required in Table 72: Container requirements and, for
        those whose iterator nested type models `std::bidirectinal_iterator`,
        those in Table 73: Reversible container requirements.

        For an object `d` of type `D`, a call to `std::ranges::begin(d)` sxhall
        not mutate any data members of `d`, and `d`'s destructor shall end the
        lifetimes of the objects in `[std::ranges::begin(d),
        std::ranges::end(d))`. */
    template<
        typename Derived,
        element_layout Contiguity = element_layout::discontiguous
#ifndef BOOST_STL_INTERFACES_DOXYGEN
        ,
        typename E = std::enable_if_t<
            std::is_class<Derived>::value &&
            std::is_same<Derived, std::remove_cv_t<Derived>>::value>
#endif
        >
    struct sequence_container_interface;

    namespace v1_dtl {
        template<typename Iter>
        using in_iter = std::is_convertible<
            typename std::iterator_traits<Iter>::iterator_category,
            std::input_iterator_tag>;

        template<typename D, typename = void>
        struct clear_impl
        {
            static constexpr void call(D & d) noexcept {}
        };
        template<typename D>
        struct clear_impl<D, void_t<decltype(std::declval<D>().clear())>>
        {
            static constexpr void call(D & d) noexcept { d.clear(); }
        };

        template<typename D, element_layout Contiguity>
        void derived_container(sequence_container_interface<D, Contiguity> const &);
    }

    template<
        typename Derived,
        element_layout Contiguity
#ifndef BOOST_STL_INTERFACES_DOXYGEN
        ,
        typename E
#endif
        >
    struct sequence_container_interface
    {
#ifndef BOOST_STL_INTERFACES_DOXYGEN
    private:
        constexpr Derived & derived() noexcept
        {
            return static_cast<Derived &>(*this);
        }
        constexpr const Derived & derived() const noexcept
        {
            return static_cast<Derived const &>(*this);
        }
        constexpr Derived & mutable_derived() const noexcept
        {
            return const_cast<Derived &>(static_cast<Derived const &>(*this));
        }
#endif

    public:
        template<typename D = Derived>
        constexpr auto empty() noexcept(
            noexcept(std::declval<D &>().begin() == std::declval<D &>().end()))
            -> decltype(
                std::declval<D &>().begin() == std::declval<D &>().end())
        {
            return derived().begin() == derived().end();
        }
        template<typename D = Derived>
        constexpr auto empty() const noexcept(noexcept(
            std::declval<D const &>().begin() ==
            std::declval<D const &>().end()))
            -> decltype(
                std::declval<D const &>().begin() ==
                std::declval<D const &>().end())
        {
            return derived().begin() == derived().end();
        }

        template<
            typename D = Derived,
            element_layout C = Contiguity,
            typename Enable = std::enable_if_t<C == element_layout::contiguous>>
        constexpr auto data() noexcept(noexcept(std::declval<D &>().begin()))
            -> decltype(std::addressof(*std::declval<D &>().begin()))
        {
            return std::addressof(*derived().begin());
        }
        template<
            typename D = Derived,
            element_layout C = Contiguity,
            typename Enable = std::enable_if_t<C == element_layout::contiguous>>
        constexpr auto data() const
            noexcept(noexcept(std::declval<D const &>().begin()))
                -> decltype(std::addressof(*std::declval<D const &>().begin()))
        {
            return std::addressof(*derived().begin());
        }

        template<typename D = Derived>
        constexpr auto size()
#if !BOOST_CLANG
            noexcept(noexcept(
                std::declval<D &>().end() - std::declval<D &>().begin()))
#endif
            -> decltype(typename D::size_type(
                std::declval<D &>().end() - std::declval<D &>().begin()))
        {
            return derived().end() - derived().begin();
        }
        template<typename D = Derived>
        constexpr auto size() const noexcept(noexcept(
            std::declval<D const &>().end() -
            std::declval<D const &>().begin()))
            -> decltype(typename D::size_type(
#if !BOOST_CLANG
                std::declval<D const &>().end() -
                std::declval<D const &>().begin()
#endif
                ))
        {
            return derived().end() - derived().begin();
        }

        template<typename D = Derived>
        constexpr auto front() noexcept(noexcept(*std::declval<D &>().begin()))
            -> decltype(*std::declval<D &>().begin())
        {
            return *derived().begin();
        }
        template<typename D = Derived>
        constexpr auto front() const
            noexcept(noexcept(*std::declval<D const &>().begin()))
                -> decltype(*std::declval<D const &>().begin())
        {
            return *derived().begin();
        }

        template<typename D = Derived>
        constexpr auto push_front(typename D::value_type const & x) noexcept(
            noexcept(std::declval<D &>().emplace_front(x)))
            -> decltype((void)std::declval<D &>().emplace_front(x))
        {
            derived().emplace_front(x);
        }

        template<typename D = Derived>
        constexpr auto push_front(typename D::value_type && x) noexcept(
            noexcept(std::declval<D &>().emplace_front(std::move(x))))
            -> decltype((void)std::declval<D &>().emplace_front(std::move(x)))
        {
            derived().emplace_front(std::move(x));
        }

        template<typename D = Derived>
        constexpr auto pop_front() noexcept -> decltype(
            std::declval<D &>().emplace_front(
                std::declval<typename D::value_type &>()),
            (void)std::declval<D &>().erase(std::declval<D &>().begin()))
        {
            derived().erase(derived().begin());
        }

        template<
            typename D = Derived,
            typename Enable = std::enable_if_t<
                v1_dtl::decrementable_sentinel<D>::value &&
                v1_dtl::common_range<D>::value>>
        constexpr auto
        back() noexcept(noexcept(*std::prev(std::declval<D &>().end())))
            -> decltype(*std::prev(std::declval<D &>().end()))
        {
            return *std::prev(derived().end());
        }
        template<
            typename D = Derived,
            typename Enable = std::enable_if_t<
                v1_dtl::decrementable_sentinel<D>::value &&
                v1_dtl::common_range<D>::value>>
        constexpr auto back() const
            noexcept(noexcept(*std::prev(std::declval<D const &>().end())))
                -> decltype(*std::prev(std::declval<D const &>().end()))
        {
            return *std::prev(derived().end());
        }

        template<typename D = Derived>
        constexpr auto push_back(typename D::value_type const & x) noexcept(
            noexcept(std::declval<D &>().emplace_back(x)))
            -> decltype((void)std::declval<D &>().emplace_back(x))
        {
            derived().emplace_back(x);
        }

        template<typename D = Derived>
        constexpr auto push_back(typename D::value_type && x) noexcept(
            noexcept(std::declval<D &>().emplace_back(std::move(x))))
            -> decltype((void)std::declval<D &>().emplace_back(std::move(x)))
        {
            derived().emplace_back(std::move(x));
        }

        template<typename D = Derived>
        constexpr auto pop_back() noexcept -> decltype(
            std::declval<D &>().emplace_back(
                std::declval<typename D::value_type>()),
            (void)std::declval<D &>().erase(
                std::prev(std::declval<D &>().end())))
        {
            derived().erase(std::prev(derived().end()));
        }

        template<typename D = Derived>
        constexpr auto operator[](typename D::size_type n) noexcept(
            noexcept(std::declval<D &>().begin()[n]))
            -> decltype(std::declval<D &>().begin()[n])
        {
            return derived().begin()[n];
        }
        template<typename D = Derived>
        constexpr auto operator[](typename D::size_type n) const
            noexcept(noexcept(std::declval<D const &>().begin()[n]))
                -> decltype(std::declval<D const &>().begin()[n])
        {
            return derived().begin()[n];
        }

        template<typename D = Derived>
        constexpr auto at(typename D::size_type i)
            -> decltype(std::declval<D &>().size(), std::declval<D &>()[i])
        {
            if (derived().size() <= i) {
                throw std::out_of_range(
                    "Bounds check failed in sequence_container_interface::at()");
            }
            return derived()[i];
        }

        template<typename D = Derived>
        constexpr auto at(typename D::size_type i) const -> decltype(
            std::declval<D const &>().size(), std::declval<D const &>()[i])
        {
            if (derived().size() <= i) {
                throw std::out_of_range(
                    "Bounds check failed in sequence_container_interface::at()");
            }
            return derived()[i];
        }

        template<typename D = Derived, typename Iter = typename D::const_iterator>
        constexpr Iter begin() const
            noexcept(noexcept(std::declval<D &>().begin()))
        {
            return Iter(mutable_derived().begin());
        }
        template<typename D = Derived, typename Iter = typename D::const_iterator>
        constexpr Iter end() const noexcept(noexcept(std::declval<D &>().end()))
        {
            return Iter(mutable_derived().end());
        }

        template<typename D = Derived>
        constexpr auto cbegin() const
            noexcept(noexcept(std::declval<D const &>().begin()))
                -> decltype(std::declval<D const &>().begin())
        {
            return derived().begin();
        }
        template<typename D = Derived>
        constexpr auto cend() const
            noexcept(noexcept(std::declval<D const &>().end()))
                -> decltype(std::declval<D const &>().end())
        {
            return derived().end();
        }

        template<
            typename D = Derived,
            typename Enable = std::enable_if_t<v1_dtl::common_range<D>::value>>
        constexpr auto rbegin() noexcept(noexcept(
            stl_interfaces::make_reverse_iterator(std::declval<D &>().end())))
        {
            return stl_interfaces::make_reverse_iterator(derived().end());
        }
        template<
            typename D = Derived,
            typename Enable = std::enable_if_t<v1_dtl::common_range<D>::value>>
        constexpr auto rend() noexcept(noexcept(
            stl_interfaces::make_reverse_iterator(std::declval<D &>().begin())))
        {
            return stl_interfaces::make_reverse_iterator(derived().begin());
        }

        template<typename D = Derived>
        constexpr auto rbegin() const
            noexcept(noexcept(std::declval<D &>().rbegin()))
        {
            return
                typename D::const_reverse_iterator(mutable_derived().rbegin());
        }
        template<typename D = Derived>
        constexpr auto rend() const
            noexcept(noexcept(std::declval<D &>().rend()))
        {
            return typename D::const_reverse_iterator(mutable_derived().rend());
        }

        template<typename D = Derived>
        constexpr auto crbegin() const
            noexcept(noexcept(std::declval<D const &>().rbegin()))
                -> decltype(std::declval<D const &>().rbegin())
        {
            return derived().rbegin();
        }
        template<typename D = Derived>
        constexpr auto crend() const
            noexcept(noexcept(std::declval<D const &>().rend()))
                -> decltype(std::declval<D const &>().rend())
        {
            return derived().rend();
        }

        template<typename D = Derived>
        constexpr auto insert(
            typename D::const_iterator pos,
            typename D::value_type const &
                x) noexcept(noexcept(std::declval<D &>().emplace(pos, x)))
            -> decltype(std::declval<D &>().emplace(pos, x))
        {
            return derived().emplace(pos, x);
        }

        template<typename D = Derived>
        constexpr auto insert(
            typename D::const_iterator pos,
            typename D::value_type &&
                x) noexcept(noexcept(std::declval<D &>()
                                         .emplace(pos, std::move(x))))
            -> decltype(std::declval<D &>().emplace(pos, std::move(x)))
        {
            return derived().emplace(pos, std::move(x));
        }

        template<typename D = Derived>
        constexpr auto insert(
            typename D::const_iterator pos,
            typename D::size_type n,
            typename D::value_type const & x)
            // If you see an error in this noexcept() expression, that's
            // because this function is not properly constrained.  In other
            // words, Derived does not have a "range" insert like
            // insert(position, first, last).  If that is the case, this
            // function should be removed via SFINAE from overload resolution.
            // However, both the trailing decltype code below and a
            // std::enable_if in the template parameters do not work.  Sorry
            // about that.  See below for details.
            noexcept(noexcept(std::declval<D &>().insert(
                pos, detail::make_n_iter(x, n), detail::make_n_iter_end(x, n))))
        // This causes the compiler to infinitely recurse into this function's
        // declaration, even though the call below does not match the
        // signature of this function.
#if 0
            -> decltype(std::declval<D &>().insert(
                pos, detail::make_n_iter(x, n), detail::make_n_iter_end(x, n)))
#endif
        {
            return derived().insert(
                pos, detail::make_n_iter(x, n), detail::make_n_iter_end(x, n));
        }

        template<typename D = Derived>
        constexpr auto insert(
            typename D::const_iterator pos,
            std::initializer_list<typename D::value_type>
                il) noexcept(noexcept(std::declval<D &>()
                                          .insert(pos, il.begin(), il.end())))
            -> decltype(std::declval<D &>().insert(pos, il.begin(), il.end()))
        {
            return derived().insert(pos, il.begin(), il.end());
        }

        template<typename D = Derived>
        constexpr auto erase(typename D::const_iterator pos) noexcept
            -> decltype(std::declval<D &>().erase(pos, std::next(pos)))
        {
            return derived().erase(pos, std::next(pos));
        }

        template<
            typename InputIterator,
            typename D = Derived,
            typename Enable =
                std::enable_if_t<v1_dtl::in_iter<InputIterator>::value>>
        constexpr auto assign(InputIterator first, InputIterator last) noexcept(
            noexcept(std::declval<D &>().insert(
                std::declval<D &>().begin(), first, last)))
            -> decltype(
                std::declval<D &>().erase(
                    std::declval<D &>().begin(), std::declval<D &>().end()),
                (void)std::declval<D &>().insert(
                    std::declval<D &>().begin(), first, last))
        {
            auto out = derived().begin();
            auto const out_last = derived().end();
            for (; out != out_last && first != last; ++first, ++out) {
                *out = *first;
            }
            if (out != out_last)
                derived().erase(out, out_last);
            if (first != last)
                derived().insert(derived().end(), first, last);
        }

        template<typename D = Derived>
        constexpr auto assign(
            typename D::size_type n,
            typename D::value_type const &
                x) noexcept(noexcept(std::declval<D &>()
                                         .insert(
                                             std::declval<D &>().begin(),
                                             detail::make_n_iter(x, n),
                                             detail::make_n_iter_end(x, n))))
            -> decltype(
                std::declval<D &>().size(),
                std::declval<D &>().erase(
                    std::declval<D &>().begin(), std::declval<D &>().end()),
                (void)std::declval<D &>().insert(
                    std::declval<D &>().begin(),
                    detail::make_n_iter(x, n),
                    detail::make_n_iter_end(x, n)))
        {
            if (detail::fake_capacity(derived()) < n) {
                Derived temp(n, x);
                derived().swap(temp);
            } else {
                auto const min_size =
                    std::min<std::ptrdiff_t>(n, derived().size());
                auto const fill_end =
                    std::fill_n(derived().begin(), min_size, x);
                if (min_size < (std::ptrdiff_t)derived().size()) {
                    derived().erase(fill_end, derived().end());
                } else {
                    n -= min_size;
                    derived().insert(
                        derived().begin(),
                        detail::make_n_iter(x, n),
                        detail::make_n_iter_end(x, n));
                }
            }
        }

        template<typename D = Derived>
        constexpr auto
        assign(std::initializer_list<typename D::value_type> il) noexcept(
            noexcept(std::declval<D &>().assign(il.begin(), il.end())))
            -> decltype((void)std::declval<D &>().assign(il.begin(), il.end()))
        {
            derived().assign(il.begin(), il.end());
        }

        template<typename D = Derived>
        constexpr auto
        operator=(std::initializer_list<typename D::value_type> il) noexcept(
            noexcept(std::declval<D &>().assign(il.begin(), il.end())))
            -> decltype(
                std::declval<D &>().assign(il.begin(), il.end()),
                std::declval<D &>())
        {
            derived().assign(il.begin(), il.end());
            return *this;
        }

        template<typename D = Derived>
        constexpr auto clear() noexcept
            -> decltype((void)std::declval<D &>().erase(
                std::declval<D &>().begin(), std::declval<D &>().end()))
        {
            derived().erase(derived().begin(), derived().end());
        }
    };

    /** Implementation of free function `swap()` for all containers derived
        from `sequence_container_interface`.  */
    template<typename ContainerInterface>
    constexpr auto swap(
        ContainerInterface & lhs,
        ContainerInterface & rhs) noexcept(noexcept(lhs.swap(rhs)))
        -> decltype(v1_dtl::derived_container(lhs), lhs.swap(rhs))
    {
        return lhs.swap(rhs);
    }

    /** Implementation of `operator==()` for all containers derived from
        `sequence_container_interface`.  */
    template<typename ContainerInterface>
    constexpr auto
    operator==(ContainerInterface const & lhs, ContainerInterface const & rhs) noexcept(
        noexcept(lhs.size() == rhs.size()) &&
        noexcept(*lhs.begin() == *rhs.begin()))
        -> decltype(
            v1_dtl::derived_container(lhs),
            lhs.size() == rhs.size(),
            *lhs.begin() == *rhs.begin(),
            true)
    {
        return lhs.size() == rhs.size() &&
               std::equal(lhs.begin(), lhs.end(), rhs.begin());
    }

    /** Implementation of `operator!=()` for all containers derived from
        `sequence_container_interface`.  */
    template<typename ContainerInterface>
    constexpr auto operator!=(
        ContainerInterface const & lhs,
        ContainerInterface const & rhs) noexcept(noexcept(lhs == rhs))
        -> decltype(v1_dtl::derived_container(lhs), lhs == rhs)
    {
        return !(lhs == rhs);
    }

    /** Implementation of `operator<()` for all containers derived from
        `sequence_container_interface`.  */
    template<typename ContainerInterface>
    constexpr auto operator<(
        ContainerInterface const & lhs,
        ContainerInterface const &
            rhs) noexcept(noexcept(*lhs.begin() < *rhs.begin()))
        -> decltype(
            v1_dtl::derived_container(lhs), *lhs.begin() < *rhs.begin(), true)
    {
        auto it1 = lhs.begin();
        auto const last1 = lhs.end();
        auto it2 = rhs.begin();
        auto const last2 = rhs.end();
        for (; it1 != last1 && it2 != last2; ++it1, ++it2) {
            if (*it1 < *it2)
                return true;
            if (*it2 < *it1)
                return false;
        }
        return it1 == last1 && it2 != last2;
    }

    /** Implementation of `operator<=()` for all containers derived from
        `sequence_container_interface`.  */
    template<typename ContainerInterface>
    constexpr auto operator<=(
        ContainerInterface const & lhs,
        ContainerInterface const & rhs) noexcept(noexcept(lhs < rhs))
        -> decltype(v1_dtl::derived_container(lhs), lhs < rhs)
    {
        return !(rhs < lhs);
    }

    /** Implementation of `operator>()` for all containers derived from
        `sequence_container_interface`.  */
    template<typename ContainerInterface>
    constexpr auto operator>(
        ContainerInterface const & lhs,
        ContainerInterface const & rhs) noexcept(noexcept(lhs < rhs))
        -> decltype(v1_dtl::derived_container(lhs), lhs < rhs)
    {
        return rhs < lhs;
    }

    /** Implementation of `operator>=()` for all containers derived from
        `sequence_container_interface`.  */
    template<typename ContainerInterface>
    constexpr auto operator>=(
        ContainerInterface const & lhs,
        ContainerInterface const & rhs) noexcept(noexcept(lhs < rhs))
        -> decltype(v1_dtl::derived_container(lhs), lhs < rhs)
    {
        return !(lhs < rhs);
    }

}}}

#if defined(BOOST_STL_INTERFACES_DOXYGEN) || BOOST_STL_INTERFACES_USE_CONCEPTS

namespace boost { namespace stl_interfaces { BOOST_STL_INTERFACES_NAMESPACE_V2 {

    namespace v2_dtl {

        // This needs to become an exposition-only snake-case template alias
        // when standardized.
        template<typename T>
        using container_size_t = typename T::size_type;

        template<typename T, typename I>
        // clang-format off
        concept range_insert =
            requires (T t, std::ranges::iterator_t<T> t_it, I it) {
            t.template insert<I>(t_it, it, it);
            // clang-format on
        };

        template<typename T>
        using n_iter_t =
            detail::n_iter<std::ranges::range_value_t<T>, container_size_t<T>>;
    }

    // clang-format off

    /** A CRTP template that one may derive from to make it easier to define
        container types.

        The template parameter `D` for `sequence_container_interface` may be
        an incomplete type. Before any member of the resulting specialization
        of `sequence_container_interface` other than special member functions
        is referenced, `D` shall be complete; shall model
        `std::derived_from<sequence_container_interface<D>>`,
        `std::semiregular`, and `std::forward_range`; and shall contain all
        the nested types required in Table 72: Container requirements and, for
        those whose iterator nested type models `std::bidirectinal_iterator`,
        those in Table 73: Reversible container requirements.

        For an object `d` of type `D`, a call to `std::ranges::begin(d)` shall
        not mutate any data members of `d`, and `d`'s destructor shall end the
        lifetimes of the objects in `[std::ranges::begin(d),
        std::ranges::end(d))`.

        The `Contiguity` template parameter is not needed, and is unused.  It
        only exists to make the transition from `namespace v1` to `namespace
        v2` seamless. */
    template<typename D,
             element_layout Contiguity = element_layout::discontiguous>
      requires std::is_class_v<D> && std::same_as<D, std::remove_cv_t<D>>
    struct sequence_container_interface
    {
    private:
      constexpr D& derived() noexcept {
        return static_cast<D&>(*this);
      }
      constexpr const D& derived() const noexcept {
        return static_cast<const D&>(*this);
      }
      constexpr D & mutable_derived() const noexcept {
        return const_cast<D&>(static_cast<const D&>(*this));
      }
      static constexpr void clear_impl(D& d) noexcept {}
      static constexpr void clear_impl(D& d) noexcept
        requires requires { d.clear(); }
      { d.clear(); }

    public:
      constexpr bool empty() const {
        return std::ranges::begin(derived()) == std::ranges::end(derived());
      }

      constexpr auto data() requires std::contiguous_iterator<std::ranges::iterator_t<D>> {
        return std::to_address(std::ranges::begin(derived()));
      }
      constexpr auto data() const requires std::contiguous_iterator<std::ranges::iterator_t<const D>> {
          return std::to_address(std::ranges::begin(derived()));
        }

      template<typename C = D>
      constexpr v2_dtl::container_size_t<C> size() const
        requires std::sized_sentinel_for<std::ranges::sentinel_t<const C>, std::ranges::iterator_t<const C>> {
          return v2_dtl::container_size_t<C>(
            std::ranges::end(derived()) - std::ranges::begin(derived()));
        }

      constexpr decltype(auto) front() {
        BOOST_ASSERT(!empty());
        return *std::ranges::begin(derived());
      }
      constexpr decltype(auto) front() const {
        BOOST_ASSERT(!empty());
        return *std::ranges::begin(derived());
      }

      template<typename C = D>
        constexpr void push_front(const std::ranges::range_value_t<C>& x)
          requires requires (D d) { d.emplace_front(x); } {
            derived().emplace_front(x);
          }
      template<typename C = D>
        constexpr void push_front(std::ranges::range_value_t<C>&& x)
          requires requires (D d) { d.emplace_front(std::move(x)); } {
            derived().emplace_front(std::move(x));
          }
      constexpr void pop_front() noexcept
        requires requires (D d, const std::ranges::range_value_t<D>& x, std::ranges::iterator_t<D> position) {
          d.emplace_front(x);
          d.erase(position);
        } {
          return derived().erase(std::ranges::begin(derived()));
        }

      constexpr decltype(auto) back()
        requires std::ranges::bidirectional_range<D> && std::ranges::common_range<D> {
          BOOST_ASSERT(!empty());
          return *std::ranges::prev(std::ranges::end(derived()));
        }
      constexpr decltype(auto) back() const
        requires std::ranges::bidirectional_range<const D> && std::ranges::common_range<const D> {
          BOOST_ASSERT(!empty());
          return *std::ranges::prev(std::ranges::end(derived()));
        }

      template<std::ranges::bidirectional_range C = D>
        constexpr void push_back(const std::ranges::range_value_t<C>& x)
	requires std::ranges::common_range<C> && requires (D d) { d.emplace_back(x); } {
            derived().emplace_back(x);
          }
      template<std::ranges::bidirectional_range C = D>
        constexpr void push_back(std::ranges::range_value_t<C>&& x)
	requires std::ranges::common_range<C> && requires (D d) { d.emplace_back(std::move(x)); } {
            derived().emplace_back(std::move(x));
          }
      constexpr void pop_back() noexcept
        requires std::ranges::bidirectional_range<D> && std::ranges::common_range<D> &&
        requires (D d, std::ranges::range_value_t<D> x, std::ranges::iterator_t<D> position) {
          d.emplace_back(std::move(x));
            d.erase(position);
          } {
            return derived().erase(std::ranges::prev(std::ranges::end(derived())));
          }

      template<std::ranges::random_access_range C = D>
        constexpr decltype(auto) operator[](v2_dtl::container_size_t<C> n) {
          return std::ranges::begin(derived())[n];
        }
      template<std::ranges::random_access_range C = const D>
        constexpr decltype(auto) operator[](v2_dtl::container_size_t<C> n) const {
          return std::ranges::begin(derived())[n];
        }

      template<std::ranges::random_access_range C = D>
        constexpr decltype(auto) at(v2_dtl::container_size_t<C> n) {
          if (derived().size() <= n)
            throw std::out_of_range("Bounds check failed in sequence_container_interface::at()");
          return std::ranges::begin(derived())[n];
        }
      template<std::ranges::random_access_range C = const D>
        constexpr decltype(auto) at(v2_dtl::container_size_t<C> n) const {
          if (derived().size() <= n)
            throw std::out_of_range("Bounds check failed in sequence_container_interface::at()");
          return std::ranges::begin(derived())[n];
        }

      constexpr auto begin() const {
        return typename D::const_iterator(mutable_derived().begin());
      }
      constexpr auto end() const {
        return typename D::const_iterator(mutable_derived().end());
      }

      constexpr auto cbegin() const { return derived().begin(); }
      constexpr auto cend() const { return derived().end(); }

      constexpr auto rbegin()
        requires std::ranges::bidirectional_range<D> && std::ranges::common_range<D> {
          return std::reverse_iterator(std::ranges::end(derived()));
        }
      constexpr auto rend()
        requires std::ranges::bidirectional_range<D> && std::ranges::common_range<D> {
          return std::reverse_iterator(std::ranges::begin(derived()));
        }

      constexpr auto rbegin() const
        requires std::ranges::bidirectional_range<const D> &&
          std::ranges::common_range<const D> {
            return std::reverse_iterator(std::ranges::iterator_t<const D>(
              mutable_derived().end()));
          }
      constexpr auto rend() const
        requires std::ranges::bidirectional_range<const D> &&
          std::ranges::common_range<const D> {
            return std::reverse_iterator(std::ranges::iterator_t<const D>(
              mutable_derived().begin()));
          }

      constexpr auto crbegin() const
        requires std::ranges::bidirectional_range<const D> &&
          std::ranges::common_range<const D> {
            return std::reverse_iterator(std::ranges::iterator_t<const D>(
              mutable_derived().end()));
            }
      constexpr auto crend() const
        requires std::ranges::bidirectional_range<const D> &&
          std::ranges::common_range<const D> {
            return std::reverse_iterator(std::ranges::iterator_t<const D>(
              mutable_derived().begin()));
            }

      template<typename C = D>
        constexpr auto insert(std::ranges::iterator_t<const C> position,
                              const std::ranges::range_value_t<C>& x)
          requires requires (D d) { d.emplace(position, x); } {
            return derived().emplace(position, x);
          }
      template<typename C = D>
        constexpr auto insert(std::ranges::iterator_t<const C> position,
                              std::ranges::range_value_t<C>&& x)
          requires requires (D d) { d.emplace(position, std::move(x)); } {
            return derived().emplace(position, std::move(x));
          }
      template<typename C = D>
        constexpr auto insert(std::ranges::iterator_t<const C> position,
                              v2_dtl::container_size_t<C> n,
                              const std::ranges::range_value_t<C>& x)
          requires v2_dtl::range_insert<C, v2_dtl::n_iter_t<C>> {
            auto first = detail::make_n_iter(x, n);
            auto last = detail::make_n_iter_end(x, n);
            return derived().insert(
                position, detail::make_n_iter(x, n), detail::make_n_iter_end(x, n));
            }
      template<typename C = D>
        constexpr auto insert(std::ranges::iterator_t<const C> position,
                              std::initializer_list<std::ranges::range_value_t<C>> il)
	requires requires (D d) {
            d.template insert<decltype(position), decltype(il)>(
                position, il.begin(), il.end()); } {
              return derived().insert(position, il.begin(), il.end());
            }

      template<typename C = D>
        constexpr void erase(typename C::const_iterator position)
	requires requires (D d) { d.erase(position, std::ranges::next(position)); } {
            derived().erase(position, std::ranges::next(position));
          }

      template<std::input_iterator Iter, typename C = D>
        constexpr void assign(Iter first, Iter last)
	requires requires (D d) {
            d.erase(std::ranges::begin(d), std::ranges::end(d));
            d.insert(std::ranges::begin(d), first, last); } {
              auto out = derived().begin();
              auto const out_last = derived().end();
              for (; out != out_last && first != last; ++first, ++out) {
                *out = *first;
              }
              if (out != out_last)
                derived().erase(out, out_last);
              if (first != last)
                derived().insert(derived().end(), first, last);
            }
      template<typename C = D>
        constexpr void assign(v2_dtl::container_size_t<C> n,
                              const std::ranges::range_value_t<C>& x)
          requires requires (D d) {
            { d.size() } -> std::convertible_to<std::size_t>;
            d.erase(std::ranges::begin(d), std::ranges::end(d));
            d.insert(std::ranges::begin(d),
                             detail::make_n_iter(x, n),
                             detail::make_n_iter_end(x, n)); } {
              if (detail::fake_capacity(derived()) < n) {
                C temp(n, x);
                derived().swap(temp);
              } else {
                auto const min_size = std::min<std::ptrdiff_t>(n, derived().size());
                auto const fill_end = std::fill_n(derived().begin(), min_size, x);
                if (min_size < (std::ptrdiff_t)derived().size()) {
                  derived().erase(fill_end, derived().end());
                } else {
                  n -= min_size;
                  derived().insert(
                    derived().begin(),
                    detail::make_n_iter(x, n),
                    detail::make_n_iter_end(x, n));
                }
              }
            }
      template<typename C = D>
        constexpr void assign(std::initializer_list<std::ranges::range_value_t<C>> il)
          requires requires (D d) { d.assign(il.begin(), il.end()); } {
            derived().assign(il.begin(), il.end());
          }

      constexpr void clear() noexcept
        requires requires (D d) {
          d.erase(std::ranges::begin(d), std::ranges::end(d)); } {
            derived().erase(std::ranges::begin(derived()), std::ranges::end(derived()));
          }

      template<typename C = D>
        constexpr decltype(auto) operator=(
            std::initializer_list<std::ranges::range_value_t<C>> il)
          requires requires (D d) { d.assign(il.begin(), il.end()); } {
            derived().assign(il.begin(), il.end());
            return *this;
          }

      friend constexpr void swap(D& lhs, D& rhs) requires requires { lhs.swap(rhs); } {
        return lhs.swap(rhs);
      }

      friend constexpr bool operator==(const D& lhs, const D& rhs)
        requires std::ranges::sized_range<const D> &&
          requires { std::ranges::equal(lhs, rhs); } {
            return lhs.size() == rhs.size() && std::ranges::equal(lhs, rhs);
          }
#if 0 // TODO: This appears to work, but as of this writing (and using GCC
      // 10), op<=> is not yet being used to evaluate op==, op<, etc.
      friend constexpr std::compare_three_way_result_t<std::ranges::range_reference_t<const D>>
        operator<=>(const D& lhs, const D& rhs)
        requires std::three_way_comparable<std::ranges::range_reference_t<const D>> {
          return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(),
                                                        rhs.begin(), rhs.end());
        }
#else
      friend constexpr bool operator!=(const D& lhs, const D& rhs)
        requires requires { lhs == rhs; } {
          return !(lhs == rhs);
        }
      friend constexpr bool operator<(D lhs, D rhs)
        requires std::totally_ordered<std::ranges::range_reference_t<D>> {
          return std::ranges::lexicographical_compare(lhs, rhs);
        }
      friend constexpr bool operator<=(D lhs, D rhs)
        requires std::totally_ordered<std::ranges::range_reference_t<D>> {
            return lhs == rhs || lhs < rhs;
          }
      friend constexpr bool operator>(D lhs, D rhs)
        requires std::totally_ordered<std::ranges::range_reference_t<D>> {
          return !(lhs <= rhs);
        }
      friend constexpr bool operator>=(D lhs, D rhs)
        requires std::totally_ordered<std::ranges::range_reference_t<D>> {
            return rhs <= lhs;
          }
#endif
    };

    // clang-format on

}}}

#endif

#endif
