Skip to the content.

:heavy_check_mark: view/concat.hpp

Depends on

Required by

Verified with

Code

#pragma once


#include <cassert>
#include <iterator>
#include <utility>
#include <tuple>
#include <type_traits>
#include <functional>
#include <ranges>


#include "internal/dev_env.hpp"
#include "internal/types.hpp"
#include "internal/iterator.hpp"
#include "internal/type_traits.hpp"
#include "internal/ranges.hpp"



namespace uni {

namespace internal {

namespace view_impl {


template<std::ranges::input_range V0, std::ranges::input_range V1>
    requires std::ranges::view<V0> && std::ranges::view<V1>
struct concat_view : std::ranges::view_interface<concat_view<V0, V1>> {
  private:
    V0 _b0;
    V1 _b1;

    template<bool Const> using B0 = internal::maybe_const_t<Const, V0>;
    template<bool Const> using B1 = internal::maybe_const_t<Const, V1>;

    template<bool Const> struct iterator_tag {};

    template<bool Const>
        requires std::ranges::forward_range<B0<Const>> && std::ranges::forward_range<B1<Const>>
    struct iterator_tag<Const> {
      public:
        using iterator_category = uni::internal::most_primitive_iterator_tag<
            typename std::iterator_traits<std::ranges::iterator_t<B0<Const>>>::iterator_category,
            typename std::iterator_traits<std::ranges::iterator_t<B1<Const>>>::iterator_category
        >;
    };

  public:
    template<bool> class iterator;

    constexpr explicit concat_view(V0 v0, V1 v1) noexcept(NO_EXCEPT)
      : _b0(std::move(v0)), _b1(std::move(v1))
    {}

    inline constexpr std::pair<V0, V1> base() const & noexcept(NO_EXCEPT)
        requires std::copy_constructible<V0> && std::copy_constructible<V0>
    {
        return { this->_b0, this->_b1 };
    }

    inline constexpr std::pair<V0,V1> base() && noexcept(NO_EXCEPT) {
        return { std::move(this->_b0), std::move(this->_b1) };
    }

    inline constexpr auto begin() noexcept(NO_EXCEPT)
        requires (!internal::simple_view<V0> && !internal::simple_view<V1>)
    {
        return iterator<false>(this, std::ranges::begin(this->_b0), std::ranges::begin(this->_b1), 0);
    }

    inline constexpr auto begin() const noexcept(NO_EXCEPT)
        requires std::ranges::range<const V0> && std::ranges::range<const V1>
    {
        return iterator<true>(this, std::ranges::begin(this->_b0), std::ranges::begin(this->_b1), 0);
    }

    inline constexpr auto end() noexcept(NO_EXCEPT)
        requires (!internal::simple_view<V0> && !internal::simple_view<V1>)
    {
        if constexpr(std::ranges::common_range<V0> && std::ranges::common_range<V1>) {
            return iterator<false>(this, std::ranges::end(this->_b0), std::ranges::end(this->_b1), 1);
        }
        else {
            return std::default_sentinel;
        }
    }

    inline constexpr auto end() const noexcept(NO_EXCEPT)
        requires std::ranges::range<const V0> && std::ranges::range<const V1>
    {
        if constexpr(std::ranges::common_range<const V0> && std::ranges::common_range<const V1>) {
            return iterator<true>(this, std::ranges::end(this->_b0), std::ranges::end(this->_b1), 1);
        }
        else {
            return std::default_sentinel;
        }
    }

    inline constexpr auto size() noexcept(NO_EXCEPT)
        requires std::ranges::sized_range<V0> && std::ranges::sized_range<V1>
    {
        return static_cast<std::size_t>(std::ranges::distance(this->_b0) + std::ranges::distance(this->_b1));
    }

    inline constexpr auto size() const noexcept(NO_EXCEPT)
        requires std::ranges::sized_range<const V0> && std::ranges::sized_range<const V1>
    {
        return static_cast<std::size_t>(std::ranges::distance(this->_b0) + std::ranges::distance(this->_b1));
    }
};

template<std::ranges::input_range V0, std::ranges::input_range V1>
    requires std::ranges::view<V0> && std::ranges::view<V1>
template<bool Const>
struct concat_view<V0, V1>::iterator : iterator_tag<Const> {
  private:
    using Parent = internal::maybe_const_t<Const, concat_view>;

    using B0 = concat_view::B0<Const>;
    using B1 = concat_view::B1<Const>;

    std::ranges::iterator_t<B0> _c0 = std::ranges::iterator_t<B0>();
    std::ranges::iterator_t<B0> _b0 = std::ranges::iterator_t<B0>();
    std::ranges::sentinel_t<B0> _e0 = std::ranges::sentinel_t<B0>();

    std::ranges::iterator_t<B1> _c1 = std::ranges::iterator_t<B1>();
    std::ranges::iterator_t<B1> _b1 = std::ranges::iterator_t<B1>();
    std::ranges::sentinel_t<B1> _e1 = std::ranges::sentinel_t<B1>();

    int _block = 0;

    constexpr iterator(Parent *const parent, const std::ranges::iterator_t<B0> c0, const std::ranges::iterator_t<B1> c1, const int block) noexcept(NO_EXCEPT)
      : _c0(std::move(c0)), _b0(std::ranges::begin(parent->_b0)), _e0(std::ranges::end(parent->_b0)),
        _c1(std::move(c1)), _b1(std::ranges::begin(parent->_b1)), _e1(std::ranges::end(parent->_b1)),
        _block(block || std::ranges::empty(parent->_b0))
    {}

    friend concat_view;

  public:
    using difference_type = std::common_type_t<std::ranges::range_difference_t<B0>, std::ranges::range_difference_t<B1>>;

    using value_type = std::common_type_t<std::ranges::range_value_t<B0>, std::ranges::range_value_t<B1>>;
    using reference_type = std::common_reference_t<std::ranges::range_reference_t<B0>, std::ranges::range_reference_t<B1>>;

    using iterator_concept = most_primitive_iterator_concept<Const, V0, V1>;

    iterator() noexcept(NO_EXCEPT)
        requires std::default_initializable<std::ranges::iterator_t<B0>> &&
                 std::default_initializable<std::ranges::iterator_t<B0>>
    = default;

    constexpr iterator(iterator<!Const> itr) noexcept(NO_EXCEPT)
        requires
            Const &&
            std::convertible_to<std::ranges::iterator_t<V0>, std::ranges::iterator_t<B0>> &&
            std::convertible_to<std::ranges::sentinel_t<V0>, std::ranges::sentinel_t<B0>> &&
            std::convertible_to<std::ranges::iterator_t<V1>, std::ranges::iterator_t<B1>> &&
            std::convertible_to<std::ranges::sentinel_t<V1>, std::ranges::sentinel_t<B1>>
      : _c0(std::move(itr._c0)), _b0(std::move(itr._b0)), _e0(std::move(itr._e0)),
        _c1(std::move(itr._c0)), _b1(std::move(itr._b0)), _e1(std::move(itr._e1)),
        _block(itr._block)
    {}

    inline constexpr std::variant<std::ranges::iterator_t<B0>, std::ranges::iterator_t<B1>>
    base() && noexcept(NO_EXCEPT) {
        if(this->_block == 0) return std::move(this->_c0);
        else return std::move(this->_C1);
    }

    inline constexpr
        std::variant<
            std::reference_wrapper<const std::ranges::iterator_t<B0>>,
            std::reference_wrapper<const std::ranges::iterator_t<B1>>
        >
    base() const & noexcept {
        if(this->_block == 0) return std::move(this->_c0);
        else return std::move(this->_c1);
    }

    inline constexpr reference_type operator*() const noexcept(NO_EXCEPT)
    {

        if(this->_block == 0) return *this->_c0;
        else return *this->_c1;
    }

