/* Ranges for libostd. * * This file is part of libostd. See COPYING.md for futher information. */ #ifndef OSTD_RANGE_HH #define OSTD_RANGE_HH #include #include #include #include #include #include #include #include #include namespace ostd { struct input_range_tag {}; struct output_range_tag {}; struct forward_range_tag: input_range_tag {}; struct bidirectional_range_tag: forward_range_tag {}; struct random_access_range_tag: bidirectional_range_tag {}; struct finite_random_access_range_tag: random_access_range_tag {}; struct contiguous_range_tag: finite_random_access_range_tag {}; namespace detail { template struct range_category_test { template static char test(typename RR::range_category *); template static int test(...); static constexpr bool value = (sizeof(test(0)) == sizeof(char)); }; template constexpr bool test_range_category = range_category_test::value; template struct range_traits_base {}; template struct range_traits_base { using range_category = typename R::range_category; using size_type = typename R::size_type; using value_type = typename R::value_type; using reference = typename R::reference; using difference_type = typename R::difference_type; }; template struct range_traits_impl {}; template struct range_traits_impl: range_traits_base< R, std::is_convertible_v || std::is_convertible_v > {}; } template struct range_traits: detail::range_traits_impl> {}; template using range_category_t = typename range_traits::range_category; template using range_size_t = typename range_traits::size_type; template using range_value_t = typename range_traits::value_type; template using range_reference_t = typename range_traits::reference; template using range_difference_t = typename range_traits::difference_type; // is input range namespace detail { template constexpr bool is_input_range_core = std::is_convertible_v, input_range_tag>; template> constexpr bool is_input_range_base = false; template constexpr bool is_input_range_base = detail::is_input_range_core; } template constexpr bool is_input_range = detail::is_input_range_base; // is forward range namespace detail { template constexpr bool is_forward_range_core = std::is_convertible_v, forward_range_tag>; template> constexpr bool is_forward_range_base = false; template constexpr bool is_forward_range_base = detail::is_forward_range_core; } template constexpr bool is_forward_range = detail::is_forward_range_base; // is bidirectional range namespace detail { template constexpr bool is_bidirectional_range_core = std::is_convertible_v, bidirectional_range_tag>; template> constexpr bool is_bidirectional_range_base = false; template constexpr bool is_bidirectional_range_base = detail::is_bidirectional_range_core; } template constexpr bool is_bidirectional_range = detail::is_bidirectional_range_base; // is random access range namespace detail { template constexpr bool is_random_access_range_core = std::is_convertible_v, random_access_range_tag>; template> constexpr bool is_random_access_range_base = false; template constexpr bool is_random_access_range_base = detail::is_random_access_range_core; } template constexpr bool is_random_access_range = detail::is_random_access_range_base; // is finite random access range namespace detail { template constexpr bool is_finite_random_access_range_core = std::is_convertible_v, finite_random_access_range_tag>; template> constexpr bool is_finite_random_access_range_base = false; template constexpr bool is_finite_random_access_range_base = detail::is_finite_random_access_range_core; } template constexpr bool is_finite_random_access_range = detail::is_finite_random_access_range_base; // is infinite random access range template constexpr bool is_infinite_random_access_range = is_random_access_range && !is_finite_random_access_range; // is contiguous range namespace detail { template constexpr bool is_contiguous_range_core = std::is_convertible_v, contiguous_range_tag>; template> constexpr bool is_contiguous_range_base = false; template constexpr bool is_contiguous_range_base = detail::is_contiguous_range_core; } template constexpr bool is_contiguous_range = detail::is_contiguous_range_base; // is output range namespace detail { template static std::true_type test_outrange(typename std::is_same< decltype(std::declval().put(std::declval())), void >::type *); template static std::false_type test_outrange(...); template constexpr bool output_range_test = decltype(test_outrange())::value; template constexpr bool is_output_range_core = std::is_convertible_v, output_range_tag> || ( is_input_range && ( output_range_test const &> || output_range_test &&> || output_range_test > ) ); template> constexpr bool is_output_range_base = false; template constexpr bool is_output_range_base = detail::is_output_range_core; } template constexpr bool is_output_range = detail::is_output_range_base; namespace detail { // range iterator template struct range_iterator { range_iterator() = delete; explicit range_iterator(T const &range): p_range(range) {} explicit range_iterator(T &&range): p_range(std::move(range)) {} range_iterator &operator++() { p_range.pop_front(); return *this; } range_reference_t operator*() const { return p_range.front(); } bool operator!=(std::nullptr_t) const { return !p_range.empty(); } private: std::decay_t p_range; }; } template struct reverse_range; template struct move_range; template struct enumerated_range; template struct take_range; template struct chunks_range; template struct join_range; template struct zip_range; template inline range_size_t range_pop_front_n(IR &range, range_size_t n) { if constexpr(std::is_convertible_v< range_category_t, finite_random_access_range_tag >) { auto rs = range.size(); n = std::min(n, rs); range = range.slice(n, rs); return n; } else { size_t r = 0; while (n-- && !range.empty()) { range.pop_front(); ++r; } return r; } } template inline range_size_t range_pop_back_n(IR &range, range_size_t n) { if constexpr(std::is_convertible_v< range_category_t, finite_random_access_range_tag >) { auto rs = range.size(); n = std::min(n, rs); range = range.slice(0, rs - n); return n; } else { size_t r = 0; while (n-- && !range.empty()) { range.pop_back(); ++r; } return r; } } template struct input_range { detail::range_iterator begin() const { return detail::range_iterator(*static_cast(this)); } std::nullptr_t end() const { return nullptr; } B iter() const { return B(*static_cast(this)); } reverse_range reverse() const { return reverse_range(iter()); } move_range movable() const { return move_range(iter()); } enumerated_range enumerate() const { return enumerated_range(iter()); } template take_range take(Size n) const { return take_range(iter(), n); } template chunks_range chunks(Size n) const { return chunks_range(iter(), n); } template join_range join(R1 r1, RR ...rr) const { return join_range(iter(), std::move(r1), std::move(rr)...); } template zip_range zip(R1 r1, RR ...rr) const { return zip_range(iter(), std::move(r1), std::move(rr)...); } auto operator*() const { return std::forward(this)->front())>( static_cast(this)->front() ); } B &operator++() { static_cast(this)->pop_front(); return *static_cast(this); } B operator++(int) { B tmp(*static_cast(this)); static_cast(this)->pop_front(); return tmp; } /* pipe op, must be a member to work for user ranges automagically */ template auto operator|(F &&func) & { return func(*static_cast(this)); } template auto operator|(F &&func) const & { return func(*static_cast(this)); } template auto operator|(F &&func) && { return func(std::move(*static_cast(this))); } template auto operator|(F &&func) const && { return func(std::move(*static_cast(this))); } /* universal bool operator */ explicit operator bool() const { return !(static_cast(this)->empty()); } }; template struct output_range { using range_category = output_range_tag; }; template inline void range_put_all(OR &orange, IR range) { for (; !range.empty(); range.pop_front()) { orange.put(range.front()); } } template struct noop_output_range: output_range> { using value_type = T; using reference = T &; using size_type = size_t; using difference_type = ptrdiff_t; void put(T const &) {} }; template struct counting_output_range: output_range> { using value_type = range_value_t; using reference = range_reference_t; using size_type = range_size_t; using difference_type = range_difference_t; private: R p_range; size_type p_written = 0; public: counting_output_range() = delete; counting_output_range(R const &range): p_range(range) {} void put(value_type const &v) { p_range.put(v); ++p_written; } void put(value_type &&v) { p_range.put(std::move(v)); ++p_written; } size_type get_written() const { return p_written; } }; template inline counting_output_range range_counter(R const &range) { return counting_output_range{range}; } inline auto reverse() { return [](auto &&obj) { return obj.reverse(); }; } inline auto movable() { return [](auto &&obj) { return obj.movable(); }; } inline auto enumerate() { return [](auto &&obj) { return obj.enumerate(); }; } template inline auto take(T n) { return [n](auto &&obj) { return obj.take(n); }; } template inline auto chunks(T n) { return [n](auto &&obj) { return obj.chunks(n); }; } template inline auto join(R &&range) { return [range = std::forward(range)](auto &&obj) mutable { return obj.join(std::forward(range)); }; } template inline auto join(R1 &&r1, R &&...rr) { return [ ranges = std::forward_as_tuple( std::forward(r1), std::forward(rr)... ) ] (auto &&obj) mutable { return std::apply([&obj](auto &&...args) mutable { return obj.join(std::forward(args)...); }, std::move(ranges)); }; } template inline auto zip(R &&range) { return [range = std::forward(range)](auto &&obj) mutable { return obj.zip(std::forward(range)); }; } template inline auto zip(R1 &&r1, R &&...rr) { return [ ranges = std::forward_as_tuple( std::forward(r1), std::forward(rr)... ) ] (auto &&obj) mutable { return std::apply([&obj](auto &&...args) mutable { return obj.zip(std::forward(args)...); }, std::move(ranges)); }; } namespace detail { template static std::true_type test_direct_iter(decltype(std::declval().iter()) *); template static std::false_type test_direct_iter(...); template constexpr bool direct_iter_test = decltype(test_direct_iter(0))::value; template static std::true_type test_std_iter( decltype(std::begin(std::declval())) *, decltype(std::end(std::declval())) * ); template static std::false_type test_std_iter(...); template constexpr bool std_iter_test = decltype(test_std_iter(0, 0))::value; /* direct iter and std iter; the case for std iter is * specialized after iterator_range is actually defined */ template struct ranged_traits_core {}; /* direct iter is available, regardless of std iter being available */ template struct ranged_traits_core { using range = decltype(std::declval().iter()); static range iter(C &r) { return r.iter(); } }; template struct ranged_traits_impl: ranged_traits_core< C, direct_iter_test, std_iter_test > {}; } template struct ranged_traits: detail::ranged_traits_impl {}; template inline auto iter(T &&r) -> decltype(ranged_traits>::iter(r)) { return ranged_traits>::iter(r); } template inline auto citer(T const &r) -> decltype(ranged_traits::iter(r)) { return ranged_traits::iter(r); } namespace detail { template static std::true_type test_iterable(decltype(ostd::iter(std::declval())) *); template static std::false_type test_iterable(...); template constexpr bool iterable_test = decltype(test_iterable(0))::value; } template struct reverse_range: input_range> { using range_category = std::common_type_t< range_category_t, finite_random_access_range_tag >; using value_type = range_value_t ; using reference = range_reference_t ; using size_type = range_size_t ; using difference_type = range_difference_t; private: T p_range; public: reverse_range() = delete; reverse_range(T const &range): p_range(range) {} reverse_range(reverse_range const &it): p_range(it.p_range) {} reverse_range(reverse_range &&it): p_range(std::move(it.p_range)) {} reverse_range &operator=(reverse_range const &v) { p_range = v.p_range; return *this; } reverse_range &operator=(reverse_range &&v) { p_range = std::move(v.p_range); return *this; } reverse_range &operator=(T const &v) { p_range = v; return *this; } reverse_range &operator=(T &&v) { p_range = std::move(v); return *this; } bool empty() const { return p_range.empty(); } size_type size() const { return p_range.size(); } void pop_front() { p_range.pop_back(); } void pop_back() { p_range.pop_front(); } reference front() const { return p_range.back(); } reference back() const { return p_range.front(); } reference operator[](size_type i) const { return p_range[size() - i - 1]; } reverse_range slice(size_type start, size_type end) const { size_type len = p_range.size(); return reverse_range{p_range.slice(len - end, len - start)}; } reverse_range slice(size_type start) const { return slice(start, size()); } }; template struct move_range: input_range> { using range_category = std::common_type_t< range_category_t, finite_random_access_range_tag >; using value_type = range_value_t ; using reference = range_value_t &&; using size_type = range_size_t ; using difference_type = range_difference_t; private: T p_range; public: move_range() = delete; move_range(T const &range): p_range(range) {} move_range(move_range const &it): p_range(it.p_range) {} move_range(move_range &&it): p_range(std::move(it.p_range)) {} move_range &operator=(move_range const &v) { p_range = v.p_range; return *this; } move_range &operator=(move_range &&v) { p_range = std::move(v.p_range); return *this; } move_range &operator=(T const &v) { p_range = v; return *this; } move_range &operator=(T &&v) { p_range = std::move(v); return *this; } bool empty() const { return p_range.empty(); } size_type size() const { return p_range.size(); } void pop_front() { p_range.pop_front(); } void pop_back() { p_range.pop_back(); } reference front() const { return std::move(p_range.front()); } reference back() const { return std::move(p_range.back()); } reference operator[](size_type i) const { return std::move(p_range[i]); } move_range slice(size_type start, size_type end) const { return move_range{p_range.slice(start, end)}; } move_range slice(size_type start) const { return slice(start, size()); } void put(value_type const &v) { p_range.put(v); } void put(value_type &&v) { p_range.put(std::move(v)); } }; template struct number_range: input_range> { using range_category = forward_range_tag; using value_type = T; using reference = T; using size_type = size_t; using difference_type = ptrdiff_t; number_range() = delete; number_range(T a, T b, T step = T(1)): p_a(a), p_b(b), p_step(step) {} number_range(T v): p_a(0), p_b(v), p_step(1) {} bool empty() const { return p_a * p_step >= p_b * p_step; } void pop_front() { p_a += p_step; } T front() const { return p_a; } private: T p_a, p_b, p_step; }; template inline number_range range(T a, T b, T step = T(1)) { return number_range(a, b, step); } template inline number_range range(T v) { return number_range(v); } template struct enumerated_value_t { S index; T value; }; template struct enumerated_range: input_range> { using range_category = std::common_type_t< range_category_t, forward_range_tag >; using value_type = range_value_t; using reference = enumerated_value_t< range_reference_t, range_size_t >; using size_type = range_size_t ; using difference_type = range_difference_t; private: T p_range; size_type p_index; public: enumerated_range() = delete; enumerated_range(T const &range): p_range(range), p_index(0) {} enumerated_range(enumerated_range const &it): p_range(it.p_range), p_index(it.p_index) {} enumerated_range(enumerated_range &&it): p_range(std::move(it.p_range)), p_index(it.p_index) {} enumerated_range &operator=(enumerated_range const &v) { p_range = v.p_range; p_index = v.p_index; return *this; } enumerated_range &operator=(enumerated_range &&v) { p_range = std::move(v.p_range); p_index = v.p_index; return *this; } enumerated_range &operator=(T const &v) { p_range = v; p_index = 0; return *this; } enumerated_range &operator=(T &&v) { p_range = std::move(v); p_index = 0; return *this; } bool empty() const { return p_range.empty(); } void pop_front() { p_range.pop_front(); ++p_index; } reference front() const { return reference{p_index, p_range.front()}; } }; template struct take_range: input_range> { using range_category = std::common_type_t< range_category_t, forward_range_tag >; using value_type = range_value_t ; using reference = range_reference_t ; using size_type = range_size_t ; using difference_type = range_difference_t; private: T p_range; size_type p_remaining; public: take_range() = delete; take_range(T const &range, range_size_t rem): p_range(range), p_remaining(rem) {} take_range(take_range const &it): p_range(it.p_range), p_remaining(it.p_remaining) {} take_range(take_range &&it): p_range(std::move(it.p_range)), p_remaining(it.p_remaining) {} take_range &operator=(take_range const &v) { p_range = v.p_range; p_remaining = v.p_remaining; return *this; } take_range &operator=(take_range &&v) { p_range = std::move(v.p_range); p_remaining = v.p_remaining; return *this; } bool empty() const { return (p_remaining <= 0) || p_range.empty(); } void pop_front() { p_range.pop_front(); if (p_remaining >= 1) { --p_remaining; } } reference front() const { return p_range.front(); } }; template struct chunks_range: input_range> { using range_category = std::common_type_t< range_category_t, forward_range_tag >; using value_type = take_range ; using reference = take_range ; using size_type = range_size_t ; using difference_type = range_difference_t; private: T p_range; size_type p_chunksize; public: chunks_range() = delete; chunks_range(T const &range, range_size_t chs): p_range(range), p_chunksize(chs) {} chunks_range(chunks_range const &it): p_range(it.p_range), p_chunksize(it.p_chunksize) {} chunks_range(chunks_range &&it): p_range(std::move(it.p_range)), p_chunksize(it.p_chunksize) {} chunks_range &operator=(chunks_range const &v) { p_range = v.p_range; p_chunksize = v.p_chunksize; return *this; } chunks_range &operator=(chunks_range &&v) { p_range = std::move(v.p_range); p_chunksize = v.p_chunksize; return *this; } bool empty() const { return p_range.empty(); } void pop_front() { range_pop_front_n(p_range, p_chunksize); } reference front() const { return p_range.take(p_chunksize); } }; namespace detail { template inline void join_range_pop(T &tup) { if constexpr(I != N) { if (!std::get(tup).empty()) { std::get(tup).pop_front(); return; } join_range_pop(tup); } } template inline auto join_range_front(T &tup) { if constexpr(I != N) { if (!std::get(tup).empty()) { return std::get(tup).front(); } return join_range_front(tup); } /* fallback, will probably throw */ return std::get<0>(tup).front(); } } template struct join_range: input_range> { using range_category = std::common_type_t< forward_range_tag, range_category_t... >; using value_type = std::common_type_t...>; using reference = std::common_type_t...>; using size_type = std::common_type_t...>; using difference_type = std::common_type_t...>; private: std::tuple p_ranges; public: join_range() = delete; join_range(R const &...ranges): p_ranges(ranges...) {} join_range(R &&...ranges): p_ranges(std::forward(ranges)...) {} join_range(join_range const &v): p_ranges(v.p_ranges) {} join_range(join_range &&v): p_ranges(std::move(v.p_ranges)) {} join_range &operator=(join_range const &v) { p_ranges = v.p_ranges; return *this; } join_range &operator=(join_range &&v) { p_ranges = std::move(v.p_ranges); return *this; } bool empty() const { return std::apply([](auto const &...args) { return (... && args.empty()); }, p_ranges); } void pop_front() { detail::join_range_pop<0, sizeof...(R)>(p_ranges); } reference front() const { return detail::join_range_front<0, sizeof...(R)>(p_ranges); } }; namespace detail { template struct zip_value_type { using type = std::tuple; }; template struct zip_value_type { using type = std::pair; }; template using zip_value_t = typename detail::zip_value_type::type; } template struct zip_range: input_range> { using range_category = std::common_type_t< forward_range_tag, range_category_t... >; using value_type = detail::zip_value_t...>; using reference = detail::zip_value_t...>; using size_type = std::common_type_t...>; using difference_type = std::common_type_t...>; private: std::tuple p_ranges; public: zip_range() = delete; zip_range(R const &...ranges): p_ranges(ranges...) {} zip_range(R &&...ranges): p_ranges(std::forward(ranges)...) {} zip_range(zip_range const &v): p_ranges(v.p_ranges) {} zip_range(zip_range &&v): p_ranges(std::move(v.p_ranges)) {} zip_range &operator=(zip_range const &v) { p_ranges = v.p_ranges; return *this; } zip_range &operator=(zip_range &&v) { p_ranges = std::move(v.p_ranges); return *this; } bool empty() const { return std::apply([](auto const &...args) { return (... || args.empty()); }, p_ranges); } void pop_front() { std::apply([](auto &...args) { (args.pop_front(), ...); }, p_ranges); } reference front() const { return std::apply([](auto &&...args) { return reference{args.front()...}; }, p_ranges); } }; template struct appender_range: output_range> { using value_type = typename T::value_type; using reference = typename T::reference; using size_type = typename T::size_type; using difference_type = typename T::difference_type; appender_range(): p_data() {} appender_range(T const &v): p_data(v) {} appender_range(T &&v): p_data(std::move(v)) {} appender_range(appender_range const &v): p_data(v.p_data) {} appender_range(appender_range &&v): p_data(std::move(v.p_data)) {} appender_range &operator=(appender_range const &v) { p_data = v.p_data; return *this; } appender_range &operator=(appender_range &&v) { p_data = std::move(v.p_data); return *this; } appender_range &operator=(T const &v) { p_data = v; return *this; } appender_range &operator=(T &&v) { p_data = std::move(v); return *this; } void clear() { p_data.clear(); } bool empty() const { return p_data.empty(); } void reserve(typename T::size_type cap) { p_data.reserve(cap); } void resize(typename T::size_type len) { p_data.resize(len); } size_type size() const { return p_data.size(); } size_type capacity() const { return p_data.capacity(); } void put(typename T::const_reference v) { p_data.push_back(v); } void put(typename T::value_type &&v) { p_data.push_back(std::move(v)); } T &get() { return p_data; } private: T p_data; }; template inline appender_range appender() { return appender_range(); } template inline appender_range appender(T &&v) { return appender_range(std::forward(v)); } namespace detail { template struct iterator_range_tag_base { /* fallback, the most basic range */ using type = input_range_tag; }; template<> struct iterator_range_tag_base { using type = output_range_tag; }; template<> struct iterator_range_tag_base { using type = forward_range_tag; }; template<> struct iterator_range_tag_base { using type = bidirectional_range_tag; }; template<> struct iterator_range_tag_base { using type = finite_random_access_range_tag; }; } template using iterator_range_tag = typename detail::iterator_range_tag_base::type; template struct iterator_range: input_range> { using range_category = std::conditional_t< std::is_pointer_v, contiguous_range_tag, iterator_range_tag::iterator_category> >; using value_type = typename std::iterator_traits::value_type; using reference = typename std::iterator_traits::reference; using size_type = std::make_unsigned_t< typename std::iterator_traits::difference_type >; using difference_type = typename std::iterator_traits::difference_type; iterator_range(T beg = T{}, T end = T{}): p_beg(beg), p_end(end) {} template && std::is_pointer_v && std::is_convertible_v >> iterator_range(iterator_range const &v): p_beg(&v[0]), p_end(&v[v.size()]) {} iterator_range(iterator_range const &v): p_beg(v.p_beg), p_end(v.p_end) {} iterator_range(iterator_range &&v): p_beg(std::move(v.p_beg)), p_end(std::move(v.p_end)) {} iterator_range &operator=(iterator_range const &v) { p_beg = v.p_beg; p_end = v.p_end; return *this; } iterator_range &operator=(iterator_range &&v) { p_beg = std::move(v.p_beg); p_end = std::move(v.p_end); return *this; } /* satisfy input_range / forward_range */ bool empty() const { return p_beg == p_end; } void pop_front() { ++p_beg; /* rely on iterators to do their own checks */ if constexpr(std::is_pointer_v) { if (p_beg > p_end) { throw std::out_of_range{"pop_front on empty range"}; } } } reference front() const { return *p_beg; } /* satisfy bidirectional_range */ void pop_back() { /* rely on iterators to do their own checks */ if constexpr(std::is_pointer_v) { if (p_end == p_beg) { throw std::out_of_range{"pop_back on empty range"}; } } --p_end; } reference back() const { return *(p_end - 1); } /* satisfy finite_random_access_range */ size_type size() const { return size_type(p_end - p_beg); } iterator_range slice(size_type start, size_type end) const { return iterator_range(p_beg + start, p_beg + end); } iterator_range slice(size_type start) const { return slice(start, size()); } reference operator[](size_type i) const { return p_beg[i]; } /* statisfy contiguous_range */ value_type *data() { return p_beg; } value_type const *data() const { return p_beg; } /* satisfy output_range */ void put(value_type const &v) { /* rely on iterators to do their own checks */ if constexpr(std::is_pointer_v) { if (p_beg == p_end) { throw std::out_of_range{"put into an empty range"}; } } *(p_beg++) = v; } void put(value_type &&v) { if constexpr(std::is_pointer_v) { if (p_beg == p_end) { throw std::out_of_range{"put into an empty range"}; } } *(p_beg++) = std::move(v); } private: T p_beg, p_end; }; template iterator_range make_range(T beg, T end) { return iterator_range{beg, end}; } template struct ranged_traits> { using range = iterator_range; static range iter(std::initializer_list il) { return range{il.begin(), il.end()}; } }; /* ranged_traits for initializer lists is not enough; we need to be able to * call ostd::iter({initializer list}) and that won't match against a generic * template, so we also need to define that here explicitly... */ template iterator_range iter(std::initializer_list init) noexcept { return iterator_range(init.begin(), init.end()); } template iterator_range citer(std::initializer_list init) noexcept { return iterator_range(init.begin(), init.end()); } template struct ranged_traits { using range = iterator_range; static range iter(T (&array)[N]) { return range{array, array + N}; } }; template inline iterator_range iter(T *a, T *b) { return iterator_range(a, b); } /* iter on standard containers */ namespace detail { /* std iter is available, but at the same time direct iter is not */ template struct ranged_traits_core { using range = iterator_range()))>; static range iter(C &r) { return range{r.begin(), r.end()}; } }; } } /* namespace ostd */ #endif