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 {

struct take_evenly_fn {  // NOLINT(readability-identifier-naming)

  template <class Range>
  constexpr auto operator()(Range&& range, std::size_t count) const {
    const std::size_t size = ranges::size(range);

    const auto filter_function = [size, count](const auto& pair) {
      if ((size == 0UL) || (count == 0UL)) {
        return false;
      }

      if (count > size) {
        return true;
      }

      const auto [index, _] = pair;
      if (count == 1UL) {
        return index == 0UL;
      }

      if ((index == 0UL) || (index == size - 1UL)) {
        return true;
      }

      const std::size_t m0 = (index - 1UL) * (count - 1UL) / (size - 1UL);
      const std::size_t m1 = index * (count - 1UL) / (size - 1UL);
      return m0 != m1;
    };

    // `cache1` ensures that views prior to `filter` in the pipeline are iterated exactly once.
    // This is needed because filter needs to dereference the input iterator twice.
    return ranges::views::enumerate(range) | ranges::views::cache1 | ranges::views::filter(filter_function) |
           beluga::views::elements<1>;
  }


  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;  // NOLINT(readability-identifier-naming)

}  // namespace beluga::views

#endif