Program Listing for File take_evenly.hpp
↰ Return to documentation for file (include/beluga/views/take_evenly.hpp)
// Copyright 2023 Ekumen, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BELUGA_VIEWS_TAKE_EVENLY_HPP
#define BELUGA_VIEWS_TAKE_EVENLY_HPP
#include <beluga/views/elements.hpp>
#include <range/v3/view/cache1.hpp>
#include <range/v3/view/enumerate.hpp>
#include <range/v3/view/filter.hpp>
namespace beluga::views {
namespace detail {
template <class Range>
struct take_evenly_view : public ranges::view_facade<take_evenly_view<Range>, ranges::finite> {
public:
static_assert(ranges::view_<Range>);
static_assert(ranges::forward_range<Range>);
static_assert(ranges::sized_range<Range>);
constexpr take_evenly_view() = default;
constexpr take_evenly_view(Range range, std::size_t count)
: range_{std::move(range)}, count_{count}, size_{ranges::size(range_)} {}
[[nodiscard]] constexpr std::size_t size() const {
if (size_ == 0U) {
return 0U;
}
if (count_ > size_) {
return size_;
}
return count_;
}
private:
friend ranges::range_access;
struct cursor {
public:
cursor() = default;
constexpr cursor(const take_evenly_view* view, std::size_t pos)
: view_{view}, pos_{pos}, it_{ranges::begin(view_->range_)} {}
[[nodiscard]] constexpr decltype(auto) read() const { return *it_; }
[[nodiscard]] constexpr bool equal(const cursor& other) const { return view_ == other.view_ && pos_ == other.pos_; }
[[nodiscard]] constexpr bool equal(const ranges::default_sentinel_t&) const {
return pos_ >= view_->count_ || pos_ >= view_->size_;
}
constexpr void next() {
const std::size_t new_pos = pos_ + 1;
ranges::advance(it_, view_->compute_offset(pos_, new_pos));
pos_ = new_pos;
}
template <
class T = Range,
std::enable_if_t<std::is_same_v<T, Range> && ranges::bidirectional_range<Range>, int> = 0>
constexpr void prev() {
const std::size_t new_pos = pos_ - 1;
ranges::advance(it_, view_->compute_offset(pos_, new_pos));
pos_ = new_pos;
}
template <
class T = Range,
std::enable_if_t<std::is_same_v<T, Range> && ranges::random_access_range<Range>, int> = 0>
constexpr void advance(std::ptrdiff_t distance) {
const auto new_pos = static_cast<std::size_t>(static_cast<std::ptrdiff_t>(pos_) + distance);
ranges::advance(it_, view_->compute_offset(pos_, new_pos));
pos_ = new_pos;
}
template <
class T = Range,
std::enable_if_t<std::is_same_v<T, Range> && ranges::random_access_range<Range>, int> = 0>
[[nodiscard]] constexpr std::ptrdiff_t distance_to(const cursor& other) const {
return static_cast<std::ptrdiff_t>(other.pos_) - static_cast<std::ptrdiff_t>(pos_);
}
template <
class T = Range,
std::enable_if_t<std::is_same_v<T, Range> && ranges::random_access_range<Range>, int> = 0>
[[nodiscard]] constexpr std::ptrdiff_t distance_to(const ranges::default_sentinel_t&) const {
return static_cast<std::ptrdiff_t>(view_->count_) - static_cast<std::ptrdiff_t>(pos_);
}
private:
const take_evenly_view* view_;
std::size_t pos_;
ranges::iterator_t<Range> it_;
};
[[nodiscard]] constexpr auto begin_cursor() const { return cursor{this, 0U}; }
[[nodiscard]] constexpr auto end_cursor() const noexcept { return ranges::default_sentinel_t{}; }
[[nodiscard]] constexpr std::ptrdiff_t compute_offset(std::size_t pos) const {
assert(pos <= count_);
if (pos == 0U) {
return 0;
}
if (count_ == 1U) {
return static_cast<std::ptrdiff_t>(size_);
}
const std::ptrdiff_t a = static_cast<std::ptrdiff_t>(pos) * (static_cast<std::ptrdiff_t>(size_) - 1);
const std::ptrdiff_t b = static_cast<std::ptrdiff_t>(count_) - 1;
return a / b + ((a % b == 0) ? 0 : 1);
}
[[nodiscard]] constexpr std::ptrdiff_t compute_offset(std::size_t current, std::size_t target) const {
if (count_ > size_) {
return static_cast<std::ptrdiff_t>(target) - static_cast<std::ptrdiff_t>(current);
}
return compute_offset(target) - compute_offset(current);
}
Range range_;
std::size_t count_;
std::size_t size_;
};
template <class Range>
take_evenly_view(Range&& range, std::size_t max) -> take_evenly_view<ranges::views::all_t<Range>>;
struct take_evenly_fn {
template <class Range>
constexpr auto operator()(Range&& range, std::size_t count) const {
return take_evenly_view{std::forward<Range>(range), count};
}
constexpr auto operator()(std::size_t count) const {
return ranges::make_view_closure(ranges::bind_back(take_evenly_fn{}, count));
}
};
} // namespace detail
inline constexpr detail::take_evenly_fn take_evenly;
} // namespace beluga::views
#endif