SeqAn3 3.2.0-rc.1
The Modern C++ library for sequence analysis.
zip.hpp
Go to the documentation of this file.
1// -----------------------------------------------------------------------------------------------------
2// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
3// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik
4// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
5// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
6// -----------------------------------------------------------------------------------------------------
7
13#pragma once
14
15#include <algorithm>
16#include <seqan3/std/ranges>
17
22
24// Contains helpers for seqan3::views::zip.
25namespace seqan3::detail::zip
26{
27
28// https://eel.is/c++draft/range.zip#concept:zip-is-common
29template <typename... range_ts>
30concept zip_is_common = (sizeof...(range_ts) == 1 && (std::ranges::common_range<range_ts> && ...))
31 || (!(std::ranges::bidirectional_range<range_ts> && ...)
32 && (std::ranges::common_range<range_ts> && ...))
33 || ((std::ranges::random_access_range<range_ts> && ...)
34 && (std::ranges::sized_range<range_ts> && ...));
35
36// Helper for tuple_or_pair
37template <typename... ts>
38struct tuple_or_pair_impl;
39
40// Helper for tuple_or_pair
41template <typename... ts>
42 requires (sizeof...(ts) != 2)
43struct tuple_or_pair_impl<ts...>
44{
45 using type = seqan3::common_tuple<ts...>;
46};
47
48// Helper for tuple_or_pair
49template <typename... ts>
50 requires (sizeof...(ts) == 2)
51struct tuple_or_pair_impl<ts...>
52{
53 using type = seqan3::common_pair<ts...>;
54};
55
56// https://eel.is/c++draft/range.zip#view-1
57template <typename... ts>
58using tuple_or_pair = tuple_or_pair_impl<ts...>::type;
59
60// std::abs has problems with ambiguous overloads
61template <typename t>
62constexpr t abs(t && v) noexcept
63{
64 if constexpr (std::is_signed_v<t>)
65 return v < 0 ? -v : v;
66 else
67 return v;
68}
69
70// Returns a new tuple containing the result of applying a function to each tuple element.
71// https://eel.is/c++draft/range.zip.view
72template <typename fun_t, typename tuple_t>
73constexpr auto tuple_transform(fun_t && f, tuple_t && tuple)
74{
75 return std::apply(
76 [&]<typename... ts>(ts &&... elements)
77 {
78 return tuple_or_pair<std::invoke_result_t<fun_t &, ts>...>{std::invoke(f, std::forward<ts>(elements))...};
79 },
80 std::forward<tuple_t>(tuple));
81}
82
83// Applies a function to each tuple element.
84// https://eel.is/c++draft/range.zip.view
85template <typename fun_t, typename tuple_t>
86constexpr void tuple_for_each(fun_t && f, tuple_t && tuple)
87{
89 [&]<typename... ts>(ts &&... elements)
90 {
91 (std::invoke(f, std::forward<ts>(elements)), ...);
92 },
93 std::forward<tuple_t>(tuple));
94}
95
96template <bool is_const, typename... range_ts>
97concept all_random_access = (std::ranges::random_access_range<view_helper::maybe_const<is_const, range_ts>> && ...);
98
99template <bool is_const, typename... range_ts>
100concept all_bidirectional = (std::ranges::bidirectional_range<view_helper::maybe_const<is_const, range_ts>> && ...);
101
102template <bool is_const, typename... range_ts>
103concept all_forward = (std::ranges::forward_range<view_helper::maybe_const<is_const, range_ts>> && ...);
104
105// Pre cpp20-iterators: Infer iterator_category from modelled concepts.
106#if defined(__GNUC__) && (__GNUC__ == 10)
107template <bool is_const, typename... range_ts>
108concept all_contiguous = (std::ranges::contiguous_range<view_helper::maybe_const<is_const, range_ts>> && ...);
109
110template <bool Const, typename... Views>
111struct iterator_category_t
112{
113 using iterator_category = std::conditional_t<
114 all_contiguous<Const, Views...>,
115 std::contiguous_iterator_tag,
117 all_random_access<Const, Views...>,
120 all_bidirectional<Const, Views...>,
123};
124#else // cpp20 iterators
125template <bool>
126struct iterator_category_t;
127template <>
128struct iterator_category_t<true>
129{
130 using iterator_category = std::input_iterator_tag;
131};
132template <>
133struct iterator_category_t<false>
134{};
135#endif
136
137} // namespace seqan3::detail::zip
138
139namespace seqan3::detail
140{
141
142template <std::ranges::input_range... Views>
143 requires (std::ranges::view<Views> && ...) && (sizeof...(Views) > 0)
144class zip_view : public std::ranges::view_interface<zip_view<Views...>>
145{
146 seqan3::common_tuple<Views...> views_;
147
148 template <bool>
149 class iterator;
150
151 template <bool>
152 class sentinel;
153
154public:
155 zip_view()
156 requires (std::is_default_constructible_v<Views> && ...)
157 = default;
158 constexpr explicit zip_view(Views... views) : views_(std::move(views)...)
159 {}
160
161 constexpr auto begin()
162 requires (!(view_helper::simple_view<Views> && ...))
163 {
164 return iterator<false>(zip::tuple_transform(std::ranges::begin, views_));
165 }
166
167 constexpr auto begin() const
168 requires (std::ranges::range<Views const> && ...)
169 {
170 return iterator<true>(zip::tuple_transform(std::ranges::begin, views_));
171 }
172
173 constexpr auto end()
174 requires (!(view_helper::simple_view<Views> && ...))
175 {
176 if constexpr (!zip::zip_is_common<Views...>)
177 return sentinel<false>(zip::tuple_transform(std::ranges::end, views_));
178 else if constexpr ((std::ranges::random_access_range<Views> && ...))
180 else
181 return iterator<false>(zip::tuple_transform(std::ranges::end, views_));
182 }
183
184 constexpr auto end() const
185 requires (std::ranges::range<Views const> && ...)
186 {
187 if constexpr (!zip::zip_is_common<Views const...>)
188 return sentinel<true>(zip::tuple_transform(std::ranges::end, views_));
189 else if constexpr ((std::ranges::random_access_range<Views const> && ...))
191 else
192 return iterator<true>(zip::tuple_transform(std::ranges::end, views_));
193 }
194
195 constexpr auto size()
196 requires (std::ranges::sized_range<Views> && ...)
197 {
198 return std::apply(
199 [](auto... sizes)
200 {
201 using common_size_t = std::make_unsigned_t<std::common_type_t<decltype(sizes)...>>;
202 return std::ranges::min({static_cast<common_size_t>(sizes)...});
203 },
204 zip::tuple_transform(std::ranges::size, views_));
205 }
206
207 constexpr auto size() const
208 requires (std::ranges::sized_range<Views const> && ...)
209 {
210 return std::apply(
211 [](auto... sizes)
212 {
213 using common_size_t = std::make_unsigned_t<std::common_type_t<decltype(sizes)...>>;
214 return std::ranges::min({static_cast<common_size_t>(sizes)...});
215 },
216 zip::tuple_transform(std::ranges::size, views_));
217 }
218};
219
220template <typename... range_ts>
221zip_view(range_ts &&...) -> zip_view<seqan3::detail::all_t<range_ts>...>;
222
223template <std::ranges::input_range... Views>
224 requires (std::ranges::view<Views> && ...) && (sizeof...(Views) > 0)
225template <bool Const>
226#if defined(__GNUC__) && (__GNUC__ == 10) // cpp17 iterators
227class zip_view<Views...>::iterator : public zip::iterator_category_t<Const, Views...>
228#else // cpp20 iterators
229class zip_view<Views...>::iterator : public zip::iterator_category_t<zip::all_forward<Const, Views...>>
230#endif
231{
232private:
233 constexpr explicit iterator(
234 zip::tuple_or_pair<std::ranges::iterator_t<view_helper::maybe_const<Const, Views>>...> current) :
235 current_(std::move(current))
236 {}
237
238 friend class zip_view<Views...>;
239
240public:
241 // Exposition-only. Is public for access via friend operator== of the sentinel.
242 zip::tuple_or_pair<std::ranges::iterator_t<view_helper::maybe_const<Const, Views>>...> current_;
243
244 using iterator_concept = std::conditional_t<
245 zip::all_random_access<Const, Views...>,
248 zip::all_bidirectional<Const, Views...>,
250 std::conditional_t<zip::all_forward<Const, Views...>, std::forward_iterator_tag, std::input_iterator_tag>>>;
251 using value_type = zip::tuple_or_pair<std::ranges::range_value_t<view_helper::maybe_const<Const, Views>>...>;
252 using difference_type =
254
255 iterator() = default;
256 constexpr iterator(iterator<!Const> i)
257 requires Const
258 && (std::convertible_to<std::ranges::iterator_t<Views>,
259 std::ranges::iterator_t<view_helper::maybe_const<Const, Views>>>
260 && ...)
261 : current_(std::move(i.current))
262 {}
263
264 constexpr auto operator*() const
265 {
266 return zip::tuple_transform(
267 [](auto & i) -> decltype(auto)
268 {
269 return *i;
270 },
271 current_);
272 }
273
274 constexpr iterator & operator++()
275 {
276 zip::tuple_for_each(
277 [](auto & i)
278 {
279 ++i;
280 },
281 current_);
282 return *this;
283 }
284
285 constexpr void operator++(int)
286 {
287 ++*this;
288 }
289
290 constexpr iterator operator++(int)
291 requires zip::all_forward<Const, Views...>
292 {
293 auto tmp = *this;
294 ++*this;
295 return tmp;
296 }
297
298 constexpr iterator & operator--()
299 requires zip::all_bidirectional<Const, Views...>
300 {
301 zip::tuple_for_each(
302 [](auto & i)
303 {
304 --i;
305 },
306 current_);
307 return *this;
308 }
309
310 constexpr iterator operator--(int)
311 requires zip::all_bidirectional<Const, Views...>
312 {
313 auto tmp = *this;
314 --*this;
315 return tmp;
316 }
317
318 constexpr iterator & operator+=(difference_type x)
319 requires zip::all_random_access<Const, Views...>
320 {
321 zip::tuple_for_each(
322 [&]<typename I>(I & i)
323 {
325 },
326 current_);
327 return *this;
328 }
329
330 constexpr iterator & operator-=(difference_type x)
331 requires zip::all_random_access<Const, Views...>
332 {
333 zip::tuple_for_each(
334 [&]<typename I>(I & i)
335 {
337 },
338 current_);
339 return *this;
340 }
341
342 constexpr auto operator[](difference_type n) const
343 requires zip::all_random_access<Const, Views...>
344 {
345 return zip::tuple_transform(
346 [&]<typename I>(I & i) -> decltype(auto)
347 {
348 return i[std::iter_difference_t<I>(n)];
349 },
350 current_);
351 }
352
353 friend constexpr bool operator==(iterator const & x, iterator const & y)
354 requires (std::equality_comparable<std::ranges::iterator_t<view_helper::maybe_const<Const, Views>>> && ...)
355 {
356 if constexpr (zip::all_bidirectional<Const, Views...>)
357 {
358 return x.current_ == y.current_;
359 }
360 else
361 {
362 return [&]<size_t... N>(std::integer_sequence<size_t, N...>)
363 {
364 return ((std::get<N>(x.current_) == std::get<N>(y.current_)) || ...);
365 }
366 (std::index_sequence_for<Views...>{});
367 }
368 }
369
370 friend constexpr bool operator<(iterator const & x, iterator const & y)
371 requires zip::all_random_access<Const, Views...>
372 {
373 return x.current_ < y.current_;
374 }
375
376 friend constexpr bool operator>(iterator const & x, iterator const & y)
377 requires zip::all_random_access<Const, Views...>
378 {
379 return y < x;
380 }
381
382 friend constexpr bool operator<=(iterator const & x, iterator const & y)
383 requires zip::all_random_access<Const, Views...>
384 {
385 return !(y < x);
386 }
387
388 friend constexpr bool operator>=(iterator const & x, iterator const & y)
389 requires zip::all_random_access<Const, Views...>
390 {
391 return !(x < y);
392 }
393
394#ifdef __cpp_lib_three_way_comparison
395 friend constexpr auto operator<=>(iterator const & x, iterator const & y)
396 requires zip::all_random_access<Const, Views...>
397 && (std::three_way_comparable<std::ranges::iterator_t<view_helper::maybe_const<Const, Views>>> && ...)
398 {
399 return x.current_ <=> y.current_;
400 }
401#endif
402
403 friend constexpr iterator operator+(iterator const & i, difference_type n)
404 requires zip::all_random_access<Const, Views...>
405 {
406 auto r = i;
407 r += n;
408 return r;
409 }
410
411 friend constexpr iterator operator+(difference_type n, iterator const & i)
412 requires zip::all_random_access<Const, Views...>
413 {
414 return i + n;
415 }
416
417 friend constexpr iterator operator-(iterator const & i, difference_type n)
418 requires zip::all_random_access<Const, Views...>
419 {
420 auto r = i;
421 r -= n;
422 return r;
423 }
424
425 friend constexpr difference_type operator-(iterator const & x, iterator const & y)
426 requires (std::sized_sentinel_for<std::ranges::iterator_t<view_helper::maybe_const<Const, Views>>,
427 std::ranges::iterator_t<view_helper::maybe_const<Const, Views>>>
428 && ...)
429 {
430 return [&]<size_t... N>(std::integer_sequence<size_t, N...>)
431 {
432 return std::ranges::min(
433 {static_cast<difference_type>(std::get<N>(x.current_) - std::get<N>(y.current_))...},
434 [](difference_type a, difference_type b)
435 {
436 return zip::abs(b) < zip::abs(a);
437 });
438 }
439 (std::index_sequence_for<Views...>{});
440 }
441
442 friend constexpr auto iter_move(iterator const & i) noexcept(
443 (noexcept(std::ranges::iter_move(
444 std::declval<std::ranges::iterator_t<view_helper::maybe_const<Const, Views>> const &>()))
445 && ...)
447 std::ranges::range_rvalue_reference_t<view_helper::maybe_const<Const, Views>>>
448 && ...))
449 {
450 return zip::tuple_transform(std::ranges::iter_move, i.current_);
451 }
452
453 friend constexpr void iter_swap(iterator const & l, iterator const & r) noexcept(
454 (noexcept(std::ranges::iter_swap(
455 std::declval<std::ranges::iterator_t<view_helper::maybe_const<Const, Views>> const &>(),
456 std::declval<std::ranges::iterator_t<view_helper::maybe_const<Const, Views>> const &>()))
457 && ...))
458 requires (std::indirectly_swappable<std::ranges::iterator_t<view_helper::maybe_const<Const, Views>>> && ...)
459 {
460 [&]<size_t... N>(std::integer_sequence<size_t, N...>)
461 {
462 (std::ranges::iter_swap(std::get<N>(l.current_), std::get<N>(r.current)), ...);
463 }
464 (std::index_sequence_for<Views...>{});
465 }
466};
467
468template <std::ranges::input_range... Views>
469 requires (std::ranges::view<Views> && ...) && (sizeof...(Views) > 0)
470template <bool Const>
471class zip_view<Views...>::sentinel
472{
473private:
474 constexpr explicit sentinel(
475 zip::tuple_or_pair<std::ranges::sentinel_t<view_helper::maybe_const<Const, Views>>...> end) :
476 end_(std::move(end))
477 {}
478
479 friend class zip_view<Views...>;
480
481public:
482 // Exposition-only. Is public such that it can be accessed by friends.
483 zip::tuple_or_pair<std::ranges::sentinel_t<view_helper::maybe_const<Const, Views>>...> end_;
484
485 sentinel() = default;
486 constexpr sentinel(sentinel<!Const> i)
487 requires Const
488 && (std::convertible_to<std::ranges::sentinel_t<Views>,
489 std::ranges::sentinel_t<view_helper::maybe_const<Const, Views>>>
490 && ...)
491 : end_(std::move(i.end_))
492 {}
493
494 template <bool OtherConst>
495 requires (std::sentinel_for<std::ranges::sentinel_t<view_helper::maybe_const<Const, Views>>,
496 std::ranges::iterator_t<view_helper::maybe_const<OtherConst, Views>>>
497 && ...)
498 friend constexpr bool operator==(iterator<OtherConst> const & x, sentinel const & y)
499 {
500 return [&]<size_t... N>(std::integer_sequence<size_t, N...>)
501 {
502 return ((std::get<N>(x.current_) == std::get<N>(y.end_)) || ...);
503 }
504 (std::index_sequence_for<Views...>{});
505 }
506
507 template <bool OtherConst>
508 requires (std::sized_sentinel_for<std::ranges::sentinel_t<view_helper::maybe_const<Const, Views>>,
509 std::ranges::iterator_t<view_helper::maybe_const<OtherConst, Views>>>
510 && ...)
512 operator-(iterator<OtherConst> const & x, sentinel const & y)
513 {
514 using return_t =
516 return [&]<size_t... N>(std::integer_sequence<size_t, N...>)
517 {
518 return std::ranges::min({static_cast<return_t>(std::get<N>(x.current_) - std::get<N>(y.end_))...},
519 [](return_t a, return_t b)
520 {
521 return zip::abs(b) < zip::abs(a);
522 });
523 }
524 (std::index_sequence_for<Views...>{});
525 }
526
527 template <bool OtherConst>
528 requires (std::sized_sentinel_for<std::ranges::sentinel_t<view_helper::maybe_const<Const, Views>>,
529 std::ranges::iterator_t<view_helper::maybe_const<OtherConst, Views>>>
530 && ...)
532 operator-(sentinel const & y, iterator<OtherConst> const & x)
533 {
534 return -(x - y);
535 }
536};
537
538struct zip_fn
539{
540 template <typename urng_t>
541 constexpr auto operator()(urng_t && rng) const
542 {
543 return adaptor_from_functor{*this, std::forward<urng_t>(rng)};
544 }
545
546 template <typename... urng_ts>
547 requires (sizeof...(urng_ts) == 0)
548 constexpr auto operator()(urng_ts &&... ranges) const
549 {
550 return std::views::empty<seqan3::common_tuple<>>;
551 }
552
553 template <typename... urng_ts>
554 requires (sizeof...(urng_ts) > 1)
555 constexpr auto operator()(urng_ts &&... ranges) const
556 {
557 return zip_view{std::forward<urng_ts>(ranges)...};
558 }
559};
560
561} // namespace seqan3::detail
563
564namespace seqan3::views
565{
566
572inline constexpr auto zip = seqan3::detail::zip_fn{};
573
574} // namespace seqan3::views
Provides seqan3::detail::adaptor_from_functor.
Provides seqan3::detail::all.
T apply(T... args)
T begin(T... args)
A std::tuple implementation that incorporates most changes from C++23's standard library.
Definition: common_tuple.hpp:31
Provides seqan3::common_tuple.
T declval(T... args)
T end(T... args)
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:146
constexpr auto zip
A view adaptor that takes several views and returns tuple-like values from every i-th element of each...
Definition: zip.hpp:572
constexpr auto elements
A view calling get on each element in a range.
Definition: elements.hpp:80
Provides implementation helper for seqan3::views::zip and seqan3::views::join_with.
T invoke(T... args)
T is_nothrow_move_constructible_v
T iter_swap(T... args)
The internal SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:29
The SeqAn namespace for views.
Definition: char_strictly_to.hpp:22
SeqAn specific customisations in the standard namespace.
T operator>(T... args)
The <ranges> header from C++20's standard library.
A std::pair implementation that incorporates most changes from C++23's standard library.
Definition: common_pair.hpp:28