#pragma once #if !defined(MIJIN_UTIL_ITERATORS_HPP_INCLUDED) #define MIJIN_UTIL_ITERATORS_HPP_INCLUDED 1 #include #include #include #include #include #include #include #include #include #include "../container/optional.hpp" #include "../internal/common.hpp" #include "../util/annot.hpp" namespace mijin { struct RangeAdapter {}; template struct RangeRef { T& range; decltype(auto) begin() const { return std::begin(range); } decltype(auto) end() const { return std::end(range); } }; template struct RangeRef> { std::pair iterators; TIterator begin() const { return iterators.first; } TIterator end() const { return iterators.second; } }; // in case of pass-by-reference template struct RangeRef&> : RangeRef> {}; static_assert(std::is_same_v>>().iterators.first), int*>, "RangeRef for iterator pair not working."); static_assert(std::is_same_v&>>().iterators.first), std::string_view::iterator>, "RangeRef for iterator pair not working."); template struct is_range_adapter_type : std::false_type {}; template requires std::is_base_of_v struct is_range_adapter_type : std::true_type {}; // allow passing in spans and string_views by value template struct is_range_adapter_type> : std::true_type {}; template struct is_range_adapter_type> : std::true_type {}; template concept RangeAdapterType = is_range_adapter_type>::value; static_assert(!is_range_adapter_type>::value); static_assert(is_range_adapter_type::value); template struct RangeRef { std::remove_reference_t range; decltype(auto) begin() const { return range.begin(); } decltype(auto) end() const { return range.end(); } }; template struct EnumeratingIterator { TIdx idx; TIterator base; EnumeratingIterator(TIdx idx_, TIterator base_) MIJIN_NOEXCEPT : idx(idx_), base(base_) {} EnumeratingIterator(const EnumeratingIterator&) MIJIN_NOEXCEPT = default; EnumeratingIterator& operator=(const EnumeratingIterator&) MIJIN_NOEXCEPT = default; auto operator*() const MIJIN_NOEXCEPT { return std::tie(idx, *base); } EnumeratingIterator& operator++() MIJIN_NOEXCEPT { ++idx; ++base; return *this; } EnumeratingIterator operator++(int) MIJIN_NOEXCEPT { EnumeratingIterator copy(*this); ++(*this); return copy; } EnumeratingIterator& operator--() MIJIN_NOEXCEPT { --idx; --base; return *this; } EnumeratingIterator operator--(int) MIJIN_NOEXCEPT { EnumeratingIterator copy(*this); --(*this); return copy; } bool operator==(const EnumeratingIterator& other) const MIJIN_NOEXCEPT { return base == other.base; // note: ignoring idx so we don't have to find it out for end() } bool operator!=(const EnumeratingIterator& other) const MIJIN_NOEXCEPT { return base != other.base; // note: ignoring idx so we don't have to find it out for end() } }; template EnumeratingIterator(TIdx, TIterator) -> EnumeratingIterator; template struct Enumeratable : RangeAdapter { RangeRef base; auto begin() const MIJIN_NOEXCEPT { return EnumeratingIterator(TIdx(0), base.begin()); } auto end() const MIJIN_NOEXCEPT { return EnumeratingIterator(TIdx(0), base.end()); } }; template Enumeratable enumerate(TIterable&& iterable) { return {.base = {std::forward(iterable)}}; } template struct ZippingIterator { TFirstIterator first; TSecondIterator second; ZippingIterator(TFirstIterator first_, TSecondIterator second_) MIJIN_NOEXCEPT : first(first_), second(second_) {} ZippingIterator(const ZippingIterator&) MIJIN_NOEXCEPT = default; ZippingIterator& operator=(const ZippingIterator&) MIJIN_NOEXCEPT = default; decltype(auto) operator*() const MIJIN_NOEXCEPT { return std::tie(*first, *second); } ZippingIterator& operator++() MIJIN_NOEXCEPT { ++first; ++second; return *this; } ZippingIterator operator++(int) MIJIN_NOEXCEPT { ZippingIterator copy(*this); ++(*this); return copy; } ZippingIterator& operator--() MIJIN_NOEXCEPT { --first; --second; return *this; } ZippingIterator operator--(int) MIJIN_NOEXCEPT { ZippingIterator copy(*this); --(*this); return copy; } bool operator==(const ZippingIterator& other) const MIJIN_NOEXCEPT { return first == other.first || second == other.second; // note: this uses or so reaching the end on one range also ends the zipped one. } bool operator!=(const ZippingIterator& other) const MIJIN_NOEXCEPT { return !(*this == other); } }; template ZippingIterator(TFirstIterator, TSecondIterator) -> ZippingIterator; template struct ZippingRange : RangeAdapter { RangeRef first; RangeRef second; auto begin() const MIJIN_NOEXCEPT { return ZippingIterator(first.begin(), second.begin()); } auto end() const MIJIN_NOEXCEPT { return ZippingIterator(first.end(), second.end()); } }; template ZippingRange zip(TFirst&& first, TSecond&& second) { return {.first = {std::forward(first)}, .second = {std::forward(second)}}; } template struct ReplacingIterator { using difference_type = std::ptrdiff_t; using value_type = typename std::iterator_traits::value_type; using pointer = std::add_const_t*; using reference = std::add_const_t&; using iterator_category = std::bidirectional_iterator_tag; // TODO? TIterator base; value_type what; value_type with; ReplacingIterator(TIterator base_, value_type what_, value_type with_) MIJIN_NOEXCEPT : base(base_), what(what_), with(with_) {} ReplacingIterator(const ReplacingIterator&) MIJIN_NOEXCEPT = default; ReplacingIterator& operator=(const ReplacingIterator&) MIJIN_NOEXCEPT = default; pointer operator->() const MIJIN_NOEXCEPT { if (*base == what) { return &with; } return &*base; } reference operator*() const MIJIN_NOEXCEPT { if (*base == what) { return with; } return *base; } ReplacingIterator& operator++() MIJIN_NOEXCEPT { ++base; return *this; } ReplacingIterator operator++(int) MIJIN_NOEXCEPT { ReplacingIterator copy(*this); ++(*this); return copy; } ReplacingIterator& operator--() MIJIN_NOEXCEPT { --base; return *this; } ReplacingIterator operator--(int) MIJIN_NOEXCEPT { ReplacingIterator copy(*this); --(*this); return copy; } bool operator==(const ReplacingIterator& other) const MIJIN_NOEXCEPT { return what == other.what && with == other.with && base == other.base; } bool operator!=(const ReplacingIterator& other) const MIJIN_NOEXCEPT { return !(*this == other); } }; template struct ReplacingRange : RangeAdapter { using value_type = typename std::iterator_traits()))>::value_type; RangeRef base; value_type what; value_type with; auto begin() const MIJIN_NOEXCEPT { return ReplacingIterator(base.begin(), what, with); } auto end() const MIJIN_NOEXCEPT { return ReplacingIterator(base.end(), what, with); } }; template auto replace( TIterable&& iterable, typename std::iterator_traits()))>::value_type what, typename std::iterator_traits()))>::value_type with) { return ReplacingRange{ .base = {std::forward(iterable)}, .what = std::move(what), .with = std::move(with) }; } template struct MappingIterator { using orig_value_type = typename std::iterator_traits::value_type; using difference_type = std::ptrdiff_t; using value_type = std::invoke_result_t; using reference = value_type&; using iterator_category = std::bidirectional_iterator_tag; // TODO? static_assert(!std::is_same_v, "Invalid mapping result type."); TIterator base; TFunctor functor; mutable Optional result; MappingIterator(TIterator base_, TFunctor functor_) MIJIN_NOEXCEPT : base(base_), functor(std::move(functor_)) {} MappingIterator(const MappingIterator&) MIJIN_NOEXCEPT = default; MappingIterator(MappingIterator&&) MIJIN_NOEXCEPT = default; MappingIterator& operator=(const MappingIterator&) MIJIN_NOEXCEPT = default; MappingIterator& operator=(MappingIterator&&) MIJIN_NOEXCEPT = default; reference operator*() const MIJIN_NOEXCEPT { if (result.empty()) { result = std::invoke(functor, *base); } return *result; } MappingIterator& operator++() MIJIN_NOEXCEPT { ++base; result.reset(); return *this; } MappingIterator operator++(int) MIJIN_NOEXCEPT { MappingIterator copy(*this); ++(*this); return copy; } MappingIterator& operator--() MIJIN_NOEXCEPT { --base; result.reset(); return *this; } MappingIterator operator--(int) MIJIN_NOEXCEPT { MappingIterator copy(*this); --(*this); return copy; } bool operator==(const MappingIterator& other) const MIJIN_NOEXCEPT { return base == other.base; // TODO: compare functor? -> doesn't always work and may be useless } bool operator!=(const MappingIterator& other) const MIJIN_NOEXCEPT { return !(*this == other); } }; template struct MappingRange : RangeAdapter { // using value_type = typename std::iterator_traits()))>::value_type; using in_value_type = typename std::iterator_traits>().begin())>::value_type; using value_type = std::invoke_result_t; RangeRef base; TFunctor functor; auto begin() const MIJIN_NOEXCEPT { return MappingIterator(base.begin(), functor); } auto end() const MIJIN_NOEXCEPT { return MappingIterator(base.end(), functor); } [[nodiscard]] bool empty() const MIJIN_NOEXCEPT { return base.begin() == base.end(); } }; template auto map(TIterable&& iterable, TFunctor&& functor) { return MappingRange{ .base = {std::forward(iterable)}, .functor = std::forward(functor) }; } template struct OptionalMappingIterator { using orig_value_type = typename std::iterator_traits::value_type; using difference_type = std::ptrdiff_t; using optional_type = std::invoke_result_t; using value_type = std::decay_t().value())>; using iterator_category = std::forward_iterator_tag; // TODO? using reference = value_type&; static_assert(!std::is_same_v, "Invalid mapping result type."); TIterator base; TIterator end; TFunctor functor; mutable optional_type result; // must be mutable so dereferencing can stay const, not nice :/ OptionalMappingIterator(TIterator base_, TIterator end_, TFunctor functor_) MIJIN_NOEXCEPT : base(base_), end(end_), functor(std::move(functor_)) { if (base != end) { result = functor(*base); if (!result.has_value()) { ++(*this); } } } OptionalMappingIterator(const OptionalMappingIterator&) MIJIN_NOEXCEPT = default; OptionalMappingIterator(OptionalMappingIterator&&) MIJIN_NOEXCEPT = default; OptionalMappingIterator& operator=(const OptionalMappingIterator&) MIJIN_NOEXCEPT = default; OptionalMappingIterator& operator=(OptionalMappingIterator&&) MIJIN_NOEXCEPT = default; reference operator*() const MIJIN_NOEXCEPT { return *result; } OptionalMappingIterator& operator++() MIJIN_NOEXCEPT { do { ++base; result = base != end ? functor(*base) : std::nullopt; } while (base != end && !result.has_value()); return *this; } OptionalMappingIterator operator++(int) MIJIN_NOEXCEPT { OptionalMappingIterator copy(*this); ++(*this); return copy; } bool operator==(const OptionalMappingIterator& other) const MIJIN_NOEXCEPT { return base == other.base && end == other.end; // TODO: compare functor? -> doesn't always work and may be useless } bool operator!=(const OptionalMappingIterator& other) const MIJIN_NOEXCEPT { return !(*this == other); } }; template struct OptionalMappingRange : RangeAdapter { // using value_type = typename std::iterator_traits()))>::value_type; using value_type = typename std::iterator_traits>().begin())>::value_type; RangeRef base; TFunctor functor; auto begin() const MIJIN_NOEXCEPT { return OptionalMappingIterator(base.begin(), base.end(), functor); } auto end() const MIJIN_NOEXCEPT { return OptionalMappingIterator(base.end(), base.end(), functor); } }; template auto mapOptional(TIterable&& iterable, TFunctor&& functor) { return OptionalMappingRange{ .base = {std::forward(iterable)}, .functor = std::forward(functor) }; } template struct FilteringIterator { using difference_type = typename std::iterator_traits::difference_type; using value_type = typename std::iterator_traits::value_type; using iterator_category = std::forward_iterator_tag; // TODO? using reference = typename std::iterator_traits::reference; TIterator base; TIterator end; TPredicate predicate; FilteringIterator(TIterator base_, TIterator end_, TPredicate predicate_) MIJIN_NOEXCEPT : base(base_), end(end_), predicate(std::move(predicate_)) { if (base != end && !predicate(*base)) { ++(*this); } } FilteringIterator(const FilteringIterator&) MIJIN_NOEXCEPT = default; FilteringIterator(FilteringIterator&&) MIJIN_NOEXCEPT = default; FilteringIterator& operator=(const FilteringIterator&) MIJIN_NOEXCEPT = default; FilteringIterator& operator=(FilteringIterator&&) MIJIN_NOEXCEPT = default; reference operator*() const MIJIN_NOEXCEPT { return *base; } FilteringIterator& operator++() MIJIN_NOEXCEPT { do { ++base; } while (base != end && !predicate(*base)); return *this; } FilteringIterator operator++(int) MIJIN_NOEXCEPT { FilteringIterator copy(*this); ++(*this); return copy; } bool operator==(const FilteringIterator& other) const MIJIN_NOEXCEPT { return base == other.base && end == other.end; // TODO: compare predicate? } bool operator!=(const FilteringIterator& other) const MIJIN_NOEXCEPT { return !(*this == other); } }; template struct FilteringRange : RangeAdapter { // using value_type = typename std::iterator_traits()))>::value_type; using value_type = typename std::iterator_traits>().begin())>::value_type; RangeRef base; TPredicate predicate; auto begin() const MIJIN_NOEXCEPT { return FilteringIterator(base.begin(), base.end(), predicate); } auto end() const MIJIN_NOEXCEPT { return FilteringIterator(base.end(), base.end(), predicate); } }; template auto filter(TIterable&& iterable, TPredicate&& predicate) { return FilteringRange{ .base = {std::forward(iterable)}, .predicate = std::forward(predicate) }; } template struct ChainingIterator { using difference_type = std::ptrdiff_t; using value_type = std::remove_reference_t::reference>; using pointer = value_type*; using reference = value_type&; using iterator_category = std::bidirectional_iterator_tag; // TODO? TFirstIterator firstBase; TFirstIterator firstEnd; TSecondIterator secondBase; TSecondIterator secondBegin; ChainingIterator(TFirstIterator firstBase_, TFirstIterator firstEnd_, TSecondIterator secondBase_, TSecondIterator secondBegin_) MIJIN_NOEXCEPT : firstBase(firstBase_), firstEnd(firstEnd_), secondBase(secondBase_), secondBegin(secondBegin_) {} ChainingIterator(const ChainingIterator&) MIJIN_NOEXCEPT = default; ChainingIterator& operator=(const ChainingIterator&) MIJIN_NOEXCEPT = default; pointer operator->() const MIJIN_NOEXCEPT { if (firstBase == firstEnd) { return &*secondBase; } return &*firstBase; } reference operator*() const MIJIN_NOEXCEPT { if (firstBase == firstEnd) { return *secondBase; } return *firstBase; } ChainingIterator& operator++() MIJIN_NOEXCEPT { if (firstBase == firstEnd) { ++secondBase; } else { ++firstBase; } return *this; } ChainingIterator operator++(int) MIJIN_NOEXCEPT { ChainingIterator copy(*this); ++(*this); return copy; } ChainingIterator& operator--() MIJIN_NOEXCEPT { if (secondBase == secondBegin) { --firstBase; } else { --secondBase; } return *this; } ChainingIterator operator--(int) MIJIN_NOEXCEPT { ChainingIterator copy(*this); --(*this); return copy; } bool operator==(const ChainingIterator& other) const MIJIN_NOEXCEPT { return firstBase == other.firstBase && secondBase == other.secondBase; // should be enough } bool operator!=(const ChainingIterator& other) const MIJIN_NOEXCEPT { return !(*this == other); } }; template struct ChainedRange : RangeAdapter { using value_type = typename std::iterator_traits()))>::value_type; RangeRef first; RangeRef second; auto begin() const MIJIN_NOEXCEPT { return ChainingIterator(first.begin(), first.end(), second.begin(), second.begin()); } auto end() const MIJIN_NOEXCEPT { return ChainingIterator(first.end(), first.end(), second.end(), second.begin()); } }; template auto chain(TFirstRange&& firstRange, TSecondRange&& secondRange) { return ChainedRange{ .first = {std::forward(firstRange)}, .second = {std::forward(secondRange)} }; } template auto chain(TFirstRange&& firstRange, TSecondRange&& secondRange, TMoreRanges&&... moreRanges) { return chain(std::forward(firstRange), chain(std::forward(secondRange), std::forward(moreRanges)...)); } template struct TypeFilteringIterator { using difference_type = std::ptrdiff_t; using value_type = TType; using pointer = std::remove_pointer_t*; using reference = std::remove_reference_t&; using iterator_category = std::bidirectional_iterator_tag; // TODO? TIterator base; TIterator end; TypeFilteringIterator(TIterator base_, TIterator end_) MIJIN_NOEXCEPT : base(base_), end(end_) { if (base != end && !isCastable()) { ++(*this); } } TypeFilteringIterator(const TypeFilteringIterator&) MIJIN_NOEXCEPT = default; TypeFilteringIterator(TypeFilteringIterator&&) MIJIN_NOEXCEPT = default; TypeFilteringIterator& operator=(const TypeFilteringIterator&) MIJIN_NOEXCEPT = default; TypeFilteringIterator& operator=(TypeFilteringIterator&&) MIJIN_NOEXCEPT = default; reference operator*() const MIJIN_NOEXCEPT { return static_cast(*base); } TypeFilteringIterator& operator++() MIJIN_NOEXCEPT { do { ++base; } while (base != end && !isCastable()); return *this; } TypeFilteringIterator operator++(int) MIJIN_NOEXCEPT { FilteringIterator copy(*this); ++(*this); return copy; } bool operator==(const TypeFilteringIterator& other) const MIJIN_NOEXCEPT { return base == other.base && end == other.end; } bool operator!=(const TypeFilteringIterator& other) const MIJIN_NOEXCEPT { return !(*this == other); } private: bool isCastable() const { if constexpr (std::is_pointer_v) { return dynamic_cast(*base); } else { return dynamic_cast(&*base); } } }; template struct TypeFilteringRange : RangeAdapter { using orig_value_type = typename std::iterator_traits()))>::value_type; using value_type = TType; RangeRef iterable; auto begin() const MIJIN_NOEXCEPT { return TypeFilteringIterator(iterable.begin(), iterable.end()); } auto end() const MIJIN_NOEXCEPT { return TypeFilteringIterator(iterable.end(), iterable.end()); } }; template auto filterType(TIterable&& iterable) { return TypeFilteringRange{ .iterable = {std::forward(iterable)} }; } template TAs collect(TIterable&& iterable) { return TAs(iterable.begin(), iterable.end()); } template class FixedArrayOutputIterator { public: using array_t = std::array; using difference_type = std::ptrdiff_t; using value_type = TEle; using pointer = value_type*; using reference = value_type&; static constexpr std::size_t NUM_ELES = numEles; private: not_null_t array_; std::size_t counter_ = 0; public: constexpr explicit FixedArrayOutputIterator(array_t& array) MIJIN_NOEXCEPT : array_(&array) {} constexpr explicit FixedArrayOutputIterator(array_t& array, array_t::iterator pos) MIJIN_NOEXCEPT : array_(&array), counter_(static_cast(pos - array.begin())) {} constexpr FixedArrayOutputIterator(const FixedArrayOutputIterator&) noexcept = default; constexpr FixedArrayOutputIterator& operator=(const FixedArrayOutputIterator&) noexcept = default; [[nodiscard]] constexpr std::size_t getCounter() const MIJIN_NOEXCEPT { return counter_; } [[nodiscard]] constexpr bool didOverflow() const MIJIN_NOEXCEPT { return counter_ >= NUM_ELES; } constexpr reference operator*() const MIJIN_NOEXCEPT { static TEle dummy_; // returned when we're at the end of the array if (counter_ < NUM_ELES) { return array_->at(counter_); } return dummy_; } constexpr FixedArrayOutputIterator& operator++() MIJIN_NOEXCEPT { ++counter_; return *this; } constexpr FixedArrayOutputIterator operator++(int) const MIJIN_NOEXCEPT { FixedArrayOutputIterator copy(*this); ++copy; return copy; } }; static_assert(std::output_iterator, int>); namespace pipe { template struct Replace { T what; T with; Replace(T what_, T with_) : what(std::move(what_)), with(std::move(with_)) {} }; template Replace(T, T) -> Replace; template auto operator|(TIterable&& iterable, Replace()))>::value_type> repl) { return replace(std::forward(iterable), std::move(repl.what), std::move(repl.with)); } template struct Collect {}; template auto operator|(TIterable&& iterable, Collect) { return collect(std::forward(iterable)); } struct Chain {}; template struct Chain2 { RangeRef range; }; template auto operator|(TIterable&& iterable, Chain) { return Chain2{.range = {.range = std::forward(iterable)}}; } template auto operator|(Chain2 chain2, TSecondIterable&& secondIterable) { return chain(chain2.range.range, std::forward(secondIterable)); } template struct Map { T functor; explicit Map(T functor_) : functor(std::move(functor_)) {} }; template Map(T) -> Map; template auto operator|(TIterable&& iterable, Map mapper) { return map(std::forward(iterable), std::move(mapper.functor)); } template struct MapOptional { T functor; explicit MapOptional(T functor_) : functor(std::move(functor_)) {} }; template MapOptional(T) -> MapOptional; template auto operator|(TIterable&& iterable, MapOptional mapper) { return mapOptional(std::forward(iterable), std::move(mapper.functor)); } template struct Filter { T predicate; explicit Filter(T predicate_) : predicate(std::move(predicate_)) {} }; template Filter(T) -> Filter; template auto operator|(TIterable&& iterable, Filter filterer) { return filter(std::forward(iterable), std::move(filterer.predicate)); } template struct FilterType { }; template auto operator|(TIterable&& iterable, FilterType) { return filterType(std::forward(iterable)); } template struct Xth {}; using First = Xth<0>; using Second = Xth<1>; template auto operator|(TIterable&& iterable, Xth) { return map(std::forward(iterable), [](auto&& element) { return std::get(element); } ); } } // namespace pipe } // namespace mijin #endif // MIJIN_UTIL_ITERATORS_HPP_INCLUDED