    inline constexpr iterator& operator++() noexcept(NO_EXCEPT)
    {
        assert(this->_c0 != this->_e0 or this->_c1 != this->_e1);

        if(this->_block == 0) {
            if(++this->_c0 == this->_e0) {
                this->_block = 1;
                assert(this->_c1 == this->_b1);
            }
        }
        else {
            ++this->_c1;
        }

        return *this;
    }

    inline constexpr void operator++(int) noexcept(NO_EXCEPT) { ++*this; }

    inline constexpr iterator operator++(int) noexcept(NO_EXCEPT)
        requires std::ranges::forward_range<B0> && std::ranges::forward_range<B1>
    {
        const auto res = *this; ++*this; return res;
    }

    inline constexpr iterator& operator--() noexcept(NO_EXCEPT)
        requires
            std::ranges::bidirectional_range<B0> && std::ranges::bidirectional_range<B1> &&
            std::bidirectional_iterator<std::ranges::sentinel_t<B0>>
    {
        if(this->_block == 1) {
            if(this->_c1 == this->_b1) {
                this->_block = 0;
                this->_c0 = std::ranges::prev(this->_e0);
            }
            else {
                --this->_c1;
            }
        }
        else {
            --this->_c0;
        }

        return *this;
    }

    inline constexpr iterator operator--(int) noexcept(NO_EXCEPT)
        requires std::ranges::bidirectional_range<B0> && std::ranges::bidirectional_range<B1>
    {
        const auto res = *this; --*this; return res;
    }

    inline constexpr iterator& operator+=(const difference_type diff) noexcept(NO_EXCEPT)
        requires
            std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        if(diff > 0) {
            if(this->_block == 0) {
                const auto missing = std::ranges::advance(this->_c0, diff, this->_e0);
                if(this->_c0 == this->_e0) {
                    this->_block = 1;
                    assert(this->_c1 == this->_b1);
                    std::ranges::advance(this->_c1, missing, this->_e1);
                }
            }
            else {
                std::ranges::advance(this->_c1, diff, this->_e1);
            }
        }
        if(diff < 0) {
            if(this->_block == 1) {
                const auto missing = std::ranges::advance(this->_c1, diff, this->_b1);
                if(missing < 0) {
                    this->_block = 0;
                    assert(this->_c0 == this->_e0);
                    std::ranges::advance(this->_c0, missing, this->_b0);
                }
            }
            else {
                std::ranges::advance(this->_c0, diff, this->_b0);
            }
        }

        return *this;
    }

    inline constexpr iterator& operator-=(const difference_type diff) noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        return *this += -diff;
    }

    inline constexpr decltype(auto) operator[](const difference_type diff) const noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        return *(*this + diff);
    }

    friend inline constexpr bool operator==(const iterator& lhs, std::default_sentinel_t) noexcept(NO_EXCEPT)
    {
        if(lhs._block == 0) return false;
        if(lhs._block == 1) return lhs._c1 == lhs._e1;
        assert(false);
    }

    friend inline constexpr bool operator==(const iterator& lhs, const iterator& rhs) noexcept(NO_EXCEPT)
        requires
            std::equality_comparable<std::ranges::iterator_t<B0>> &&
            std::equality_comparable<std::ranges::iterator_t<B1>>
    {
        if(lhs._block != rhs._block) return false;
        return lhs._block == 0 ? lhs._c0 == rhs._c0 : lhs._c1 == rhs._c1;
    }

    friend inline constexpr auto operator<=>(const iterator& lhs, const iterator& rhs) noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        if(lhs._block != rhs._block) return lhs._block <=> rhs._block;
        return lhs._block == 0 ? lhs._c0 <=> rhs._c0 : lhs._c1 <=> rhs._c1;
    }

    friend inline constexpr iterator operator+(const iterator& itr, const difference_type diff) noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        auto res = itr; res += diff; return res;
    }

    friend inline constexpr iterator operator+(const difference_type diff, const iterator& itr) noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        return itr + diff;
    }

    friend inline constexpr iterator operator-(const iterator& itr, const difference_type diff) noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        auto res = itr; res -= diff; return res;
    }

    friend inline constexpr const difference_type operator-(const iterator& lhs, const iterator& rhs) noexcept(NO_EXCEPT)
        requires
            std::sized_sentinel_for<std::ranges::iterator_t<B0>, std::ranges::iterator_t<B0>> &&
            std::sized_sentinel_for<std::ranges::iterator_t<B1>, std::ranges::iterator_t<B1>>
    {
        if(lhs._block == rhs._block) {
            return lhs._block == 0 ? std::ranges::distance(rhs._c0, lhs._c0) : std::ranges::distance(rhs._c1, lhs._c1);
        }
        if(lhs._block > rhs._block) return std::ranges::distance(rhs._c0, rhs._e0) + std::ranges::distance(lhs._b1, lhs._c1);
        if(lhs._block < rhs._block) return -(rhs - lhs);
        assert(false);
    }

    friend inline constexpr const difference_type operator-(std::default_sentinel_t, const iterator& rhs) noexcept(NO_EXCEPT)
        requires
            std::sized_sentinel_for<std::ranges::sentinel_t<B0>, std::ranges::iterator_t<B0>> &&
            std::sized_sentinel_for<std::ranges::sentinel_t<B1>, std::ranges::iterator_t<B1>>
    {
        if(rhs._block == 0) return std::ranges::distance(rhs._c0, rhs._e0) + std::ranges::distance(rhs._b1, rhs._e1);
        if(rhs._block == 1) return std::ranges::distance(rhs._c1, rhs._e1);
        assert(false);
    }

    friend inline constexpr const difference_type operator-(const iterator& lhs, std::default_sentinel_t rhs) noexcept(NO_EXCEPT)
        requires
            std::sized_sentinel_for<std::ranges::sentinel_t<B0>, std::ranges::iterator_t<B0>> &&
            std::sized_sentinel_for<std::ranges::sentinel_t<B1>, std::ranges::iterator_t<B1>>
    {
        return -(rhs - lhs);
    }

    friend inline constexpr
    std::common_reference_t<
        std::ranges::range_rvalue_reference_t<B0>,
        std::ranges::range_rvalue_reference_t<B1>
    >
    iter_move(const iterator& itr) noexcept(NO_EXCEPT)
    {
        if(itr._block == 0) return std::ranges::iter_move(itr._c0);
        if(itr._block == 1) return std::ranges::iter_move(itr._c1);
        assert(false);
    }

    friend inline constexpr void iter_swap(const iterator& lhs, const iterator& rhs) noexcept(NO_EXCEPT)
        requires
            std::indirectly_swappable<std::ranges::iterator_t<B0>> &&
            std::indirectly_swappable<std::ranges::iterator_t<B1>> &&
            std::indirectly_swappable<std::ranges::iterator_t<B0>, std::ranges::iterator_t<B1>>
    {
        if(lhs._block == 0 && rhs._block == 0) std::ranges::iter_swap(lhs._c0, rhs._c0);
        if(lhs._block == 0 && rhs._block == 1) std::ranges::iter_swap(lhs._c0, rhs._c1);
        if(lhs._block == 1 && rhs._block == 0) std::ranges::iter_swap(lhs._c1, rhs._c0);
        if(lhs._block == 1 && rhs._block == 1) std::ranges::iter_swap(lhs._c1, rhs._c1);
        assert(false);
    }
};


} // namespace view_impl

} // namespace internal


template<class...> struct concat_view;

template<class T>
struct concat_view<T> : std::views::all_t<T> {
    using std::views::all_t<T>::all_t;
};

