#include "view/concat.hpp"
#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> && ...); }