#ifndef CORE_RANGE_HPP #define CORE_RANGE_HPP #include #include #include #include #include #include namespace core { inline namespace v2 { namespace impl { using ::std::begin; using ::std::end; template using adl_begin_t = decltype(begin(::std::declval())); template using adl_end_t = decltype(end(::std::declval())); template adl_begin_t adl_begin (T&& t) { using ::std::begin; return begin(::core::forward(t)); } template adl_end_t adl_end (T&& t) { using ::std::end; return end(::core::forward(t)); } } /* namespace impl */ template struct is_range : meta::all_t< is_detected::value, is_detected::value > { }; template struct range { using traits = ::std::iterator_traits; using iterator_category = typename traits::iterator_category; using difference_type = typename traits::difference_type; using value_type = typename traits::value_type; using reference = typename traits::reference; using pointer = typename traits::pointer; using iterator = Iterator; static constexpr bool is_input = ::std::is_convertible< iterator_category, ::std::input_iterator_tag >::value; static constexpr bool is_output = ::std::is_convertible< iterator_category, ::std::output_iterator_tag >::value; static constexpr bool is_forward = ::std::is_convertible< iterator_category, ::std::forward_iterator_tag >::value; static constexpr bool is_bidirectional = ::std::is_convertible< iterator_category, ::std::bidirectional_iterator_tag >::value; static constexpr bool is_random_access = ::std::is_convertible< iterator_category, ::std::random_access_iterator_tag >::value; template < class Range, class=meta::when< meta::all< meta::none< ::std::is_pointer::value, ::std::is_same, range>::value >(), is_range::value, is_detected_convertible::value >() > > explicit range (Range&& r) noexcept : begin_ { impl::adl_begin(::core::forward(r)) }, end_ { impl::adl_end(::core::forward(r)) } { } range (::std::pair pair) noexcept : range { ::std::get<0>(pair), ::std::get<1>(pair) } { } range (iterator begin_, iterator end_) noexcept : begin_ { begin_ }, end_ { end_ } { } range (range const& that) : range { that.begin_, that.end_ } { } range (range&& that) noexcept : range { ::core::move(that.begin_), ::core::move(that.end_) } { that.begin_ = that.end_; } range () = default; ~range () = default; range& operator = (range const& that) { return *this = range { that }; } range& operator = (range&& that) { range { ::std::move(that) }.swap(*this); return *this; } reference operator [](difference_type idx) const { static_assert(is_random_access, "can only subscript into random-access"); return idx < 0 ? this->end()[idx] : this->begin()[idx]; } iterator begin () const { return this->begin_; } iterator end () const { return this->end_; } reference front () const { return *this->begin(); } reference back () const { static_assert(is_bidirectional, "can only get back of bidirectional"); return *::std::prev(this->end()); } bool empty () const { return this->begin() == this->end(); } difference_type size () const { static_assert(is_forward, "can only get size of forward-range"); return ::std::distance(this->begin(), this->end()); } /* Creates an open-ended range of [start, stop) */ range slice (difference_type start, difference_type stop) const { static_assert(is_forward, "can only slice forward-range"); /* Behavior is: * if start is negative, the begin marker is this->end() - start * if stop is negative, the end marker is this->end() - stop * if start is positive, the begin marker is this->begin() + start * if stop is positive, the end marker is this->begin() + stop * * if start and stop are positive, and stop is less than or equal to start, * an empty range is returned. * * if start and stop are negative and stop is less than or equal to start, * an empty range is returned. * * if start is positive and stop is negative and abs(stop) + start is * greater than or equal to this->size(), an empty range is returned. * * if start is negative and stop is positive and this->size() + start is * greater or equal to stop, an empty range is returned. * * The first two conditions can be computed cheaply, while the third and * fourth are a bit more expensive, but WILL be required no matter what * iterator type we are. However we don't compute the size until after * we've checked the first two conditions * * An example with python style slicing for each would be: * [4:3] -> empty range * [-4:-4] -> empty range * [7:-4] -> empty range for string of size 11 or more * [-4:15] -> empty range for a string of size 19 or less. */ bool const start_positive = start > 0; bool const stop_positive = stop > 0; bool const stop_less = stop < start; bool const first_return_empty = start_positive == stop_positive and stop_less; if (first_return_empty) { return range { }; } /* now safe to compute size */ auto const size = this->size(); auto const third_empty = ::std::abs(stop) + start; bool const second_return_empty = (start_positive and not stop_positive and third_empty >= size) or (not start_positive and stop_positive and size + start >= stop); if (second_return_empty) { return range { }; } /* While the code below technically works for all iterators it is * ineffecient in some cases for bidirectional ranges, where either of * start or stop are negative. * TODO: Specialize for bidirectional operators */ if (not start_positive) { start += size; } if (not stop_positive) { stop += size; } auto begin = this->begin(); ::std::advance(begin, start); auto end = begin; ::std::advance(end, stop - start); return range { begin, end }; } /* Creates an open-ended range of [start, end()) */ range slice (difference_type start) const { static_assert(is_forward, "can only slice forward-range"); return range { split(start).second }; } ::std::pair split (difference_type idx) const { static_assert(is_forward,"can only split a forward-range"); if (idx >= 0) { range second { *this }; second.pop_front_upto(idx); return ::std::make_pair(range { this->begin(), second.begin() }, second); } range first { *this }; first.pop_back_upto(-idx); return ::std::make_pair(first, range { first.end(), this->end() }); } /* mutates range */ void pop_front (difference_type n) { ::std::advance(this->begin_, n); } void pop_front () { ++this->begin_; } void pop_back (difference_type n) { static_assert(is_bidirectional, "can only pop-back bidirectional-range"); ::std::advance(this->end_, -n); } void pop_back () { static_assert(is_bidirectional, "can only pop-back bidirectional-range"); --this->end_; } /* Negative argument causes no change */ void pop_front_upto (difference_type n) { ::std::advance( this->begin_, ::std::min(::std::max(0, n), this->size()) ); } /* Negative argument causes no change */ void pop_back_upto (difference_type n) { static_assert(is_bidirectional, "can only pop-back-upto bidirectional"); ::std::advance( this->end_, -::std::min(::std::max(0, n), this->size()) ); } void swap (range& that) noexcept(is_nothrow_swappable::value) { using ::std::swap; swap(this->begin_, that.begin_); swap(this->end_, that.end_); } private: iterator begin_; iterator end_; }; template auto make_range (T* ptr, ::std::size_t n) -> range { return range { ptr, ptr + n }; } template auto make_range (Iterator begin, Iterator end) -> range { return range { begin, end }; } template auto make_range (Range&& value) -> range { using ::std::begin; using ::std::end; return make_range(begin(value), end(value)); } /* Used like: core::make_range(::std::cin) */ template < class T, class CharT, class Traits=::std::char_traits > auto make_range (::std::basic_istream& stream) -> range< ::std::istream_iterator > { using iterator = ::std::istream_iterator; return make_range(iterator { stream }, iterator { }); } template > auto make_range (::std::basic_streambuf* buffer) -> range< ::std::istreambuf_iterator > { using iterator = ::std::istreambuf_iterator; return make_range(iterator { buffer }, iterator { }); } template range<::std::move_iterator> make_move_range (Iter start, Iter stop) { return make_range( ::std::make_move_iterator(start), ::std::make_move_iterator(stop)); } template range<::std::move_iterator> make_move_range (T* ptr, ::std::size_t n) { return make_move_range(ptr, ptr + n); } template range> make_number_range(T start, T stop, T step) noexcept { auto begin = make_number_iterator(start, step); auto end = make_number_iterator(stop, step); return make_range(begin, end); } template range> make_number_range (T start, T stop) noexcept { return make_range(make_number_iterator(start), make_number_iterator(stop)); } template void swap (range& lhs, range& rhs) noexcept( noexcept(lhs.swap(rhs)) ) { lhs.swap(rhs); } }} /* namespace core::v2 */ #endif /* CORE_RANGE_HPP */