template<class T0, class T1>
struct concat_view<T0, T1> : internal::view_impl::concat_view<std::views::all_t<T0>, std::views::all_t<T1>> {
    explicit concat_view(T0&& v0, T1&& v1) noexcept(NO_EXCEPT)
      : internal::view_impl::concat_view<std::views::all_t<T0>, std::views::all_t<T1>>(std::forward<T0>(v0), std::forward<T1>(v1))
    {}
};

template<class T0, class T1, class... Ts>
struct concat_view<T0, T1, Ts...> : concat_view<concat_view<T0, T1>, Ts...> {
    explicit concat_view(T0&& v0, T1&& v1, Ts&&... vs) noexcept(NO_EXCEPT)
      : concat_view<concat_view<T0, T1>, Ts...>(
            concat_view<T0, T1>(std::forward<T0>(v0), std::forward<T1>(v1)), std::forward<Ts>(vs)...
        )
    {}
};

namespace views {

namespace internal {


template<class... Ts>
concept can_concat_view = requires { concat_view<Ts...>(std::declval<Ts>()...); };


} // namespace internal


struct Concat {
    template<class... Ts>
        requires (sizeof...(Ts) == 0 || internal::can_concat_view<Ts...>)
    inline constexpr auto operator() [[nodiscard]] (Ts&&... vs) const {
        if constexpr(sizeof...(Ts) == 0) return std::views::empty<std::nullptr_t>;
        else return concat_view<std::views::all_t<Ts>...>(std::forward<Ts>(vs)...);
    }
};


inline constexpr Concat concat;


} // namespace views

} // namespace uni.


namespace std::ranges {


template<class... Views>
inline constexpr bool enable_borrowed_range<uni::concat_view<Views...>> = (enable_borrowed_range<Views> && ...);


}
#line 2 "view/concat.hpp"


#include <cassert>
#include <iterator>
#include <utility>
#include <tuple>
#include <type_traits>
#include <functional>
#include <ranges>


#line 2 "internal/dev_env.hpp"


#ifdef LOCAL_JUDGE
    inline constexpr bool DEV_ENV = true;
    inline constexpr bool NO_EXCEPT = false;
#else
    inline constexpr bool DEV_ENV = false;
    inline constexpr bool NO_EXCEPT = true;
#endif // LOCAL_JUDGE


#if __cplusplus >= 202100L
    #define CPP20 true
    #define CPP23 true
#elif __cplusplus >= 202002L
    #define CPP20 true
    #define CPP23 false
#else
    #define CPP20 false
    #define CPP23 false
#endif
#line 2 "internal/types.hpp"

#include <cstdint>

namespace uni {

namespace internal {


using size_t = std::int64_t;

using int128_t = __int128_t;
using uint128_t = __uint128_t;


} // namesapce internal

} // namespace uni
#line 2 "internal/iterator.hpp"


#line 7 "internal/iterator.hpp"
#include <variant>
#include <compare>
#line 10 "internal/iterator.hpp"


#line 13 "internal/iterator.hpp"

#line 2 "internal/type_traits.hpp"


#include <iostream>
#include <vector>
#line 7 "internal/type_traits.hpp"
#include <algorithm>
#line 9 "internal/type_traits.hpp"


#line 12 "internal/type_traits.hpp"


namespace uni {

namespace internal {


template<class... Ts> struct tuple_or_pair { using type = std::tuple<Ts...>; };
template<class T, class U> struct tuple_or_pair<T,U> { using type = std::pair<T, U>; };
template <class... Ts> using tuple_or_pair_t = typename tuple_or_pair<Ts...>::type;

template<class T>
constexpr std::underlying_type_t<T> to_underlying(const T& v) noexcept(NO_EXCEPT) {
    return static_cast<std::underlying_type_t<T>>(v);
}


template<class T, class... Ts>
using are_same = std::conjunction<std::is_same<T, Ts>...>;

template<class T, class... Ts>
inline constexpr bool are_same_v = are_same<T, Ts...>::value;


template<class T, class... Ts>
using is_same_as_any_of = std::disjunction<std::is_same<T, Ts>...>;

template<class T, class... Ts>
inline constexpr bool is_same_as_any_of_v = is_same_as_any_of<T, Ts...>::value;

template<class T, class... Ts>
concept same_as_any_of = is_same_as_any_of_v<T, Ts...>;


template<class Base, class... Derived>
using is_base_of_all = std::conjunction<std::is_base_of<Base, Derived>...>;

template<class Base, class... Derived>
inline constexpr bool is_base_of_all_v = is_base_of_all<Base, Derived...>::value;


template<class Base, class... Derived>
using is_base_of_any = std::disjunction<std::is_base_of<Base, Derived>...>;

template<class Base, class... Derived>
inline constexpr bool is_base_of_any_v = is_base_of_any<Base, Derived...>::value;


template<class T> struct remove_cvref {
  using type = typename std::remove_cv_t<std::remove_reference_t<T>>;
};

template<class T> using remove_cvref_t = typename remove_cvref<T>::type;


template<class T> struct literal_operator { static constexpr const char* value = ""; };

template<> struct literal_operator<unsigned> { static constexpr const char* value = "U"; };
template<> struct literal_operator<long> { static constexpr const char* value = "L"; };
template<> struct literal_operator<unsigned long> { static constexpr const char* value = "UL"; };
template<> struct literal_operator<long long> { static constexpr const char* value = "LL"; };
template<> struct literal_operator<unsigned long long> { static constexpr const char* value = "ULL"; };


template<> struct literal_operator<float> { static constexpr const char* value = "F"; };
template<> struct literal_operator<double> { static constexpr const char* value = "D"; };
template<> struct literal_operator<long double> { static constexpr const char* value = "LD"; };

#ifdef __SIZEOF_INT128__

template<> struct literal_operator<__int128_t> { static constexpr const char* value = "LLL"; };
template<> struct literal_operator<__uint128_t> { static constexpr const char* value = "ULLL"; };

#endif

template<class T> inline constexpr auto literal_operator_v = literal_operator<T>::value;


template <std::size_t N, typename... Types>
struct nth_type {};

template <class Head, class... Tail>
struct nth_type<0, Head, Tail...> {
	using type = Head;
};

template <std::size_t N, class Head, class... Tail>
struct nth_type<N, Head, Tail...> : public nth_type<N - 1, Tail...> {};

template <std::size_t N, typename... Types>
using nth_type_t = typename nth_type<N, Types...>::type;


template<template <class...> class, class> struct is_template_of : std::false_type {};
template<template <class...> class Template, class... Args> struct is_template_of<Template, Template<Args...>> : std::true_type {};

template<template <class...> class Template, class Type>
inline constexpr bool is_template_of_v = is_template_of<Template, Type>::value;

template<class Type, template <class...> class Template>
concept substituted_from = is_template_of_v<Template, Type>;

template<template <class...> class Base, class Derived>
struct _is_basic_tempalte_of
{
    template<class... Ts>
    static constexpr std::true_type  test(const Base<Ts...> *);

    static constexpr std::false_type test(...);

