#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_ZIP #define BLT_ITERATOR_ZIP #include #include #include #include namespace blt { namespace iterator { template struct zip_wrapper : public base_wrapper> { public: using iterator_category = meta::lowest_iterator_category_t; using value_type = std::tuple...>; using difference_type = blt::ptrdiff_t; using pointer = value_type; using reference = value_type; explicit zip_wrapper(Iter... iter): iter(std::make_tuple(iter...)) {} std::tuple...> operator*() const { return std::apply([](auto& ... i) { return std::tuple...>{*i...}; }, iter); } zip_wrapper& operator++() { std::apply([](auto& ... i) { ((++i), ...); }, iter); return *this; } zip_wrapper& operator--() { std::apply([](auto& ... i) { ((--i), ...); }, this->iter); return *this; } friend zip_wrapper operator+(const zip_wrapper& a, blt::ptrdiff_t n) { static_assert(std::is_same_v, "Iterator must allow random access"); return std::apply([n](auto& ... i) { return zip_wrapper((i + n)...); }, a.iter); } friend zip_wrapper operator-(const zip_wrapper& a, blt::ptrdiff_t n) { static_assert(std::is_same_v, "Iterator must allow random access"); return std::apply([n](auto& ... i) { return zip_wrapper((i - n)...); }, a.iter); } friend blt::ptrdiff_t operator-(const zip_wrapper& a, const zip_wrapper& b) { return sub(a, b, std::index_sequence_for()); } auto base() { return iter; } protected: std::tuple iter; template static blt::ptrdiff_t sub(const zip_wrapper& a, const zip_wrapper& b, std::integer_sequence) { blt::ptrdiff_t min = std::numeric_limits::max(); ((min = std::min(min, std::get(a.iter) - std::get(b.iter))), ...); return min; } }; template struct skip_wrapper : public passthrough_wrapper> { public: using iterator_category = typename std::iterator_traits::iterator_category; using value_type = typename std::iterator_traits::value_type; using difference_type = typename std::iterator_traits::difference_type; using pointer = typename std::iterator_traits::pointer; using reference = typename std::iterator_traits::reference; explicit skip_wrapper(Iter iter, blt::size_t n): passthrough_wrapper>(std::move(iter)), skip(n) {} skip_wrapper& operator++() { if constexpr (std::is_same_v) { if (skip > 0) { this->base() += skip; skip = 0; } } else { while (skip > 0) { ++this->base(); --skip; } } ++this->base(); return *this; } skip_wrapper& operator--() { if constexpr (std::is_same_v) { if (skip > 0) { this->base() -= skip; skip = 0; } } else { while (skip > 0) { --this->base(); --skip; } } --this->base(); return *this; } friend skip_wrapper operator+(const skip_wrapper& a, blt::ptrdiff_t n) { static_assert(std::is_same_v, "Iterator must allow random access"); return {a.base() + static_cast(a.skip) + n, 0}; } friend skip_wrapper operator-(const skip_wrapper& a, blt::ptrdiff_t n) { static_assert(std::is_same_v, "Iterator must allow random access"); return {a.base() - static_cast(a.skip) - n, 0}; } private: blt::size_t skip; }; } template struct iterator_pair { using type = Iter; iterator_pair(Iter begin, Iter end): begin(std::move(begin)), end(std::move(end)) {} Iter begin; Iter end; }; template class zip_iterator_storage; template class zip_iterator_storage_rev; template class zip_iterator_storage { public: using iterator_category = meta::lowest_iterator_category_t; public: zip_iterator_storage(iterator_pair... iterator_pairs): m_begins(std::move(iterator_pairs.begin)...), m_ends(std::move(iterator_pairs.end)...) {} zip_iterator_storage(iterator::zip_wrapper begins, iterator::zip_wrapper ends): m_begins(std::move(begins)), m_ends(std::move(ends)) {} auto rev() { static_assert((std::is_same_v || std::is_same_v), ".rev() must be used with bidirectional (or better) iterators!"); return zip_iterator_storage_rev{m_ends, m_begins}; } auto skip(blt::size_t n) { } auto begin() const { return m_begins; } auto end() const { return m_ends; } private: iterator::zip_wrapper m_begins; iterator::zip_wrapper m_ends; }; template class zip_iterator_storage_rev { public: using iterator_category = meta::lowest_iterator_category_t; public: zip_iterator_storage_rev(iterator_pair... iterator_pairs): m_begins(iterator_pairs.begin...), m_ends(iterator_pairs.end...) { static_assert((std::is_same_v || std::is_same_v), "reverse iteration is only supported on bidirectional or better iterators!"); } zip_iterator_storage_rev(iterator::zip_wrapper begins, iterator::zip_wrapper ends): m_begins(std::move(begins)), m_ends(std::move(ends)) { static_assert((std::is_same_v || std::is_same_v), "reverse iteration is only supported on bidirectional or better iterators!"); } auto rev() { return zip_iterator_storage{m_ends.base(), m_begins.base()}; } auto begin() const { return m_begins; } auto end() const { return m_ends; } private: std::reverse_iterator> m_begins; std::reverse_iterator> m_ends; }; /* * CTAD for the zip containers */ template zip_iterator_storage(iterator_pair...) -> zip_iterator_storage; template zip_iterator_storage(std::initializer_list...) -> zip_iterator_storage; template zip_iterator_storage_rev(iterator_pair...) -> zip_iterator_storage_rev; template zip_iterator_storage_rev(std::initializer_list...) -> zip_iterator_storage_rev; /* * Helper functions for creating zip containers */ template auto zip(Container& ... container) { return zip_iterator_storage{iterator_pair{container.begin(), container.end()}...}; } } #endif //BLT_ITERATOR_ZIP