/// \file // Range v3 library // // Copyright Eric Niebler 2013-present // // 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_CHUNK_HPP #define RANGES_V3_VIEW_CHUNK_HPP #include #include #include #include #include #include #include #include #include #include #include #include // for non_propagating_cache #include #include #include #include #include #include namespace ranges { /// \cond namespace detail { template constexpr bool can_sized_sentinel_() noexcept { using I = iterator_t>; return (bool)sized_sentinel_for; } template struct zero { zero() = default; constexpr explicit zero(T const &) noexcept {} constexpr zero & operator=(T const &) noexcept { return *this; } constexpr zero const & operator=(T const &) const noexcept { return *this; } constexpr operator T() const { return T(0); } constexpr T exchange(T const &) const { return T(0); } }; } // namespace detail /// \endcond /// \addtogroup group-views /// @{ template struct chunk_view_ : view_adaptor, Rng, is_finite::value ? finite : range_cardinality::value> { private: friend range_access; CPP_assert(forward_range); template using offset_t = meta::if_c> || detail::can_sized_sentinel_(), range_difference_t, detail::zero>>; range_difference_t n_ = 0; template struct RANGES_EMPTY_BASES adaptor : adaptor_base , private box> { private: friend adaptor; using CRng = meta::const_if_c; range_difference_t n_; sentinel_t end_; constexpr offset_t const & offset() const { offset_t const & result = this->box>::get(); RANGES_EXPECT(0 <= result && result < n_); return result; } constexpr offset_t & offset() { return const_cast &>( const_cast(*this).offset()); } public: adaptor() = default; constexpr adaptor(meta::const_if_c * cv) : box>{0} , n_((RANGES_EXPECT(0 < cv->n_), cv->n_)) , end_(ranges::end(cv->base())) {} CPP_template(bool Other)( // requires Const && (!Other)) constexpr adaptor(adaptor that) : box>(that.offset()) , n_(that.n_) , end_(that.end_) {} constexpr auto read(iterator_t const & it) const -> decltype(views::take(make_subrange(it, end_), n_)) { RANGES_EXPECT(it != end_); RANGES_EXPECT(0 == offset()); return views::take(make_subrange(it, end_), n_); } constexpr void next(iterator_t & it) { RANGES_EXPECT(it != end_); RANGES_EXPECT(0 == offset()); offset() = ranges::advance(it, n_, end_); } CPP_member constexpr auto prev(iterator_t & it) -> CPP_ret(void)( // requires bidirectional_range) { ranges::advance(it, -n_ + offset()); offset() = 0; } CPP_member constexpr auto distance_to(iterator_t const & here, iterator_t const & there, adaptor const & that) const -> CPP_ret(range_difference_t)( // requires(detail::can_sized_sentinel_())) { auto const delta = (there - here) + (that.offset() - offset()); // This can fail for cyclic base ranges when the chunk size does not // divide the cycle length. Such iterator pairs are NOT in the domain of // -. RANGES_ENSURE(0 == delta % n_); return delta / n_; } CPP_member constexpr auto advance(iterator_t & it, range_difference_t n) -> CPP_ret(void)( // requires random_access_range) { using Limits = std::numeric_limits>; if(0 < n) { RANGES_EXPECT(0 == offset()); RANGES_EXPECT(n <= Limits::max() / n_); auto const remainder = ranges::advance(it, n * n_, end_) % n_; RANGES_EXPECT(0 <= remainder && remainder < n_); offset() = remainder; } else if(0 > n) { RANGES_EXPECT(n >= Limits::min() / n_); ranges::advance(it, n * n_ + offset()); offset() = 0; } } }; constexpr adaptor()> begin_adaptor() { return adaptor()>{this}; } CPP_member constexpr auto begin_adaptor() const -> CPP_ret(adaptor)( // requires forward_range) { return adaptor{this}; } template constexpr Size size_(Size base_size) const { auto const n = static_cast(n_); return base_size / n + (0 != (base_size % n)); } public: chunk_view_() = default; constexpr chunk_view_(Rng rng, range_difference_t n) : chunk_view_::view_adaptor(detail::move(rng)) , n_((RANGES_EXPECT(0 < n), n)) {} CPP_member constexpr auto CPP_fun(size)()(const requires sized_range) { return size_(ranges::size(this->base())); } CPP_member constexpr auto CPP_fun(size)()(requires sized_range) { return size_(ranges::size(this->base())); } }; template struct chunk_view_ : view_facade, is_finite::value ? finite : range_cardinality::value> { private: friend range_access; CPP_assert(input_range && !forward_range); using iter_cache_t = detail::non_propagating_cache>; Rng base_; range_difference_t n_; range_difference_t remainder_; mutable iter_cache_t it_cache_; constexpr iterator_t & it() noexcept { return *it_cache_; } constexpr iterator_t const & it() const noexcept { return *it_cache_; } struct outer_cursor { private: struct inner_view : view_facade { private: friend range_access; using value_type = range_value_t; chunk_view_ * rng_ = nullptr; constexpr bool done() const noexcept { RANGES_EXPECT(rng_); return rng_->remainder_ == 0; } constexpr bool equal(default_sentinel_t) const noexcept { return done(); } constexpr iter_reference_t> read() const { RANGES_EXPECT(!done()); return *rng_->it(); } constexpr iter_rvalue_reference_t> move() const { RANGES_EXPECT(!done()); return ranges::iter_move(rng_->it()); } constexpr void next() { RANGES_EXPECT(!done()); ++rng_->it(); --rng_->remainder_; if(rng_->remainder_ != 0 && rng_->it() == ranges::end(rng_->base_)) rng_->remainder_ = 0; } CPP_member constexpr auto distance_to(default_sentinel_t) const -> CPP_ret(range_difference_t)( // requires sized_sentinel_for, iterator_t>) { RANGES_EXPECT(rng_); auto const d = ranges::end(rng_->base_) - rng_->it(); return ranges::min(d, rng_->remainder_); } public: inner_view() = default; constexpr explicit inner_view(chunk_view_ & view) noexcept : rng_{&view} {} CPP_member constexpr auto CPP_fun(size)()( requires sized_sentinel_for, iterator_t>) { using size_type = detail::iter_size_t>; return static_cast(distance_to(default_sentinel_t{})); } }; chunk_view_ * rng_ = nullptr; public: using value_type = inner_view; outer_cursor() = default; constexpr explicit outer_cursor(chunk_view_ * view) noexcept : rng_{view} {} constexpr inner_view read() const { RANGES_EXPECT(!done()); return inner_view{*rng_}; } constexpr bool done() const { RANGES_EXPECT(rng_); return rng_->it() == ranges::end(rng_->base_) && rng_->remainder_ != 0; } constexpr bool equal(default_sentinel_t) const { return done(); } constexpr void next() { RANGES_EXPECT(!done()); ranges::advance(rng_->it(), rng_->remainder_, ranges::end(rng_->base_)); rng_->remainder_ = rng_->n_; } CPP_member constexpr auto distance_to(default_sentinel_t) const -> CPP_ret(range_difference_t)( // requires sized_sentinel_for, iterator_t>) { RANGES_EXPECT(rng_); auto d = ranges::end(rng_->base_) - rng_->it(); if(d < rng_->remainder_) return 1; d -= rng_->remainder_; d = (d + rng_->n_ - 1) / rng_->n_; d += (rng_->remainder_ != 0); return d; } }; constexpr outer_cursor begin_cursor() noexcept { it_cache_ = ranges::begin(base_); return outer_cursor{this}; } template constexpr Size size_(Size base_size) const { auto const n = static_cast(this->n_); return base_size / n + (0 != base_size % n); } public: chunk_view_() = default; constexpr chunk_view_(Rng rng, range_difference_t n) : base_(detail::move(rng)) , n_((RANGES_EXPECT(0 < n), n)) , remainder_(n) , it_cache_{nullopt} {} CPP_member constexpr auto CPP_fun(size)()(const requires sized_range) { return size_(ranges::size(base_)); } CPP_member constexpr auto CPP_fun(size)()(requires sized_range) { return size_(ranges::size(base_)); } Rng base() const { return base_; } }; template struct chunk_view : chunk_view_> { using chunk_view::chunk_view_::chunk_view_; }; #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17 template chunk_view(Rng &&, range_difference_t)->chunk_view>; #endif namespace views { // In: range // Out: range>, where each inner range has $n$ elements. // The last range may have fewer. struct chunk_fn { private: friend view_access; template static constexpr auto CPP_fun(bind)(chunk_fn chunk, Int n)( // requires integral) { return make_pipeable(bind_back(chunk, n)); } public: template constexpr auto operator()(Rng && rng, range_difference_t n) const -> CPP_ret(chunk_view>)( // requires viewable_range && input_range) { return {all(static_cast(rng)), n}; } }; /// \relates chunk_fn /// \ingroup group-views RANGES_INLINE_VARIABLE(view, chunk) } // namespace views /// @} } // namespace ranges #include RANGES_SATISFY_BOOST_RANGE(::ranges::chunk_view) #endif