barretenberg
Loading...
Searching...
No Matches
zip_view.hpp
1#pragma once
2/* ********************************* FILE ************************************/
75static_assert(__cplusplus >= 201703L,
76 " must be c++17 or greater"); // could be rewritten in c++11, but the features you must use will be buggy
77 // in an older compiler anyways.
78#include "barretenberg/common/assert.hpp"
79#include <cassert>
80#include <functional>
81#include <iostream>
82#include <sstream>
83#include <tuple>
84#include <type_traits>
85#include <vector>
86
87template <class T>
94 public:
95 // speeds up compilation a little bit...
96 using tuple_indexes = std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<T>>>;
97
98 zip_iterator(T iter, T iter_end)
99 : iter(iter)
100 , iter_end(iter_end)
101 {}
102 // prefix, inc first, then return
103 zip_iterator& operator++()
104 {
105 for_each_in_tuple([](auto&& x) { return x++; }, iter);
106 // then if any hit end, update all to point to end.
107 auto end = apply2([](auto x, auto y) { return x == y; }, iter, iter_end);
108 if (if_any_in(end)) {
109 apply2([](auto& x, auto y) { return x = y; }, iter, iter_end);
110 }
111 index++;
112 return *this;
113 }
114 // sufficient because ++ keeps track and sets all to end when any is
115 bool operator!=(const zip_iterator& other) const { return other.iter != iter; }
116 auto operator*() const
117 {
118 return std::forward<decltype(get_refs(iter, tuple_indexes{}))>(get_refs(iter, tuple_indexes{}));
119 }
120
121 private:
122 T iter, iter_end;
123 std::size_t index = 0;
124
125 template <std::size_t... I> auto get_refs(T t, std::index_sequence<I...>) const
126 {
127 return std::make_tuple(std::ref(*std::get<I>(t))...);
128 }
129
130 template <class F, class A, std::size_t... I> auto apply2_impl(F&& f, A&& a, A&& b, std::index_sequence<I...>)
131 {
132 return std::make_tuple(f(std::get<I>(a), std::get<I>(b))...);
133 }
134 template <class F, class A> auto apply2(F&& f, A&& a, A&& b)
135 {
136 return apply2_impl(std::forward<F>(f), std::forward<A>(a), std::forward<A>(b), tuple_indexes{});
137 }
138 template <class A, std::size_t... I> bool if_any_impl(const A& t, std::index_sequence<I...>) const
139 {
140 return (... || std::get<I>(t)); // c++17
141 }
142
143 // in general context we must enforce that these are tuples
144 template <class A> bool if_any_in(A&& t) const { return if_any_impl(std::forward<A>(t), tuple_indexes{}); }
145
146 template <class F, class Tuple, std::size_t... I>
147 auto for_each_in_impl(F&& f, Tuple&& t, std::index_sequence<I...>) const
148 {
149 return std::make_tuple(f(std::get<I>(t))...);
150 }
151
152 template <class F, class A> void for_each_in_tuple(F&& f, A&& t) const
153 {
154 for_each_in_impl(std::forward<F>(f), std::forward<A>(t), tuple_indexes{});
155 }
156};
157
158enum class ZipAllowDifferentSizes { FLAG };
159template <class... S> class zip_view {
160 using arg_indexes = std::make_index_sequence<sizeof...(S)>;
161
162 public:
163 zip_view(S... args)
164 : args(std::forward<S>(args)...)
165 {
166 // min size matches max size
167 ASSERT(size() == max_size_impl(arg_indexes{}));
168 }
169 zip_view(ZipAllowDifferentSizes /*unused*/, S... args)
170 : args(std::forward<S>(args)...)
171 {
172 // Same in a release build, in a debug build doesn't error with different container sizes
173 }
174 auto begin() const { return get_begins(arg_indexes{}); }
175 auto end() const { return get_ends(arg_indexes{}); }
176 [[nodiscard]] std::size_t size() const { return size_impl(arg_indexes{}); }
177
178 private:
179 std::tuple<S...> args;
180 template <std::size_t... I> auto get_begins(std::index_sequence<I...>) const
181 {
182 return zip_iterator(std::make_tuple(std::get<I>(args).begin()...), std::make_tuple(std::get<I>(args).end()...));
183 }
184 template <std::size_t... I> auto get_ends(std::index_sequence<I...>) const
185 {
186 return zip_iterator(std::make_tuple(std::get<I>(args).end()...), std::make_tuple(std::get<I>(args).end()...));
187 }
188 template <std::size_t... I> auto size_impl(std::index_sequence<I...>) const
189 {
190 return std::min({ std::size_t(std::get<I>(args).size())... });
191 }
192 template <std::size_t... I> auto max_size_impl(std::index_sequence<I...>) const
193 {
194 return std::max({ std::size_t(std::get<I>(args).size())... });
195 }
196
197 template <class A, std::size_t... I> bool if_any_impl(const A& t, std::index_sequence<I...>) const
198 {
199 return (... || std::get<I>(t)); // c++17
200 }
201};
202
203// deduction guide,
204template <class... S> zip_view(S&&...) -> zip_view<S...>;
205
206// deduction guide,
207template <class... S> zip_view(ZipAllowDifferentSizes, S&&...) -> zip_view<S...>;
The zip_iterator class.
Definition: zip_view.hpp:93
Definition: zip_view.hpp:159