    using type = decltype(test(std::declval<Derived*>()));
};


template<template <class...> class Base, class Derived>
using is_basic_tempalte_of = _is_basic_tempalte_of<Base, Derived>::type;

template<template <class...> class Base, class Derived>
inline constexpr bool is_basic_tempalte_of_v = is_basic_tempalte_of<Base, Derived>::value;

template<class Derived, template <class...> class Base>
concept derived_from_template = is_basic_tempalte_of_v<Base, Derived>;


template<class T> struct is_loggable {
    template<class U>
    static constexpr auto External(U &&v) -> decltype(_debug(v), std::true_type());
    static constexpr std::false_type External(...);

    template<class U>
    static constexpr auto Member(U &&v) -> decltype(v._debug(), std::true_type());
    static constexpr std::false_type Member(...);

    static constexpr bool value = (
      decltype(External(std::declval<T>()))::value ||
      decltype(Member(std::declval<T>()))::value
    );

};

template<class T>
inline constexpr auto is_loggable_v = is_loggable<T>::value;

template<class T>
concept loggable = is_loggable_v<T>;


template<class T> struct _has_iterator {
    template<class U>
    static constexpr auto ADL(U &&v) -> decltype(begin(v), end(v), std::true_type());
    static constexpr std::false_type ADL(...);

    template<class U>
    static constexpr auto STL(U &&v) -> decltype(std::begin(v), std::end(v), std::true_type());
    static constexpr std::false_type STL(...);

    template<class U>
    static constexpr auto Member(U &&v) -> decltype(v.begin(), v.end(), std::true_type());
    static constexpr std::false_type Member(...);
};

template<class T> struct has_iterator {
    struct ADL : decltype(_has_iterator<T>::ADL(std::declval<T>())) {};
    struct STL : decltype(_has_iterator<T>::STL(std::declval<T>())) {};
    struct Member : decltype(_has_iterator<T>::Member(std::declval<T>())) {};

    static constexpr auto adl_v = ADL::value;
    static constexpr auto stl_v = STL::value;
    static constexpr auto member_v = Member::value;
};


template<class T>
struct is_iterable {
    static constexpr bool value =  has_iterator<T>::adl_v || has_iterator<T>::stl_v || has_iterator<T>::member_v;
};

template<class T>
inline constexpr auto is_iterable_v = is_iterable<T>::value;

template<class T>
concept iterable = is_iterable_v<T>;

namespace iterator_resolver {


template<class T>
inline constexpr auto begin(T&& v) noexcept(NO_EXCEPT) {
    static_assert(is_iterable_v<T>);
    if constexpr(has_iterator<T>::member_v) {
        return v.begin();
    }
    else {  // ADL
        using std::begin;
        return begin(std::forward<T>(v));
    }
}

template<class T>
inline constexpr auto end(T&& v) noexcept(NO_EXCEPT) {
    static_assert(is_iterable_v<T>);
    if constexpr(has_iterator<T>::member_v) {
        return v.end();
    }
    else {  // ADL
        using std::end;
        return end(std::forward<T>(v));
    }
}


};


template<class C> using iterator_t = decltype(iterator_resolver::begin(std::declval<C&>()));

template<class C> using container_size_t = decltype(std::size(std::declval<C&>()));


template<bool Const, class T>
using maybe_const_t = std::conditional_t<Const, const T, T>;


template<class T> using with_ref = T&;
template<class T> concept can_reference = requires { typename with_ref<T>; };


} // namespace internal

}  // namespace uni
#line 16 "internal/iterator.hpp"


namespace uni {

namespace internal {


template<class T>
struct iterator_interface {
    using iterator_category = std::output_iterator_tag;

    using difference_type = size_t;
    using value_type = T;

    using pointer = T*;
    using reference = T&;

    // virtual T operator*() const noexcept(NO_EXCEPT) { return 0; };
};

template<class T>
struct forward_iterator : iterator_interface<T> {
    using iterator_category = std::forward_iterator_tag;

    // virtual bidirectional_iterator_interface& operator++() = 0;
};

template<class T>
struct bidirectional_iterator_interface : forward_iterator<T> {
    using iterator_category = std::bidirectional_iterator_tag;

    // virtual bidirectional_iterator_interface& operator--() = 0;
};

template<class T>
struct random_access_iterator_base : bidirectional_iterator_interface<T> {
    using iterator_category = std::random_access_iterator_tag;
    using difference_type = typename bidirectional_iterator_interface<T>::difference_type;

  public:
    // virtual random_access_iterator_base& operator+=(const difference_type count) = 0;
    // virtual random_access_iterator_base& operator-=(const difference_type count) = 0;

    friend inline random_access_iterator_base operator+(random_access_iterator_base itr, const difference_type count) noexcept(NO_EXCEPT) { return itr += count, itr; }
    friend inline random_access_iterator_base operator-(random_access_iterator_base itr, const difference_type count) noexcept(NO_EXCEPT) { return itr -= count, itr; }

};

template<class T, class Container, class Derived>
struct container_iterator_interface : random_access_iterator_base<T> {
    using difference_type = std::make_signed_t<typename Container::size_type>;

  private:
    using derived = std::remove_cvref_t<Derived>;

    Container* _ref;
    difference_type _pos;

    static_assert(std::three_way_comparable<difference_type>);

    inline auto* _derived() noexcept(NO_EXCEPT) {
        return static_cast<derived*>(this);
    }
    inline const auto* _derived() const noexcept(NO_EXCEPT) {
        return static_cast<const derived*>(this);
    }

  public:
    container_iterator_interface() noexcept = default;
    container_iterator_interface(Container *const ref, const difference_type pos) noexcept(NO_EXCEPT) : _ref(ref), _pos(pos) {}

    inline auto ref() const noexcept(NO_EXCEPT) { return this->_ref; }

    inline auto pos() const noexcept(NO_EXCEPT) { return this->_pos; }
    inline auto& pos() { return this->_pos; }

    inline auto& operator++() noexcept(NO_EXCEPT) { return ++this->_pos, *this->_derived(); }
    inline auto& operator--() noexcept(NO_EXCEPT) { return --this->_pos, *this->_derived(); }

    inline auto operator++(int) noexcept(NO_EXCEPT) { auto res = *this->_derived(); return ++this->_pos, res; }
    inline auto operator--(int) noexcept(NO_EXCEPT) { auto res = *this->_derived(); return --this->_pos, res; }

    inline auto& operator+=(const difference_type count) noexcept(NO_EXCEPT) { return this->_pos += count, *this->_derived(); }
    inline auto& operator-=(const difference_type count) noexcept(NO_EXCEPT) { return this->_pos -= count, *this->_derived(); }

    inline auto operator*() const noexcept(NO_EXCEPT) { return this->ref()->get(this->_pos); }
    inline auto operator[](const difference_type count) const noexcept(NO_EXCEPT) { return *(*this->_derived() + count); }

    inline auto operator-(const derived& other) const noexcept(NO_EXCEPT) { return this->_pos - other._pos; }


    friend inline bool operator==(const derived& lhs, const derived& rhs) noexcept(NO_EXCEPT) {
        if(lhs.ref() == rhs.ref()) return lhs._pos == rhs._pos;
        return false;
    }

    friend inline std::partial_ordering operator<=>(const derived& lhs, const derived& rhs) noexcept(NO_EXCEPT) {
        if(lhs.ref() != rhs.ref()) return std::partial_ordering::unordered;
        return lhs._pos <=> rhs._pos;
    }
};


namespace iterator_impl {


template<class... Tags>
using is_all_random_access_iterator = is_base_of_all<std::random_access_iterator_tag,Tags...>;

template<class... Tags>
using is_all_bidirectional_iterator = is_base_of_all<std::bidirectional_iterator_tag,Tags...>;

template<class... Tags>
using is_all_forward_iterator = is_base_of_all<std::forward_iterator_tag,Tags...>;

template<class... Tags>
using is_all_input_iterator = is_base_of_all<std::input_iterator_tag,Tags...>;


template<class... Tags>
constexpr auto _most_primitive_iterator_tag() {
    if constexpr(is_all_random_access_iterator<Tags...>::value) {
        return std::random_access_iterator_tag{};
    }
    else if constexpr(is_all_bidirectional_iterator<Tags...>::value) {
        return std::bidirectional_iterator_tag{};
    }
    else if constexpr(is_all_forward_iterator<Tags...>::value) {
        return std::forward_iterator_tag{};
    }
    else {
        return std::input_iterator_tag{};
    }
}


} // namespace iterator_impl


template<class... Tags>
using most_primitive_iterator_tag = decltype(iterator_impl::_most_primitive_iterator_tag<Tags...>());


template<class T, class = void>
struct is_iterator {
   static constexpr bool value = false;
};

template<class T>
struct is_iterator<T, typename std::enable_if<!std::is_same<typename std::iterator_traits<T>::value_type, void>::value>::type> {
   static constexpr bool value = true;
};

template<class T>
inline constexpr bool is_iterator_v = is_iterator<T>::value;

template<class T>
using is_iterator_t = std::enable_if_t<is_iterator_v<T>>;

template<class T>
using iota_diff_t = std::make_signed_t<T>;


} // namespace internal

} // namespace uni
#line 2 "internal/ranges.hpp"


