/// \file // Range v3 library // // Copyright Eric Niebler 2013-present // Copyright Casey Carter 2017 // // Use, modification and distribution is subject to the // Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Project home: https://github.com/ericniebler/range-v3 // #ifndef RANGES_V3_VIEW_STRIDE_HPP #define RANGES_V3_VIEW_STRIDE_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ranges { /// \cond template struct stride_view; namespace detail { template using stride_view_adaptor = view_adaptor, Rng, is_finite::value ? finite : range_cardinality::value>; // Bidirectional stride views need to remember the distance between // the penultimate iterator and the last iterator - which may be less // than the stride - so that decrementing an last iterator properly // produces the penultimate iterator. stride_view_base specializes on // that distinction so that only Bidirectional stride views have the // data member "offset_". template struct stride_view_base_; template using stride_view_base = stride_view_base_>; template*/> struct stride_view_base_ : stride_view_adaptor { stride_view_base_() = default; constexpr stride_view_base_(Rng && rng, range_difference_t const stride) : stride_view_adaptor{std::move(rng)} , stride_{(RANGES_EXPECT(0 < stride), stride)} , offset_{calc_offset(meta::bool_>{})} {} protected: constexpr void set_offset(range_difference_t const delta) noexcept { RANGES_EXPECT(0 <= delta && delta < stride_); if(0 > offset_) offset_ = delta; else RANGES_EXPECT(offset_ == delta); } constexpr void set_offset(range_difference_t const) const noexcept {} constexpr range_difference_t get_offset(bool check = true) const noexcept { RANGES_EXPECT(!check || 0 <= offset_); return offset_; } range_difference_t stride_; range_difference_t offset_ = -1; private: constexpr range_difference_t calc_offset(std::true_type) { if(auto const rem = ranges::distance(this->base()) % stride_) return stride_ - rem; else return 0; } constexpr range_difference_t calc_offset(std::false_type) const noexcept { return -1; } }; template struct stride_view_base_ : stride_view_adaptor { stride_view_base_() = default; constexpr stride_view_base_(Rng && rng, range_difference_t const stride) : stride_view_adaptor{std::move(rng)} , stride_{(RANGES_EXPECT(0 < stride), stride)} {} protected: constexpr void set_offset(range_difference_t const) const noexcept {} constexpr range_difference_t get_offset(bool = true) const noexcept { return 0; } range_difference_t stride_; }; } // namespace detail /// \endcond /// \addtogroup group-views /// @{ template struct stride_view : detail::stride_view_base { private: friend range_access; // stride_view const models Range if Rng const models Range, and // either (1) Rng is sized, so we can pre-calculate offset_, or (2) // Rng is !Bidirectional, so it does not need offset_. static constexpr bool const_iterable() noexcept { return range && (sized_range || !bidirectional_range); } // If the underlying range doesn't model common_range, then we can't // decrement the last and there's no reason to adapt the sentinel. Strictly // speaking, we don't have to adapt the last iterator of input and forward // ranges, but in the interests of making the resulting stride view model // common_range, adapt it anyway. template static constexpr bool can_bound() noexcept { using CRng = meta::const_if_c; return common_range && (sized_range || !bidirectional_range); } template struct adaptor : adaptor_base { private: friend struct adaptor; using CRng = meta::const_if_c; using stride_view_t = meta::const_if_c; stride_view_t * rng_; public: adaptor() = default; constexpr adaptor(stride_view_t * rng) noexcept : rng_(rng) {} template(bool Other)( requires Const AND CPP_NOT(Other)) // adaptor(adaptor that) : rng_(that.rng_) {} constexpr void next(iterator_t & it) { auto const last = ranges::end(rng_->base()); RANGES_EXPECT(it != last); auto const delta = ranges::advance(it, rng_->stride_, last); if(it == last) { rng_->set_offset(delta); } } CPP_member constexpr auto prev(iterator_t & it) // -> CPP_ret(void)( requires bidirectional_range) { RANGES_EXPECT(it != ranges::begin(rng_->base())); auto delta = -rng_->stride_; if(it == ranges::end(rng_->base())) { RANGES_EXPECT(rng_->get_offset() >= 0); delta += rng_->get_offset(); } ranges::advance(it, delta); } template(typename Other)( requires sized_sentinel_for>) constexpr range_difference_t distance_to(iterator_t const & here, Other const & there) const { range_difference_t delta = there - here; if(delta < 0) delta -= rng_->stride_ - 1; else delta += rng_->stride_ - 1; return delta / rng_->stride_; } CPP_member constexpr auto advance(iterator_t & it, range_difference_t n) // -> CPP_ret(void)( requires random_access_range) { if(0 == n) return; n *= rng_->stride_; auto const last = ranges::end(rng_->base()); if(it == last) { RANGES_EXPECT(n < 0); RANGES_EXPECT(rng_->get_offset() >= 0); n += rng_->get_offset(); } if(0 < n) { auto delta = ranges::advance(it, n, last); if(it == last) { // advance hit the last of the base range. rng_->set_offset(delta % rng_->stride_); } } else if(0 > n) { #ifdef NDEBUG ranges::advance(it, n); #else auto const first = ranges::begin(rng_->base()); auto const delta = ranges::advance(it, n, first); RANGES_EXPECT(delta == 0); #endif } } }; constexpr adaptor begin_adaptor() noexcept { return adaptor{this}; } CPP_member constexpr auto begin_adaptor() const noexcept -> CPP_ret(adaptor)( requires(const_iterable())) { return adaptor{this}; } constexpr meta::if_c(), adaptor, adaptor_base> // end_adaptor() noexcept { return {this}; } CPP_member constexpr auto end_adaptor() const noexcept // -> CPP_ret(meta::if_c(), adaptor, adaptor_base>)( requires (const_iterable())) { return {this}; } public: stride_view() = default; constexpr stride_view(Rng rng, range_difference_t const stride) : detail::stride_view_base{std::move(rng), stride} {} CPP_auto_member constexpr auto CPP_fun(size)()( requires sized_range) { using size_type = range_size_t; auto const n = ranges::size(this->base()); return (n + static_cast(this->stride_) - 1) / static_cast(this->stride_); } CPP_auto_member constexpr auto CPP_fun(size)()(const // requires sized_range) { using size_type = range_size_t; auto const n = ranges::size(this->base()); return (n + static_cast(this->stride_) - 1) / static_cast(this->stride_); } }; #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17 template stride_view(Rng &&, range_difference_t) -> stride_view>; #endif namespace views { struct stride_base_fn { template(typename Rng)( requires viewable_range AND input_range) constexpr stride_view> // operator()(Rng && rng, range_difference_t step) const { return stride_view>{all(static_cast(rng)), step}; } }; struct stride_fn : stride_base_fn { using stride_base_fn::operator(); template(typename Difference)( requires detail::integer_like_) constexpr auto operator()(Difference step) const { return make_view_closure(bind_back(stride_base_fn{}, step)); } }; /// \relates stride_fn /// \ingroup group-views RANGES_INLINE_VARIABLE(stride_fn, stride) } // namespace views /// @} } // namespace ranges #include #include RANGES_SATISFY_BOOST_RANGE(::ranges::stride_view) #endif