/// \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 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) {} CPP_template(bool Other)( // requires Const && (!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 constexpr auto distance_to(iterator_t const & here, Other const & there) const -> CPP_ret(range_difference_t)( // requires sized_sentinel_for>) { 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 auto begin_adaptor() noexcept -> adaptor { return adaptor{this}; } CPP_member constexpr auto begin_adaptor() const noexcept -> CPP_ret(adaptor)(requires(const_iterable())) { return adaptor{this}; } constexpr auto end_adaptor() noexcept -> meta::if_c(), adaptor, adaptor_base> { 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_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_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_fn { private: friend view_access; template constexpr static auto CPP_fun(bind)(stride_fn stride, Difference step)( // requires integral) { return make_pipeable(bind_back(stride, std::move(step))); } public: template constexpr auto operator()(Rng && rng, range_difference_t step) const -> CPP_ret(stride_view>)( // requires viewable_range && input_range) { return stride_view>{all(static_cast(rng)), step}; } }; /// \relates stride_fn /// \ingroup group-views RANGES_INLINE_VARIABLE(view, stride) } // namespace views /// @} } // namespace ranges #include RANGES_SATISFY_BOOST_RANGE(::ranges::stride_view) #endif