#line 5 "internal/ranges.hpp"
#include <concepts>
#line 7 "internal/ranges.hpp"


#line 11 "internal/ranges.hpp"


namespace uni {

namespace internal {


template<class Range>
concept resizable_range
  = std::ranges::range<Range> &&
    requires (Range& r) { r.resize(0); };

template<class range>
concept simple_view
  = std::ranges::view<range> && std::ranges::range<const range> &&
    std::same_as<std::ranges::iterator_t<range>, std::ranges::iterator_t<const range>> &&
    std::same_as<std::ranges::sentinel_t<range>, std::ranges::sentinel_t<const range>>;


template<class... Ranges>
concept zip_is_common = (sizeof...(Ranges) == 1 && (std::ranges::common_range<Ranges> && ...))
    || (!(std::ranges::bidirectional_range<Ranges> && ...) && (std::ranges::common_range<Ranges> && ...))
    || ((std::ranges::random_access_range<Ranges> && ...) && (std::ranges::sized_range<Ranges> && ...));


template<bool Const, class... Views>
concept all_contiguous = (std::ranges::contiguous_range<maybe_const_t<Const, Views>> && ...);

template<bool Const, class... Views>
concept all_random_access = (std::ranges::random_access_range<maybe_const_t<Const, Views>> && ...);

template<bool Const, class... Views>
concept all_bidirectional = (std::ranges::bidirectional_range<maybe_const_t<Const, Views>> && ...);

template<bool Const, class... Views>
concept all_forward = (std::ranges::forward_range<maybe_const_t<Const, Views>> && ...);


template<bool Const, class... Views> struct zip_view_iterator_category {};

template<bool Const, class... Views>
    requires all_forward<Const, Views...>
struct zip_view_iterator_category<Const, Views...> {
    using iterator_category = std::input_iterator_tag;
};


template<bool Const, class... Views>
static auto _most_primitive_iterator_concept() noexcept(NO_EXCEPT) {
    if constexpr(all_random_access<Const, Views...>)
        return std::random_access_iterator_tag{};
    else if constexpr(all_bidirectional<Const, Views...>)
        return std::bidirectional_iterator_tag{};
    else if constexpr(all_forward<Const, Views...>)
        return std::forward_iterator_tag{};
    else
        return std::input_iterator_tag{};
}

template<bool Const, class... Views>
using most_primitive_iterator_concept = decltype(_most_primitive_iterator_concept<Const, Views...>());


template<class Range, bool Const>
using range_iterator_category = typename std::iterator_traits<
        std::ranges::iterator_t<maybe_const_t<Const, Range>>
    >::iterator_category;



template<class Range>
static constexpr auto _iterator_concept() noexcept(NO_EXCEPT) {
    if constexpr(std::ranges::random_access_range<Range>)
        return std::random_access_iterator_tag{};
    else if constexpr(std::ranges::bidirectional_range<Range>)
        return std::bidirectional_iterator_tag{};
    else if constexpr(std::ranges::forward_range<Range>)
        return std::forward_iterator_tag{};
    else
        return std::input_iterator_tag{};
}

template<class Range>
using iterator_concept = decltype(_iterator_concept<Range>());



template<std::ranges::range Range> struct cached_position {
    constexpr bool has_value() const { return false; }

    constexpr std::ranges::iterator_t<Range> get(const Range&) const {
        __builtin_unreachable();
    }

    constexpr void set(const Range &, const std::ranges::iterator_t<Range> &) const {}
};

template<std::ranges::forward_range Range>
struct cached_position<Range> : protected std::optional<std::ranges::iterator_t<Range>> {
    using std::optional<std::ranges::iterator_t<Range>>::optioanl;
    using std::optional<std::ranges::iterator_t<Range>>::has_value;

    constexpr std::ranges::iterator_t<Range> get(const Range&) const {
        assert(this->has_value());
        return **this;
    }

    constexpr void set(const Range&, const std::ranges::iterator_t<Range>& itr) {
        assert(!this->has_value());
        this->emplace(*itr);
    }
};


template<std::ranges::random_access_range Range>
    requires(sizeof(std::ranges::range_difference_t<Range>) <= sizeof(std::ranges::iterator_t<Range>))
struct cached_position<Range> {
  private:
    std::ranges::range_difference_t<Range> _offset = -1;

  public:
    cached_position() = default;

    constexpr cached_position(const cached_position &) = default;

    constexpr cached_position(cached_position &&other) noexcept {
        *this = std::move(other);
    }

    constexpr cached_position &operator=(const cached_position &) = default;

    constexpr cached_position &operator=(cached_position &&other) noexcept {
        // Propagate the cached offset, but invalidate the source.
        this->_offset = other._offset;
        other._offset = -1;
        return *this;
    }

    constexpr bool has_value() const { return this->_offset >= 0; }

    constexpr std::ranges::iterator_t<Range> get(Range& range) const {
        assert(this->has_value());
        return std::ranges::begin(range) + this->_offset;
    }

    constexpr void set(Range &range, const std::ranges::iterator_t<Range> &itr) {
        assert(!this->has_value());
        this->_offset = itr - std::ranges::begin(range);
    }
};


template<typename T, int Disc>
struct absent { };

template<bool PRESENT, class T, int Disc = 0>
using maybe_present_t = std::conditional_t<PRESENT, T, absent<T, Disc>>;


} // namespace internal


namespace views::adaptor {


template<class Adaptor, class... Args>
concept adaptor_invocable = requires { std::declval<Adaptor>()(std::declval<Args>()...); };

template<class Adaptor, class... Args>
concept adaptor_partial_app_viable =
    (Adaptor::arity > 1) && (sizeof...(Args) == Adaptor::arity - 1) &&
    (std::constructible_from<std::remove_cvref_t<Args>, Args> && ...);

template<class Adaptor, class... Args> struct partial;

template<class, class> struct pipe;


template<class Derived> struct range_adaptor_closure {};


template<class T, class U>
    requires(!std::same_as<T, range_adaptor_closure<U>>)
void is_range_adaptor_closure_fn(const T &, const range_adaptor_closure<U> &);


template<class T>
concept is_range_adaptor_closure = requires(T t) { adaptor::is_range_adaptor_closure_fn(t, t); };


template<class Self, class Range>
    requires is_range_adaptor_closure<Self> && adaptor_invocable<Self, Range>
constexpr auto operator|(Range&& range, Self&& self) {
    return std::forward<Self>(self)(std::forward<Range>(range));
}


template<class Lhs, class Rhs>
    requires is_range_adaptor_closure<Lhs> && is_range_adaptor_closure<Rhs>
constexpr auto operator|(Lhs&& lhs, Rhs&& rhs) {
    return pipe<std::remove_cvref_t<Lhs>, std::remove_cvref_t<Rhs>>{ std::forward<Lhs>(lhs), std::forward<Rhs>(rhs)};
}


template<class Derived> struct range_adaptor {
    template<class... Args>
        requires adaptor_partial_app_viable<Derived, Args...>
    inline constexpr auto operator()(Args&& ..._args) const noexcept(NO_EXCEPT) {
        return partial<Derived, std::remove_cvref_t<Args>...>{
            std::forward<Args>(_args)...
        };
    }
};

template<class Adaptor>
concept closure_has_simple_call_op = Adaptor::has_simple_call_op;

template<class Adaptor, class... Args>
concept adaptor_has_simple_extra_args =
    Adaptor::has_simple_extra_args ||
    Adaptor::template has_simple_extra_args<Args...>;

template<class Adaptor, class... Args>
struct partial : range_adaptor_closure<partial<Adaptor, Args...>> {
    std::tuple<Args...> args;

