#pragma once #include "types.hpp" #include #include #include #include namespace util { namespace rg = std::ranges; template concept simple_view = // exposition only rg::view && rg::range && std::same_as, std::ranges::iterator_t> && std::same_as, std::ranges::sentinel_t>; template requires(rg::view) class enumerate_view : public rg::view_interface> { private: R base_ = R(); public: template class iterator; template class sentinel; constexpr enumerate_view() = default; constexpr enumerate_view(R base) : base_(std::move(base)) {} constexpr auto size() -> rg::range_difference_t requires(rg::sized_range) { return rg::size(base_); } constexpr auto size() const -> rg::range_difference_t requires(rg::sized_range) { return rg::size(base_); } constexpr auto begin() -> iterator { return iterator{rg::begin(base_), 0}; } constexpr auto begin() const -> iterator { return iterator{rg::begin(base_), 0}; } constexpr auto end() { return sentinel(rg::end(base_)); } constexpr auto end() requires(rg::common_range &&rg::sized_range) { return iterator{rg::end(base_), static_cast>(size())}; } constexpr auto end() const requires(rg::range) { return sentinel{rg::end(base_)}; } constexpr auto end() const requires(rg::common_range &&rg::sized_range) { return iterator{rg::end(base_), static_cast>(size())}; } constexpr auto base() const -> R &requires(std::copy_constructible) { return base_; } constexpr auto base() -> R && { return std::move(base_); } }; template enumerate_view(R &&) -> enumerate_view>; template requires(rg::view) template class enumerate_view::iterator { public: using Base = std::conditional_t; using iterator_type = rg::iterator_t; using iterator_category = typename std::iterator_traits::iterator_category; using iterator_concept = iterator_category; using difference_type = typename rg::range_difference_t; using enumerator_type = difference_type; struct result { const enumerator_type index; rg::range_reference_t value; }; using reference = result; using value_type = result; public: enumerator_type index_ = {}; iterator_type base_ = iterator_type(); iterator() = default; constexpr explicit iterator(iterator_type base, enumerator_type index = 0) : base_(std::move(base)), index_(index) {} constexpr iterator(iterator i) requires Const && std::convertible_to, rg::iterator_t> : base_(std::move(i.base_)), index_(std::move(i.index_)) {} constexpr rg::iterator_t base() const &requires std::copyable> { return base_; } constexpr rg::iterator_t base() && { return std::move(base_); } constexpr decltype(auto) operator*() const { return result{index_, *base_}; } constexpr auto operator++() -> iterator & { ++index_; ++base_; return *this; } constexpr auto operator++(int) -> void requires(!rg::forward_range) { ++index_; ++base_; } constexpr auto operator++(int) -> iterator requires(rg::forward_range) { const auto tmp = *this; ++index_; ++base_; return tmp; } constexpr auto operator--() -> iterator &requires(rg::bidirectional_range) { --index_; --base_; return *this; } constexpr auto operator--(int) -> iterator requires(rg::bidirectional_range) { const auto tmp = *this; --index_; --base_; return tmp; } constexpr auto operator+=(difference_type n) -> iterator &requires(rg::random_access_range) { index_ += n; base_ += n; return *this; } constexpr auto operator-=(difference_type n) -> iterator &requires(rg::random_access_range) { index_ -= n; base_ -= n; return *this; } friend constexpr auto operator==(const iterator &lhs, const iterator &rhs) -> bool requires(std::equality_comparable>) { return lhs.base_ == rhs.base_; } friend constexpr auto operator<(const iterator &lhs, const iterator &rhs) -> bool requires(rg::random_access_range) { return lhs.base_ < rhs.base_; } friend constexpr auto operator>(const iterator &lhs, const iterator &rhs) -> bool requires(rg::random_access_range) { return rhs < lhs; } friend constexpr auto operator<=(const iterator &lhs, const iterator &rhs) -> bool requires(rg::random_access_range) { return !(rhs < lhs); } friend constexpr auto operator>=(const iterator &lhs, const iterator &rhs) -> bool requires(rg::random_access_range) { return !(lhs < rhs); } friend constexpr auto operator<=>( const iterator &lhs, const iterator &rhs) requires(std::three_way_comparable) { return lhs.base_ <=> rhs.base_; } friend constexpr auto operator+(const iterator &lhs, difference_type n) -> iterator requires(rg::random_access_range) { return iterator{lhs} + n; } friend constexpr auto operator+(difference_type n, const iterator &rhs) -> iterator requires(rg::random_access_range) { return n + rhs; } friend constexpr auto operator-(const iterator &lhs, difference_type n) -> iterator requires(rg::random_access_range) { return iterator{lhs} -= n; } friend constexpr auto operator-(const iterator &lhs, const iterator &rhs) -> iterator requires(rg::random_access_range) { return lhs.base_ - rhs.base_; } }; template requires(rg::view) template class enumerate_view::sentinel { private: using Base = std::conditional_t; rg::sentinel_t end_ = rg::sentinel_t(); public: sentinel() = default; constexpr explicit sentinel(rg::sentinel_t end) : end_(end) {} constexpr sentinel(sentinel other) requires Const && std::convertible_to, rg::sentinel_t> : end_(std::move(other.end_)) {} constexpr auto base() const -> rg::sentinel_t { return end_; } friend constexpr auto operator==(const iterator &rhs, const sentinel &lhs) -> bool { return rhs.base_ == lhs.end_; } friend constexpr auto operator-(const iterator &rhs, const sentinel &lhs) -> rg::range_difference_t { return rhs.base_ - lhs.end_; } friend constexpr auto operator-(const sentinel &rhs, const iterator &lhs) -> rg::range_difference_t { return rhs.end_ - lhs.base_; } }; namespace detail { struct enumerate_view_adaptor { template constexpr auto operator()(R &&r) const { return enumerate_view{std::move(r)}; } template constexpr friend auto operator|(R &&rng, const enumerate_view_adaptor &) { return enumerate_view{std::move(rng)}; } }; } // namespace detail namespace views { inline detail::enumerate_view_adaptor enumerate; } // namespace views } // namespace util