#pragma once /* * Copyright (C) 2024 Brett Terpstra * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLT_ITERATOR_H #define BLT_ITERATOR_H #include #include #include #include #include #include #include #include #include namespace blt { // forward declare useful types template> class enumerator; template> class enumerator_rev; template> class pair_iterator; template> class pair_iterator_rev; template class zip_iterator; template class zip_iterator_rev; namespace iterator { template> class enumerate_wrapper; template> class pair_wrapper; /** * struct which is returned by the enumerator. * @tparam T type to store. */ template struct enumerate_item { blt::size_t index; T value; }; /** * base class for iterators which operate on pairs of values. Handles comparison. * @tparam Iter1 first iterator type. this will be used for comparison. * @tparam Iter2 second iterator type. this value is not modified by this class. */ template class dual_iterator_base { public: explicit dual_iterator_base(Iter1 iter1, Iter2 iter2): m_iter1(std::move(iter1)), m_iter2(std::move(iter2)) {} friend bool operator==(const dual_iterator_base& a, const dual_iterator_base& b) { return a.m_iter1 == b.m_iter1; } friend bool operator!=(const dual_iterator_base& a, const dual_iterator_base& b) { return a.m_iter1 != b.m_iter1; } auto iter1() const { return m_iter1; } auto iter2() const { return m_iter2; } protected: Iter1 m_iter1; Iter2 m_iter2; }; /** * Base class for all enumerator iterators. Handles the deference (*) operator. * @tparam Iter iterator type */ template class enumerate_iterator_base : public dual_iterator_base { public: explicit enumerate_iterator_base(Iter iter, blt::size_t place = 0): dual_iterator_base(std::move(iter), place) {} enumerate_item> operator*() const { return {this->m_iter2, *this->m_iter1}; } }; template class pair_iterator_base : public dual_iterator_base { public: using dual_iterator_base::dual_iterator_base; std::pair, blt::meta::deref_return_t> operator*() const { return {*this->m_iter1, *this->m_iter2}; } }; /** * Forward iterator base class. Contains the ++ operator. * @tparam Base iterator base type. */ template class forward_iterator_base : public Base { public: using Base::Base; forward_iterator_base& operator++() { ++this->m_iter1; ++this->m_iter2; return *this; } forward_iterator_base operator++(int) { auto tmp = *this; ++*this; return tmp; } }; /** * Bidirectional iterator base class. Contains the -- operator. * @tparam Base iterator base type. */ template class bidirectional_iterator_base : public Base { public: using Base::Base; bidirectional_iterator_base& operator--() { --this->m_iter1; --this->m_iter2; return *this; } bidirectional_iterator_base operator--(int) { auto tmp = *this; --*this; return tmp; } }; template using enumerate_forward_iterator = forward_iterator_base>; template using enumerate_bidirectional_iterator = bidirectional_iterator_base>; template using pair_forward_iterator = forward_iterator_base>; template using pair_bidirectional_iterator = bidirectional_iterator_base>; /** * Enumerator wrapper class specialization for forward iterators. * @tparam Iter iterator type */ template class enumerate_wrapper, std::void_t>> : public enumerate_forward_iterator { public: using iterator_category = std::forward_iterator_tag; using value_type = enumerate_item>; using difference_type = typename std::iterator_traits::difference_type; using pointer = value_type; using reference = value_type; using enumerate_forward_iterator::enumerate_forward_iterator; }; /** * Pair wrapper class specialization for forward iterators. * @tparam Iter iterator type */ template class pair_wrapper>, std::void_t>> : public pair_forward_iterator { public: using iterator_category = std::forward_iterator_tag; using value_type = std::pair, blt::meta::deref_return_t>; using difference_type = std::common_type_t::difference_type, typename std::iterator_traits::difference_type>; using pointer = value_type; using reference = value_type; using pair_forward_iterator::pair_forward_iterator; }; /** * Enumerator wrapper class for bidirectional iterators or random access iterators. * @tparam Iter iterator type. */ template class enumerate_wrapper, std::void_t>> : public enumerate_bidirectional_iterator { public: using iterator_category = std::bidirectional_iterator_tag; using value_type = enumerate_item>; using difference_type = typename std::iterator_traits::difference_type; using pointer = value_type; using reference = value_type; using enumerate_bidirectional_iterator::enumerate_bidirectional_iterator; }; /** * Pair wrapper class for bidirectional iterators or random access iterators. * @tparam Iter iterator type. */ template class pair_wrapper>, std::void_t>> : public pair_bidirectional_iterator { public: using iterator_category = std::bidirectional_iterator_tag; using value_type = std::pair, blt::meta::deref_return_t>; using difference_type = std::common_type_t::difference_type, typename std::iterator_traits::difference_type>; using pointer = value_type; using reference = value_type; using pair_bidirectional_iterator::pair_bidirectional_iterator; }; /** * Base class for storing begin/end iterators. * @tparam IterWrapper wrapper used to iterate * @tparam CompleteClass completed class returned from skip/take methods */ template class iterator_storage_base { public: explicit iterator_storage_base(IterWrapper begin, IterWrapper end): begin_(std::move(begin)), end_(std::move(end)) {} auto begin() { return begin_; } auto end() { return end_; } /** * Creates an enumerator that skips the first n elements. * @param amount amount of values to skip. */ auto skip(blt::size_t amount) { auto begin = this->begin_; for (blt::size_t i = 0; i < amount; i++) ++begin; return CompleteClass{begin.iter1(), this->end_.iter1(), begin.iter2(), this->end_.iter2()}; } /** * Creates an enumerator that yields the first n elements, or UB if the underlying iterator ends sooner. * @param amount amount to take. */ auto take(blt::size_t amount) { auto end = this->begin(); for (blt::size_t i = 0; i < amount; i++) ++end; return CompleteClass{this->begin_.iter1(), end.iter1(), this->begin_.iter2(), end.iter2()}; } protected: IterWrapper begin_; IterWrapper end_; }; /** * Reversible (bidirectional) base class storing the begin / end iterators. * @tparam Iter iterator type. * @tparam IterWrapper wrapper used to iterate. * @tparam CompleteClass completed class returned from skip/take methods * @tparam CompleteClassRev reverse version of CompleteClass, returned from rev */ template class iterator_storage_reversible : public iterator_storage_base { public: explicit iterator_storage_reversible(IterWrapper begin, IterWrapper end): iterator_storage_base{std::move(begin), std::move(end)} {} /** * Reverses the enumerator’s direction. */ auto rev() const { return CompleteClassRev{this->end_.iter1(), this->begin_.iter1(), this->end_.iter2(), this->begin_.iter2()}; } }; /** * Random access base class storage for begin/end iterators. * Has updated skip and take methods which make use of the random access nature of the iterator. * @tparam Iter iterator type. * @tparam IterWrapper wrapper used to iterate. * @tparam CompleteClass completed class returned from skip/take methods * @tparam CompleteClassRev reverse version of CompleteClass, returned from rev */ template class iterator_storage_random_access : public iterator_storage_reversible { public: using iterator_storage_reversible::iterator_storage_reversible; auto skip(blt::size_t amount) { return CompleteClass{this->begin_.iter1() + amount, this->end_.iter1(), this->begin_.iter2() + amount, this->end_.iter2()}; } auto take(blt::size_t amount) { return CompleteClass{this->begin_.iter1(), this->begin_.iter1() + amount, this->begin_.iter2(), this->begin_.iter2() + amount}; } }; /** * Reversible (bidirectional) base class for storing the begin/end iterators, operates in reverse for reverse iteration. * @tparam Iter iterator type. * @tparam IterWrapper wrapper used to iterate (std::reverse_iterator). * @tparam CompleteClass completed class returned from skip/take methods * @tparam CompleteClassRev reverse version of CompleteClass, returned from rev */ template class iterator_storage_reversible_rev : public iterator_storage_reversible { public: using iterator_storage_reversible::iterator_storage_reversible; auto rev() const { return CompleteClass{this->end_.base().iter1(), this->begin_.base().iter1(), this->end_.base().iter2(), this->begin_.base().iter2()}; } auto skip(blt::size_t amount) { auto begin = this->begin_.base(); for (blt::size_t i = 0; i < amount; i++) --begin; return CompleteClassRev{begin.iter1(), this->end_.base().iter1(), begin.iter2(), this->end_.base().iter2()}; } auto take(blt::size_t amount) { auto end = this->begin_.base(); for (blt::size_t i = 0; i < amount; i++) --end; return CompleteClassRev{ this->begin_.base().iter1(), end.iter1(), this->begin_.base().iter2(), end.iter2()}; } }; /** * Random access base class for storing the begin/end iterator. * Has updated skip and take methods which make use of the random access nature of the iterator. * Operates in reverse for reverse iteration. * @tparam Iter iterator type. * @tparam IterWrapper wrapper used to iterate (std::reverse_iterator). * @tparam CompleteClass completed class returned from skip/take methods * @tparam CompleteClassRev reverse version of CompleteClass, returned from rev */ template class iterator_storage_random_access_rev : public iterator_storage_reversible_rev { public: using iterator_storage_reversible_rev::iterator_storage_reversible_rev; auto skip(blt::size_t amount) { return CompleteClassRev{this->begin_.base().iter1() - amount, this->end_.base().iter1(), this->begin_.base().iter2() - amount, this->end_.base().iter2()}; } auto take(blt::size_t amount) { return CompleteClassRev{this->begin_.base().iter1(), this->begin_.base().iter1() - amount, this->begin_.base().iter2(), this->begin_.base().iter2() - amount}; } }; /** * Base class for types which can be converted to an enumerator */ template class enumerator_convertible { public: auto enumerate() { auto* b = static_cast(this); return CompleteEnumerator{b->begin(), b->end(), static_cast(std::distance(b->begin(), b->end()))}; } }; } /** * Enumerator specialization for forward iterators */ template class enumerator, std::void_t>> : public iterator::iterator_storage_base, enumerator> { public: explicit enumerator(Iter begin, Iter end, blt::size_t container_size): iterator::iterator_storage_base, enumerator> {iterator::enumerate_wrapper{std::move(begin), 0}, iterator::enumerate_wrapper{std::move(end), container_size}} {} explicit enumerator(Iter begin, Iter end, blt::size_t begin_index, blt::size_t end_index): iterator::iterator_storage_base, enumerator>{ iterator::enumerate_wrapper{std::move(begin), begin_index}, iterator::enumerate_wrapper{std::move(end), end_index}} {} }; /** * Enumerator specialization for bidirectional iterators */ template class enumerator, std::void_t>> : public iterator::iterator_storage_reversible, enumerator, enumerator_rev> { public: explicit enumerator(Iter begin, Iter end, blt::size_t container_size): iterator::iterator_storage_reversible, enumerator, enumerator_rev> {iterator::enumerate_wrapper{std::move(begin), 0}, iterator::enumerate_wrapper{std::move(end), container_size}} {} explicit enumerator(Iter begin, Iter end, blt::size_t begin_index, blt::size_t end_index): iterator::iterator_storage_reversible, enumerator, enumerator_rev>{ iterator::enumerate_wrapper{std::move(begin), begin_index}, iterator::enumerate_wrapper{std::move(end), end_index}} {} }; /** * Enumerator specialization for random access iterators */ template class enumerator, std::void_t>> : public iterator::iterator_storage_random_access, enumerator, enumerator_rev> { public: explicit enumerator(Iter begin, Iter end, blt::size_t container_size): iterator::iterator_storage_random_access, enumerator, enumerator_rev> {iterator::enumerate_wrapper{std::move(begin), 0}, iterator::enumerate_wrapper{std::move(end), container_size}} {} explicit enumerator(Iter begin, Iter end, blt::size_t begin_index, blt::size_t end_index): iterator::iterator_storage_random_access, enumerator, enumerator_rev>{ iterator::enumerate_wrapper{std::move(begin), begin_index}, iterator::enumerate_wrapper{std::move(end), end_index}} {} }; /** * Reverse enumerator specialization for bidirectional iterators */ template class enumerator_rev, std::void_t>> : public iterator::iterator_storage_reversible_rev>, enumerator, enumerator_rev> { public: explicit enumerator_rev(Iter begin, Iter end, blt::size_t container_size): iterator::iterator_storage_reversible_rev>, enumerator, enumerator_rev> {std::reverse_iterator>{iterator::enumerate_wrapper{std::move(begin), 0}}, std::reverse_iterator>{ iterator::enumerate_wrapper{std::move(end), container_size}}} {} explicit enumerator_rev(Iter begin, Iter end, blt::size_t begin_index, blt::size_t end_index): iterator::iterator_storage_reversible_rev>, enumerator, enumerator_rev>{ std::reverse_iterator>{ iterator::enumerate_wrapper{std::move(begin), begin_index}}, std::reverse_iterator>{iterator::enumerate_wrapper{std::move(end), end_index}}} {} }; /** * Reverse enumerator specialization for random access iterators */ template class enumerator_rev, std::void_t>> : public iterator::iterator_storage_random_access_rev>, enumerator, enumerator_rev> { public: explicit enumerator_rev(Iter begin, Iter end, blt::size_t container_size): iterator::iterator_storage_random_access_rev>, enumerator, enumerator_rev> {std::reverse_iterator>{iterator::enumerate_wrapper{std::move(begin), 0}}, std::reverse_iterator>{ iterator::enumerate_wrapper{std::move(end), container_size}}} {} explicit enumerator_rev(Iter begin, Iter end, blt::size_t begin_index, blt::size_t end_index): iterator::iterator_storage_random_access_rev>, enumerator, enumerator_rev>{ std::reverse_iterator>{ iterator::enumerate_wrapper{std::move(begin), begin_index}}, std::reverse_iterator>{iterator::enumerate_wrapper{std::move(end), end_index}}} {} }; // CTAD for enumerators template enumerator(Iter, Iter) -> enumerator; template enumerator(Iter, Iter, blt::size_t) -> enumerator; template enumerator(Iter, Iter, blt::size_t, blt::size_t) -> enumerator; template class pair_iterator>, std::void_t>> : public iterator::iterator_storage_base, pair_iterator>, public iterator::enumerator_convertible, enumerator>> { public: explicit pair_iterator(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2): iterator::iterator_storage_base, pair_iterator> {iterator::pair_wrapper{std::move(begin1), std::move(begin2)}, iterator::pair_wrapper{std::move(end1), std::move(end2)}} {} }; template class pair_iterator>, std::void_t>> : public iterator::iterator_storage_reversible, pair_iterator, pair_iterator_rev>, public iterator::enumerator_convertible, enumerator>> { public: explicit pair_iterator(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2): iterator::iterator_storage_reversible, pair_iterator, pair_iterator_rev> {iterator::pair_wrapper{std::move(begin1), std::move(begin2)}, iterator::pair_wrapper{std::move(end1), std::move(end2)}} {} }; template class pair_iterator>, std::void_t>> : public iterator::iterator_storage_random_access, pair_iterator, pair_iterator_rev>, public iterator::enumerator_convertible, enumerator>> { public: explicit pair_iterator(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2): iterator::iterator_storage_random_access, pair_iterator, pair_iterator_rev> {iterator::pair_wrapper{std::move(begin1), std::move(begin2)}, iterator::pair_wrapper{std::move(end1), std::move(end2)}} {} }; template class pair_iterator_rev>, std::void_t>> : public iterator::iterator_storage_reversible_rev>, pair_iterator, pair_iterator_rev>, public iterator::enumerator_convertible, enumerator>> { public: explicit pair_iterator_rev(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2): iterator::iterator_storage_reversible_rev>, pair_iterator, pair_iterator_rev> {std::reverse_iterator>{ iterator::pair_wrapper{std::move(begin1), std::move(begin2)}}, std::reverse_iterator>{ iterator::pair_wrapper{std::move(end1), std::move(end2)}}} {} }; template class pair_iterator_rev>, std::void_t>> : public iterator::iterator_storage_random_access_rev>, pair_iterator, pair_iterator_rev>, public iterator::enumerator_convertible, enumerator>> { public: explicit pair_iterator_rev(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2): iterator::iterator_storage_random_access_rev>, pair_iterator, pair_iterator_rev> {std::reverse_iterator>{ iterator::pair_wrapper{std::move(begin1), std::move(begin2)}}, std::reverse_iterator>{ iterator::pair_wrapper{std::move(end1), std::move(end2)}}} {} }; // CTAD for pair iterators template pair_iterator(Iter1, Iter1, Iter2, Iter2) -> pair_iterator; template static inline auto enumerate(const T(& container)[size]) { return enumerator{&container[0], &container[size], size}; } template static inline auto enumerate(T(& container)[size]) { return enumerator{&container[0], &container[size], size}; } template static inline auto enumerate(T& container) { return enumerator{container.begin(), container.end(), container.size()}; } template static inline auto enumerate(T&& container) { return enumerator{container.begin(), container.end(), container.size()}; } template static inline auto enumerate(const T& container) { return enumerator{container.begin(), container.end(), container.size()}; } template static inline auto in_pairs(const T& container1, const G& container2) { return pair_iterator{container1.begin(), container1.end(), container2.begin(), container2.end()}; } template static inline auto in_pairs(T& container1, G& container2) { return pair_iterator{container1.begin(), container1.end(), container2.begin(), container2.end()}; } template static inline auto in_pairs(const T(& container1)[size], const G(& container2)[size]) { return pair_iterator{&container1[0], &container1[size], &container2[0], &container2[size]}; } template static inline auto in_pairs(T(& container1)[size], G(& container2)[size]) { return pair_iterator{&container1[0], &container1[size], &container2[0], &container2[size]}; } template static inline auto in_pairs(T&& container1, G&& container2) { return pair_iterator{container1.begin(), container1.end(), container2.begin(), container2.end()}; } } #endif //BLT_ITERATOR_H