    constexpr partial(Args... _args) noexcept(NO_EXCEPT) : args(std::move(_args)...) {}

    template<class Range>
        requires adaptor_invocable<Adaptor, Range, const Args &...>
    inline constexpr auto operator()(Range&& range) const & noexcept(NO_EXCEPT) {
        const auto forwarder = [&range](const auto &..._args) constexpr noexcept(NO_EXCEPT) {
            return Adaptor{}(std::forward<Range>(range), _args...);
        };
        return std::apply(forwarder, this->args);
    }

    template<class Range>
        requires adaptor_invocable<Adaptor, Range, Args...>
    inline constexpr auto operator()(Range&& range) && noexcept(NO_EXCEPT) {
        const auto forwarder = [&range](auto &..._args) constexpr noexcept(NO_EXCEPT) {
            return Adaptor{}(std::forward<Range>(range), std::move(_args)...);
        };
        return std::apply(forwarder, this->args);
    }

    template<class Range>
    inline constexpr auto operator()(Range&& range) const && = delete;
};

template<class Adaptor, class Arg>
struct partial<Adaptor, Arg> : range_adaptor_closure<partial<Adaptor, Arg>> {
    Arg arg;

    constexpr partial(Arg _arg) noexcept(NO_EXCEPT) : arg(std::move(_arg)) {}

    template<class Range>
        requires adaptor_invocable<Adaptor, Range, const Arg &>
    inline constexpr auto operator()(Range&& range) const & noexcept(NO_EXCEPT) {
        return Adaptor{}(std::forward<Range>(range), this->arg);
    }

    template<class Range>
        requires adaptor_invocable<Adaptor, Range, Arg>
    inline constexpr auto operator()(Range&& range) && noexcept(NO_EXCEPT) {
        return Adaptor{}(std::forward<Range>(range), std::move(this->arg));
    }

    template<class Range>
    inline constexpr auto operator()(Range&& range) const && = delete;
};

template<class Adaptor, class... Args>
    requires adaptor_has_simple_extra_args<Adaptor, Args...> && (std::is_trivially_copyable_v<Args> && ...)
struct partial<Adaptor, Args...> : range_adaptor_closure<partial<Adaptor, Args...>> {
    std::tuple<Args...> args;

    constexpr partial(Args... _args) noexcept(NO_EXCEPT) : args(std::move(_args)...) {}

    template<class Range>
        requires adaptor_invocable<Adaptor, Range, const Args &...>
    inline constexpr auto operator()(Range&& range) const noexcept(NO_EXCEPT) {
        const auto forwarder = [&range](const auto &..._args) constexpr noexcept(NO_EXCEPT) {
            return Adaptor{}(std::forward<Range>(range), _args...);
        };
        return std::apply(forwarder, this->args);
    }

    static constexpr bool has_simple_call_op = true;
};

template<class Adaptor, class Arg>
    requires adaptor_has_simple_extra_args<Adaptor, Arg> &&
             std::is_trivially_copyable_v<Arg>
struct partial<Adaptor, Arg> : range_adaptor_closure<partial<Adaptor, Arg>> {
    Arg arg;

    constexpr partial(Arg _arg) noexcept(NO_EXCEPT) : arg(std::move(_arg)) {}

    template<class Range>
        requires adaptor_invocable<Adaptor, Range, const Arg &>
    inline constexpr auto operator()(Range&& range) const noexcept(NO_EXCEPT) {
        return Adaptor{}(std::forward<Range>(range), this->arg);
    }

    static constexpr bool has_simple_call_op = true;
};

template<class Lhs, class Rhs, class Range>
concept pipe_invocable = requires {
    std::declval<Rhs>()(std::declval<Lhs>()(std::declval<Range>()));
};

template<class Lhs, class Rhs> struct pipe : range_adaptor_closure<pipe<Lhs, Rhs>> {
    [[no_unique_address]] Lhs lhs;
    [[no_unique_address]] Rhs rhs;

    constexpr pipe(Lhs _lhs, Rhs _rhs) noexcept(NO_EXCEPT) : lhs(std::move(_lhs)), rhs(std::move(_rhs)) {}

    template<class Range>
        requires pipe_invocable<const Lhs &, const Rhs &, Range>
    inline constexpr auto operator()(Range&& range) const & noexcept(NO_EXCEPT) {
        return rhs(lhs(std::forward<Range>(range)));
    }

    template<class Range>
        requires pipe_invocable<Lhs, Rhs, Range>
    inline constexpr auto operator()(Range&& range) && noexcept(NO_EXCEPT) {
        return std::move(rhs)(std::move(lhs)(std::forward<Range>(range)));
    }

    template<class Range>
    inline constexpr auto operator()(Range&& range) const && = delete;
};


template<class Lhs, class Rhs>
    requires closure_has_simple_call_op<Lhs> && closure_has_simple_call_op<Rhs>
struct pipe<Lhs, Rhs> : range_adaptor_closure<pipe<Lhs, Rhs>> {
    [[no_unique_address]] Lhs lhs;
    [[no_unique_address]] Rhs rhs;

    constexpr pipe(Lhs _lhs, Rhs _rhs) noexcept(NO_EXCEPT) : lhs(std::move(_lhs)), rhs(std::move(_rhs)) {}

    template<class Range>
        requires pipe_invocable<const Lhs &, const Rhs &, Range>
    inline constexpr auto operator()(Range&& range) const noexcept(NO_EXCEPT) {
        return rhs(lhs(std::forward<Range>(range)));
    }

    static constexpr bool has_simple_call_op = true;
};


} // namespace views::adaptor


} // namespace uni
#line 18 "view/concat.hpp"



namespace uni {

namespace internal {

namespace view_impl {


template<std::ranges::input_range V0, std::ranges::input_range V1>
    requires std::ranges::view<V0> && std::ranges::view<V1>
struct concat_view : std::ranges::view_interface<concat_view<V0, V1>> {
  private:
    V0 _b0;
    V1 _b1;

    template<bool Const> using B0 = internal::maybe_const_t<Const, V0>;
    template<bool Const> using B1 = internal::maybe_const_t<Const, V1>;

    template<bool Const> struct iterator_tag {};

    template<bool Const>
        requires std::ranges::forward_range<B0<Const>> && std::ranges::forward_range<B1<Const>>
    struct iterator_tag<Const> {
      public:
        using iterator_category = uni::internal::most_primitive_iterator_tag<
            typename std::iterator_traits<std::ranges::iterator_t<B0<Const>>>::iterator_category,
            typename std::iterator_traits<std::ranges::iterator_t<B1<Const>>>::iterator_category
        >;
    };

  public:
    template<bool> class iterator;

    constexpr explicit concat_view(V0 v0, V1 v1) noexcept(NO_EXCEPT)
      : _b0(std::move(v0)), _b1(std::move(v1))
    {}

    inline constexpr std::pair<V0, V1> base() const & noexcept(NO_EXCEPT)
        requires std::copy_constructible<V0> && std::copy_constructible<V0>
    {
        return { this->_b0, this->_b1 };
    }

