diff --git a/source/mijin/util/iterators.hpp b/source/mijin/util/iterators.hpp index ac66334..b4c3b8e 100644 --- a/source/mijin/util/iterators.hpp +++ b/source/mijin/util/iterators.hpp @@ -169,6 +169,86 @@ Enumeratable enumerate(TIterable&& iterable) return {.base = {std::forward(iterable)}}; } +template +struct ZippingIterator +{ + TFirstIterator first; + TSecondIterator second; + + ZippingIterator(TFirstIterator first_, TSecondIterator second_) noexcept : first(first_), second(second_) {} + ZippingIterator(const ZippingIterator&) noexcept = default; + + ZippingIterator& operator=(const ZippingIterator&) noexcept = default; + + auto operator*() const noexcept + { + return std::tie(*first, *second); + } + + ZippingIterator& operator++() noexcept + { + ++first; + ++second; + return *this; + } + + ZippingIterator operator++(int) noexcept + { + ZippingIterator copy(*this); + ++(*this); + return copy; + } + + ZippingIterator& operator--() noexcept + { + --first; + --second; + return *this; + } + + ZippingIterator operator--(int) noexcept + { + ZippingIterator copy(*this); + --(*this); + return copy; + } + + bool operator==(const ZippingIterator& other) const 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 noexcept + { + return !(*this == other); + } +}; +template +ZippingIterator(TFirstIterator, TSecondIterator) -> ZippingIterator; + +template +struct ZippingRange : RangeAdapter +{ + RangeRef first; + RangeRef second; + + auto begin() const noexcept + { + return ZippingIterator(first.begin(), second.begin()); + } + + auto end() const 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 {