    inline constexpr std::pair<V0,V1> base() && noexcept(NO_EXCEPT) {
        return { std::move(this->_b0), std::move(this->_b1) };
    }

    inline constexpr auto begin() noexcept(NO_EXCEPT)
        requires (!internal::simple_view<V0> && !internal::simple_view<V1>)
    {
        return iterator<false>(this, std::ranges::begin(this->_b0), std::ranges::begin(this->_b1), 0);
    }

    inline constexpr auto begin() const noexcept(NO_EXCEPT)
        requires std::ranges::range<const V0> && std::ranges::range<const V1>
    {
        return iterator<true>(this, std::ranges::begin(this->_b0), std::ranges::begin(this->_b1), 0);
    }

    inline constexpr auto end() noexcept(NO_EXCEPT)
        requires (!internal::simple_view<V0> && !internal::simple_view<V1>)
    {
        if constexpr(std::ranges::common_range<V0> && std::ranges::common_range<V1>) {
            return iterator<false>(this, std::ranges::end(this->_b0), std::ranges::end(this->_b1), 1);
        }
        else {
            return std::default_sentinel;
        }
    }

    inline constexpr auto end() const noexcept(NO_EXCEPT)
        requires std::ranges::range<const V0> && std::ranges::range<const V1>
    {
        if constexpr(std::ranges::common_range<const V0> && std::ranges::common_range<const V1>) {
            return iterator<true>(this, std::ranges::end(this->_b0), std::ranges::end(this->_b1), 1);
        }
        else {
            return std::default_sentinel;
        }
    }

    inline constexpr auto size() noexcept(NO_EXCEPT)
        requires std::ranges::sized_range<V0> && std::ranges::sized_range<V1>
    {
        return static_cast<std::size_t>(std::ranges::distance(this->_b0) + std::ranges::distance(this->_b1));
    }

    inline constexpr auto size() const noexcept(NO_EXCEPT)
        requires std::ranges::sized_range<const V0> && std::ranges::sized_range<const V1>
    {
        return static_cast<std::size_t>(std::ranges::distance(this->_b0) + std::ranges::distance(this->_b1));
    }
};

template<std::ranges::input_range V0, std::ranges::input_range V1>
    requires std::ranges::view<V0> && std::ranges::view<V1>
template<bool Const>
struct concat_view<V0, V1>::iterator : iterator_tag<Const> {
  private:
    using Parent = internal::maybe_const_t<Const, concat_view>;

    using B0 = concat_view::B0<Const>;
    using B1 = concat_view::B1<Const>;

    std::ranges::iterator_t<B0> _c0 = std::ranges::iterator_t<B0>();
    std::ranges::iterator_t<B0> _b0 = std::ranges::iterator_t<B0>();
    std::ranges::sentinel_t<B0> _e0 = std::ranges::sentinel_t<B0>();

    std::ranges::iterator_t<B1> _c1 = std::ranges::iterator_t<B1>();
    std::ranges::iterator_t<B1> _b1 = std::ranges::iterator_t<B1>();
    std::ranges::sentinel_t<B1> _e1 = std::ranges::sentinel_t<B1>();

    int _block = 0;

    constexpr iterator(Parent *const parent, const std::ranges::iterator_t<B0> c0, const std::ranges::iterator_t<B1> c1, const int block) noexcept(NO_EXCEPT)
      : _c0(std::move(c0)), _b0(std::ranges::begin(parent->_b0)), _e0(std::ranges::end(parent->_b0)),
        _c1(std::move(c1)), _b1(std::ranges::begin(parent->_b1)), _e1(std::ranges::end(parent->_b1)),
        _block(block || std::ranges::empty(parent->_b0))
    {}

    friend concat_view;

  public:
    using difference_type = std::common_type_t<std::ranges::range_difference_t<B0>, std::ranges::range_difference_t<B1>>;

    using value_type = std::common_type_t<std::ranges::range_value_t<B0>, std::ranges::range_value_t<B1>>;
    using reference_type = std::common_reference_t<std::ranges::range_reference_t<B0>, std::ranges::range_reference_t<B1>>;

    using iterator_concept = most_primitive_iterator_concept<Const, V0, V1>;

    iterator() noexcept(NO_EXCEPT)
        requires std::default_initializable<std::ranges::iterator_t<B0>> &&
                 std::default_initializable<std::ranges::iterator_t<B0>>
    = default;

    constexpr iterator(iterator<!Const> itr) noexcept(NO_EXCEPT)
        requires
            Const &&
            std::convertible_to<std::ranges::iterator_t<V0>, std::ranges::iterator_t<B0>> &&
            std::convertible_to<std::ranges::sentinel_t<V0>, std::ranges::sentinel_t<B0>> &&
            std::convertible_to<std::ranges::iterator_t<V1>, std::ranges::iterator_t<B1>> &&
            std::convertible_to<std::ranges::sentinel_t<V1>, std::ranges::sentinel_t<B1>>
      : _c0(std::move(itr._c0)), _b0(std::move(itr._b0)), _e0(std::move(itr._e0)),
        _c1(std::move(itr._c0)), _b1(std::move(itr._b0)), _e1(std::move(itr._e1)),
        _block(itr._block)
    {}

    inline constexpr std::variant<std::ranges::iterator_t<B0>, std::ranges::iterator_t<B1>>
    base() && noexcept(NO_EXCEPT) {
        if(this->_block == 0) return std::move(this->_c0);
        else return std::move(this->_C1);
    }

    inline constexpr
        std::variant<
            std::reference_wrapper<const std::ranges::iterator_t<B0>>,
            std::reference_wrapper<const std::ranges::iterator_t<B1>>
        >
    base() const & noexcept {
        if(this->_block == 0) return std::move(this->_c0);
        else return std::move(this->_c1);
    }

    inline constexpr reference_type operator*() const noexcept(NO_EXCEPT)
    {

        if(this->_block == 0) return *this->_c0;
        else return *this->_c1;
    }

    inline constexpr iterator& operator++() noexcept(NO_EXCEPT)
    {
        assert(this->_c0 != this->_e0 or this->_c1 != this->_e1);

        if(this->_block == 0) {
            if(++this->_c0 == this->_e0) {
                this->_block = 1;
                assert(this->_c1 == this->_b1);
            }
        }
        else {
            ++this->_c1;
        }

        return *this;
    }

    inline constexpr void operator++(int) noexcept(NO_EXCEPT) { ++*this; }

    inline constexpr iterator operator++(int) noexcept(NO_EXCEPT)
        requires std::ranges::forward_range<B0> && std::ranges::forward_range<B1>
    {
        const auto res = *this; ++*this; return res;
    }

    inline constexpr iterator& operator--() noexcept(NO_EXCEPT)
        requires
            std::ranges::bidirectional_range<B0> && std::ranges::bidirectional_range<B1> &&
            std::bidirectional_iterator<std::ranges::sentinel_t<B0>>
    {
        if(this->_block == 1) {
            if(this->_c1 == this->_b1) {
                this->_block = 0;
                this->_c0 = std::ranges::prev(this->_e0);
            }
            else {
                --this->_c1;
            }
        }
        else {
            --this->_c0;
        }

        return *this;
    }

    inline constexpr iterator operator--(int) noexcept(NO_EXCEPT)
        requires std::ranges::bidirectional_range<B0> && std::ranges::bidirectional_range<B1>
    {
        const auto res = *this; --*this; return res;
    }

    inline constexpr iterator& operator+=(const difference_type diff) noexcept(NO_EXCEPT)
        requires
            std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        if(diff > 0) {
            if(this->_block == 0) {
                const auto missing = std::ranges::advance(this->_c0, diff, this->_e0);
                if(this->_c0 == this->_e0) {
                    this->_block = 1;
                    assert(this->_c1 == this->_b1);
                    std::ranges::advance(this->_c1, missing, this->_e1);
                }
            }
            else {
                std::ranges::advance(this->_c1, diff, this->_e1);
            }
        }
        if(diff < 0) {
            if(this->_block == 1) {
                const auto missing = std::ranges::advance(this->_c1, diff, this->_b1);
                if(missing < 0) {
                    this->_block = 0;
                    assert(this->_c0 == this->_e0);
                    std::ranges::advance(this->_c0, missing, this->_b0);
                }
            }
            else {
                std::ranges::advance(this->_c0, diff, this->_b0);
            }
        }

        return *this;
    }

    inline constexpr iterator& operator-=(const difference_type diff) noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        return *this += -diff;
    }

    inline constexpr decltype(auto) operator[](const difference_type diff) const noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        return *(*this + diff);
    }

    friend inline constexpr bool operator==(const iterator& lhs, std::default_sentinel_t) noexcept(NO_EXCEPT)
    {
        if(lhs._block == 0) return false;
        if(lhs._block == 1) return lhs._c1 == lhs._e1;
        assert(false);
    }

    friend inline constexpr bool operator==(const iterator& lhs, const iterator& rhs) noexcept(NO_EXCEPT)
        requires
            std::equality_comparable<std::ranges::iterator_t<B0>> &&
            std::equality_comparable<std::ranges::iterator_t<B1>>
    {
        if(lhs._block != rhs._block) return false;
        return lhs._block == 0 ? lhs._c0 == rhs._c0 : lhs._c1 == rhs._c1;
    }

    friend inline constexpr auto operator<=>(const iterator& lhs, const iterator& rhs) noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        if(lhs._block != rhs._block) return lhs._block <=> rhs._block;
        return lhs._block == 0 ? lhs._c0 <=> rhs._c0 : lhs._c1 <=> rhs._c1;
    }

    friend inline constexpr iterator operator+(const iterator& itr, const difference_type diff) noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        auto res = itr; res += diff; return res;
    }

    friend inline constexpr iterator operator+(const difference_type diff, const iterator& itr) noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        return itr + diff;
    }

    friend inline constexpr iterator operator-(const iterator& itr, const difference_type diff) noexcept(NO_EXCEPT)
        requires std::ranges::random_access_range<B0> && std::ranges::random_access_range<B1>
    {
        auto res = itr; res -= diff; return res;
    }

    friend inline constexpr const difference_type operator-(const iterator& lhs, const iterator& rhs) noexcept(NO_EXCEPT)
        requires
            std::sized_sentinel_for<std::ranges::iterator_t<B0>, std::ranges::iterator_t<B0>> &&
            std::sized_sentinel_for<std::ranges::iterator_t<B1>, std::ranges::iterator_t<B1>>
    {
        if(lhs._block == rhs._block) {
            return lhs._block == 0 ? std::ranges::distance(rhs._c0, lhs._c0) : std::ranges::distance(rhs._c1, lhs._c1);
        }
        if(lhs._block > rhs._block) return std::ranges::distance(rhs._c0, rhs._e0) + std::ranges::distance(lhs._b1, lhs._c1);
        if(lhs._block < rhs._block) return -(rhs - lhs);
        assert(false);
    }

    friend inline constexpr const difference_type operator-(std::default_sentinel_t, const iterator& rhs) noexcept(NO_EXCEPT)
        requires
            std::sized_sentinel_for<std::ranges::sentinel_t<B0>, std::ranges::iterator_t<B0>> &&
            std::sized_sentinel_for<std::ranges::sentinel_t<B1>, std::ranges::iterator_t<B1>>
    {
        if(rhs._block == 0) return std::ranges::distance(rhs._c0, rhs._e0) + std::ranges::distance(rhs._b1, rhs._e1);
        if(rhs._block == 1) return std::ranges::distance(rhs._c1, rhs._e1);
        assert(false);
    }

    friend inline constexpr const difference_type operator-(const iterator& lhs, std::default_sentinel_t rhs) noexcept(NO_EXCEPT)
        requires
            std::sized_sentinel_for<std::ranges::sentinel_t<B0>, std::ranges::iterator_t<B0>> &&
            std::sized_sentinel_for<std::ranges::sentinel_t<B1>, std::ranges::iterator_t<B1>>
    {
        return -(rhs - lhs);
    }

    friend inline constexpr
    std::common_reference_t<
        std::ranges::range_rvalue_reference_t<B0>,
        std::ranges::range_rvalue_reference_t<B1>
    >
    iter_move(const iterator& itr) noexcept(NO_EXCEPT)
    {
        if(itr._block == 0) return std::ranges::iter_move(itr._c0);
        if(itr._block == 1) return std::ranges::iter_move(itr._c1);
        assert(false);
    }

    friend inline constexpr void iter_swap(const iterator& lhs, const iterator& rhs) noexcept(NO_EXCEPT)
        requires
            std::indirectly_swappable<std::ranges::iterator_t<B0>> &&
            std::indirectly_swappable<std::ranges::iterator_t<B1>> &&
            std::indirectly_swappable<std::ranges::iterator_t<B0>, std::ranges::iterator_t<B1>>
    {
        if(lhs._block == 0 && rhs._block == 0) std::ranges::iter_swap(lhs._c0, rhs._c0);
        if(lhs._block == 0 && rhs._block == 1) std::ranges::iter_swap(lhs._c0, rhs._c1);
        if(lhs._block == 1 && rhs._block == 0) std::ranges::iter_swap(lhs._c1, rhs._c0);
        if(lhs._block == 1 && rhs._block == 1) std::ranges::iter_swap(lhs._c1, rhs._c1);
        assert(false);
    }
};


} // namespace view_impl

} // namespace internal


template<class...> struct concat_view;

template<class T>
struct concat_view<T> : std::views::all_t<T> {
    using std::views::all_t<T>::all_t;
};

template<class T0, class T1>
struct concat_view<T0, T1> : internal::view_impl::concat_view<std::views::all_t<T0>, std::views::all_t<T1>> {
    explicit concat_view(T0&& v0, T1&& v1) noexcept(NO_EXCEPT)
      : internal::view_impl::concat_view<std::views::all_t<T0>, std::views::all_t<T1>>(std::forward<T0>(v0), std::forward<T1>(v1))
    {}
};

template<class T0, class T1, class... Ts>
struct concat_view<T0, T1, Ts...> : concat_view<concat_view<T0, T1>, Ts...> {
    explicit concat_view(T0&& v0, T1&& v1, Ts&&... vs) noexcept(NO_EXCEPT)
      : concat_view<concat_view<T0, T1>, Ts...>(
            concat_view<T0, T1>(std::forward<T0>(v0), std::forward<T1>(v1)), std::forward<Ts>(vs)...
        )
    {}
};

namespace views {

namespace internal {


template<class... Ts>
concept can_concat_view = requires { concat_view<Ts...>(std::declval<Ts>()...); };


} // namespace internal


struct Concat {
    template<class... Ts>
        requires (sizeof...(Ts) == 0 || internal::can_concat_view<Ts...>)
    inline constexpr auto operator() [[nodiscard]] (Ts&&... vs) const {
        if constexpr(sizeof...(Ts) == 0) return std::views::empty<std::nullptr_t>;
        else return concat_view<std::views::all_t<Ts>...>(std::forward<Ts>(vs)...);
    }
};


inline constexpr Concat concat;


} // namespace views

} // namespace uni.


namespace std::ranges {


template<class... Views>
inline constexpr bool enable_borrowed_range<uni::concat_view<Views...>> = (enable_borrowed_range<Views> && ...);


}
Back to top page