SeqAn3 3.4.0-rc.1
The Modern C++ library for sequence analysis.
Loading...
Searching...
No Matches
pairwise_combine.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2006-2024 Knut Reinert & Freie Universität Berlin
2// SPDX-FileCopyrightText: 2016-2024 Knut Reinert & MPI für molekulare Genetik
3// SPDX-License-Identifier: BSD-3-Clause
4
10#pragma once
11
12#include <cmath>
13#include <ranges>
14
20
21namespace seqan3::detail
22{
38template <std::ranges::view underlying_range_type>
39 requires std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>
40class pairwise_combine_view : public std::ranges::view_interface<pairwise_combine_view<underlying_range_type>>
41{
42private:
44 template <typename range_type>
45 class basic_iterator;
46
57
58public:
68
85 explicit constexpr pairwise_combine_view(underlying_range_type range) : u_range{std::move(range)}
86 {
87 // Check if range is empty.
88 if (std::ranges::empty(u_range))
89 {
90 back_iterator = std::ranges::end(u_range);
91 }
92 else
93 {
94 if constexpr (std::ranges::bidirectional_range<underlying_range_type>)
95 { // Simply take one before the end. We can do this as we require underlying_range_type to be a common range.
96 back_iterator = std::ranges::prev(std::ranges::end(u_range));
97 }
98 else
99 { // For all other cases we need to set the back_iterator in linear time to the correct position.
101 if constexpr (std::ranges::sized_range<underlying_range_type>)
102 {
103 std::ranges::advance(back_iterator, std::ranges::size(u_range) - 1);
104 }
105 else // We don't have the size, so we need to increment until one before the end in a linear pass.
106 {
107 auto tmp_it = back_iterator;
108 do
109 {
110 back_iterator = tmp_it;
111 }
112 while (++tmp_it != std::ranges::end(u_range));
113 }
114 }
115 }
116 }
117
137 template <typename other_range_t>
138 requires (!std::same_as<std::remove_cvref_t<other_range_t>, pairwise_combine_view>)
139 && std::ranges::viewable_range<other_range_t>
140 && // Must come after self type check to avoid conflicts with the move constructor.
141 std::constructible_from<underlying_range_type,
142 std::ranges::ref_view<std::remove_reference_t<other_range_t>>>
143 //TODO: Investigate: the following expression is equivalent to the one above but raises a weird assertion in
144 // the ranges adaptor suggesting that the pairwise_combine_view is not a viewable_range.
145 // std::constructible_from<underlying_range_type, decltype(std::views::all(std::declval<other_range_t &&>()))>
146 explicit constexpr pairwise_combine_view(other_range_t && range) :
147 pairwise_combine_view{std::views::all(std::forward<other_range_t>(range))}
148 {}
149
166 constexpr iterator begin() noexcept
167 {
168 return {std::ranges::begin(u_range), std::ranges::begin(u_range), std::ranges::end(u_range)};
169 }
170
172 constexpr const_iterator begin() const noexcept
173 requires const_iterable_range<underlying_range_type>
174 {
175 return {std::ranges::begin(u_range), std::ranges::begin(u_range), std::ranges::end(u_range)};
176 }
177
191 constexpr iterator end() noexcept
192 {
193 return {back_iterator, std::ranges::begin(u_range), std::ranges::end(u_range)};
194 }
195
197 constexpr const_iterator end() const noexcept
198 requires const_iterable_range<underlying_range_type>
199 {
200 return {back_iterator, std::ranges::begin(u_range), std::ranges::end(u_range)};
201 }
203
208 constexpr auto size() const noexcept
209 requires std::ranges::sized_range<underlying_range_type>
210 {
211 auto const size = std::ranges::size(u_range);
212 return (size * (size - 1)) / 2;
213 }
215
216private:
218 underlying_range_type u_range;
220 std::ranges::iterator_t<underlying_range_type> back_iterator{};
221};
222
228template <std::ranges::viewable_range other_range_t>
231
245template <std::ranges::view underlying_range_type>
246 requires std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>
247template <typename range_type>
248class pairwise_combine_view<underlying_range_type>::basic_iterator :
249 public maybe_iterator_category<std::ranges::iterator_t<range_type>>
250{
251private:
253 template <typename other_range_type>
254 friend class basic_iterator;
255
257 using underlying_iterator_type = std::ranges::iterator_t<range_type>;
262
263public:
275 using pointer = void;
279
283 basic_iterator() = default;
284 basic_iterator(basic_iterator const &) = default;
288 ~basic_iterator() = default;
289
304 underlying_iterator_type end_it) noexcept :
305 first_it{iter},
306 second_it{std::ranges::next(iter, 1, end_it)},
307 begin_it{begin_it},
308 end_it{end_it}
309 {}
310
319 template <typename other_range_type>
320 requires std::convertible_to<other_range_type, range_type &>
321 && std::same_as<std::remove_const_t<other_range_type>, std::remove_const_t<range_type>>
323 basic_iterator{std::move(other.first_it), std::move(other.begin_it), std::move(other.end_it)}
324 {}
326
331 constexpr reference operator*() const noexcept(noexcept(*std::declval<underlying_iterator_type>()))
332 {
333 return reference{*first_it, *second_it};
334 }
335
339 constexpr reference operator[](size_t const index) const
340 noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
341 requires std::random_access_iterator<underlying_iterator_type>
342 {
343 return *(*this + index);
344 }
346
351 constexpr basic_iterator &
352 operator++(/*pre-increment*/) noexcept(noexcept(++std::declval<underlying_iterator_type &>()))
353 {
354 if (++second_it == end_it)
355 {
356 ++first_it;
357 second_it = first_it;
358 ++second_it;
359 }
360 return *this;
361 }
362
364 constexpr basic_iterator
365 operator++(int /*post-increment*/) noexcept(noexcept(std::declval<underlying_iterator_type &>()++))
366 {
367 basic_iterator tmp{*this};
368 ++*this;
369 return tmp;
370 }
371
373 constexpr basic_iterator &
374 operator--(/*pre-decrement*/) noexcept(noexcept(--std::declval<underlying_iterator_type &>()))
375 requires std::bidirectional_iterator<underlying_iterator_type>
376 {
377 if (--second_it == first_it)
378 {
379 --first_it;
380 second_it = end_it;
381 --second_it;
382 }
383 return *this;
384 }
385
387 constexpr basic_iterator
388 operator--(int /*post-decrement*/) noexcept(noexcept(std::declval<underlying_iterator_type &>()--))
389 requires std::bidirectional_iterator<underlying_iterator_type>
390 {
391 basic_iterator tmp{*this};
392 --*this;
393 return tmp;
394 }
395
398 constexpr basic_iterator &
399 operator+=(difference_type const offset) noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
400 requires std::random_access_iterator<underlying_iterator_type>
401 {
402 from_index(to_index() + offset);
403 return *this;
404 }
405
409 noexcept(noexcept(std::declval<basic_iterator &>() += 1))
410 requires std::random_access_iterator<underlying_iterator_type>
411 {
412 basic_iterator tmp{*this};
413 return (tmp += offset);
414 }
415
418 constexpr friend basic_iterator
420 basic_iterator iter) noexcept(noexcept(std::declval<basic_iterator<range_type> &>().from_index(1)))
421 requires std::random_access_iterator<underlying_iterator_type>
422 {
423 iter.from_index(iter.to_index() + offset);
424 return iter;
425 }
426
429 constexpr basic_iterator &
430 operator-=(difference_type const offset) noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
431 requires std::random_access_iterator<underlying_iterator_type>
432 {
433 from_index(to_index() - offset);
434 return *this;
435 }
436
440 noexcept(noexcept(std::declval<basic_iterator &>() -= 1))
441 requires std::random_access_iterator<underlying_iterator_type>
442 {
443 basic_iterator tmp{*this};
444 return (tmp -= offset);
445 }
446
449 template <typename other_range_type>
450 requires std::random_access_iterator<underlying_iterator_type>
451 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
453 noexcept(noexcept(std::declval<basic_iterator &>().to_index()))
454 {
455 return static_cast<difference_type>(to_index() - rhs.to_index());
456 }
458
464 //NOTE: The comparison operators should be implemented as friends, but due to a bug in gcc friend function
465 // cannot yet be constrained. To avoid unexpected errors with the comparison all operators are implemented as
466 // direct members and not as friends.
467
469 template <typename other_range_type>
470 requires std::equality_comparable_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>>
471 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
472 constexpr bool operator==(basic_iterator<other_range_type> const & rhs) const
473 noexcept(noexcept(std::declval<underlying_iterator_type &>() == std::declval<underlying_iterator_type &>()))
474 {
475 return std::tie(first_it, second_it) == std::tie(rhs.first_it, rhs.second_it);
476 }
477
479 template <typename other_range_type>
480 requires std::equality_comparable_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>>
481 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
482 constexpr bool operator!=(basic_iterator<other_range_type> const & rhs) const
483 noexcept(noexcept(std::declval<underlying_iterator_type &>() != std::declval<underlying_iterator_type &>()))
484 {
485 return !(*this == rhs);
486 }
487
489 template <typename other_range_type>
490 requires std::totally_ordered_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>>
491 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
492 constexpr bool operator<(basic_iterator<other_range_type> const & rhs) const
493 noexcept(noexcept(std::declval<underlying_iterator_type &>() < std::declval<underlying_iterator_type &>()))
494 {
495 return std::tie(first_it, second_it) < std::tie(rhs.first_it, rhs.second_it);
496 }
497
499 template <typename other_range_type>
500 requires std::totally_ordered_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>>
501 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
502 constexpr bool operator>(basic_iterator<other_range_type> const & rhs) const
503 noexcept(noexcept(std::declval<underlying_iterator_type &>() > std::declval<underlying_iterator_type &>()))
504
505 {
506 return std::tie(first_it, second_it) > std::tie(rhs.first_it, rhs.second_it);
507 }
508
510 template <typename other_range_type>
511 requires std::totally_ordered_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>>
512 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
513 constexpr bool operator<=(basic_iterator<other_range_type> const & rhs) const
514 noexcept(noexcept(std::declval<underlying_iterator_type &>() <= std::declval<underlying_iterator_type &>()))
515 {
516 return std::tie(first_it, second_it) <= std::tie(rhs.first_it, rhs.second_it);
517 }
518
520 template <typename other_range_type>
521 requires std::totally_ordered_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>>
522 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
523 constexpr bool operator>=(basic_iterator<other_range_type> const & rhs) const
524 noexcept(noexcept(std::declval<underlying_iterator_type &>() >= std::declval<underlying_iterator_type &>()))
525 {
526 return std::tie(first_it, second_it) >= std::tie(rhs.first_it, rhs.second_it);
527 }
529
530private:
543 constexpr size_t to_index() const
544 noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()))
546 {
547 size_t src_size = end_it - begin_it;
548 size_t index_i = first_it - begin_it;
549 size_t index_j = second_it - begin_it;
550 return (src_size * (src_size - 1) / 2) - (src_size - index_i) * ((src_size - index_i) - 1) / 2 + index_j
551 - index_i - 1;
552 }
553
558 constexpr void from_index(size_t const index) noexcept(noexcept(
559 std::declval<underlying_iterator_type &>()
560 - std::declval<underlying_iterator_type &>()) && noexcept(std::declval<underlying_iterator_type &>() + 1))
561 requires std::random_access_iterator<underlying_iterator_type>
562 {
563 size_t src_size = end_it - begin_it;
564 size_t index_i =
565 src_size - 2 - std::floor(std::sqrt(-8 * index + 4 * src_size * (src_size - 1) - 7) / 2.0 - 0.5);
566 size_t index_j =
567 index + index_i + 1 - src_size * (src_size - 1) / 2 + (src_size - index_i) * ((src_size - index_i) - 1) / 2;
568 first_it = begin_it + index_i;
569 second_it = begin_it + index_j;
570 }
571
580};
581
582} // namespace seqan3::detail
583
584namespace seqan3::views
585{
649
650} // namespace seqan3::views
Provides seqan3::detail::adaptor_for_view_without_args.
T begin(T... args)
Template for range adaptor closure objects that store no arguments and always delegate to the view co...
Definition adaptor_for_view_without_args.hpp:46
The forward declared iterator type for pairwise_combine_view.
Definition pairwise_combine.hpp:250
constexpr basic_iterator operator++(int) noexcept(noexcept(std::declval< underlying_iterator_type & >()++))
Post-increment operator.
Definition pairwise_combine.hpp:365
constexpr void from_index(size_t const index) noexcept(noexcept(std::declval< underlying_iterator_type & >() - std::declval< underlying_iterator_type & >()) &&noexcept(std::declval< underlying_iterator_type & >()+1))
Sets the iterator to the given index.
Definition pairwise_combine.hpp:558
constexpr basic_iterator & operator-=(difference_type const offset) noexcept(noexcept(std::declval< basic_iterator & >().from_index(1)))
Decrements the iterator by the given offset; underlying_iterator_type must model \ std::random_access...
Definition pairwise_combine.hpp:430
constexpr basic_iterator & operator+=(difference_type const offset) noexcept(noexcept(std::declval< basic_iterator & >().from_index(1)))
Advances the iterator by the given offset; underlying_iterator_type must model \ std::random_access_i...
Definition pairwise_combine.hpp:399
constexpr reference operator[](size_t const index) const noexcept(noexcept(std::declval< basic_iterator & >().from_index(1)))
Access the element at the given index.
Definition pairwise_combine.hpp:339
constexpr reference operator*() const noexcept(noexcept(*std::declval< underlying_iterator_type >()))
Accesses the pointed-to element.
Definition pairwise_combine.hpp:331
constexpr bool operator!=(basic_iterator< other_range_type > const &rhs) const noexcept(noexcept(std::declval< underlying_iterator_type & >() !=std::declval< underlying_iterator_type & >()))
Checks whether *this is not equal to rhs.
Definition pairwise_combine.hpp:482
basic_iterator(basic_iterator const &)=default
Defaulted.
constexpr size_t to_index() const noexcept(noexcept(std::declval< underlying_iterator_type & >() - std::declval< underlying_iterator_type & >()))
Returns the index for the current iterator position.
Definition pairwise_combine.hpp:543
constexpr difference_type operator-(basic_iterator< other_range_type > const &rhs) const noexcept(noexcept(std::declval< basic_iterator & >().to_index()))
Computes the distance between two iterators; underlying_iterator_type must model \ std::random_access...
Definition pairwise_combine.hpp:452
basic_iterator & operator=(basic_iterator const &)=default
Defaulted.
constexpr bool operator==(basic_iterator< other_range_type > const &rhs) const noexcept(noexcept(std::declval< underlying_iterator_type & >()==std::declval< underlying_iterator_type & >()))
Checks whether *this is equal to rhs.
Definition pairwise_combine.hpp:472
constexpr friend basic_iterator operator+(difference_type const offset, basic_iterator iter) noexcept(noexcept(std::declval< basic_iterator< range_type > & >().from_index(1)))
Advances the iterator by the given offset; underlying_iterator_type must model \ std::random_access_i...
Definition pairwise_combine.hpp:419
constexpr basic_iterator & operator++() noexcept(noexcept(++std::declval< underlying_iterator_type & >()))
Pre-increment operator.
Definition pairwise_combine.hpp:352
common_tuple< underlying_ref_t, underlying_ref_t > reference
The reference type.
Definition pairwise_combine.hpp:273
void pointer
The pointer type.
Definition pairwise_combine.hpp:275
constexpr bool operator<=(basic_iterator< other_range_type > const &rhs) const noexcept(noexcept(std::declval< underlying_iterator_type & >()<=std::declval< underlying_iterator_type & >()))
Checks whether *this is less than or equal to rhs.
Definition pairwise_combine.hpp:513
constexpr basic_iterator(underlying_iterator_type iter, underlying_iterator_type begin_it, underlying_iterator_type end_it) noexcept
Constructs the iterator from the current underlying iterator and the end iterator of the underlying r...
Definition pairwise_combine.hpp:302
constexpr basic_iterator operator+(difference_type const offset) const noexcept(noexcept(std::declval< basic_iterator & >()+=1))
Advances the iterator by the given offset; underlying_iterator_type must model \ std::random_access_i...
Definition pairwise_combine.hpp:408
constexpr basic_iterator(basic_iterator< other_range_type > other) noexcept
Constructs const iterator from non-const iterator.
Definition pairwise_combine.hpp:322
constexpr bool operator<(basic_iterator< other_range_type > const &rhs) const noexcept(noexcept(std::declval< underlying_iterator_type & >()< std::declval< underlying_iterator_type & >()))
Checks whether *this is less than rhs.
Definition pairwise_combine.hpp:492
std::ranges::iterator_t< range_type > underlying_iterator_type
Alias type for the iterator over the passed range type.
Definition pairwise_combine.hpp:257
constexpr basic_iterator operator-(difference_type const offset) const noexcept(noexcept(std::declval< basic_iterator & >() -=1))
Decrements the iterator by the given offset; underlying_iterator_type must model \ std::random_access...
Definition pairwise_combine.hpp:439
constexpr bool operator>=(basic_iterator< other_range_type > const &rhs) const noexcept(noexcept(std::declval< underlying_iterator_type & >() >=std::declval< underlying_iterator_type & >()))
Checks whether *this is greater than or equal to rhs.
Definition pairwise_combine.hpp:523
basic_iterator & operator=(basic_iterator &&)=default
Defaulted.
constexpr basic_iterator operator--(int) noexcept(noexcept(std::declval< underlying_iterator_type & >() --))
Post-decrement operator; underlying_iterator_type must model std::bidirectional_iterator.
Definition pairwise_combine.hpp:388
basic_iterator(basic_iterator &&)=default
Defaulted.
constexpr basic_iterator & operator--() noexcept(noexcept(--std::declval< underlying_iterator_type & >()))
Pre-decrement operator; underlying_iterator_type must model std::bidirectional_iterator.
Definition pairwise_combine.hpp:374
constexpr bool operator>(basic_iterator< other_range_type > const &rhs) const noexcept(noexcept(std::declval< underlying_iterator_type & >() > std::declval< underlying_iterator_type & >()))
Checks whether *this is greater than rhs.
Definition pairwise_combine.hpp:502
Generates all pairwise combinations of the elements in the underlying range.
Definition pairwise_combine.hpp:41
underlying_range_type u_range
The underling range.
Definition pairwise_combine.hpp:218
pairwise_combine_view(pairwise_combine_view const &)=default
Defaulted.
constexpr pairwise_combine_view(other_range_t &&range)
Constructs from a view.
Definition pairwise_combine.hpp:146
pairwise_combine_view(pairwise_combine_view &&)=default
Defaulted.
constexpr pairwise_combine_view(underlying_range_type range)
Constructs from a view.
Definition pairwise_combine.hpp:85
constexpr iterator begin() noexcept
Returns an iterator to the first element of the range.
Definition pairwise_combine.hpp:166
pairwise_combine_view()=default
Defaulted.
constexpr auto size() const noexcept
Computes the size based on the size of the underlying range.
Definition pairwise_combine.hpp:208
transformation_trait_or_t< std::type_identity< basic_iterator< underlying_range_type const > >, void > const_iterator
The const iterator type. Evaluates to void if the underlying range is not const iterable.
Definition pairwise_combine.hpp:55
pairwise_combine_view & operator=(pairwise_combine_view &&)=default
Defaulted.
std::ranges::iterator_t< underlying_range_type > back_iterator
The cached iterator pointing to the last element of the underlying range.
Definition pairwise_combine.hpp:220
~pairwise_combine_view()=default
Defaulted.
constexpr const_iterator end() const noexcept
Returns an iterator to the element following the last element of the range.
Definition pairwise_combine.hpp:197
constexpr const_iterator begin() const noexcept
Returns an iterator to the first element of the range.
Definition pairwise_combine.hpp:172
pairwise_combine_view & operator=(pairwise_combine_view const &)=default
Defaulted.
constexpr iterator end() noexcept
Returns an iterator to the element following the last element of the range.
Definition pairwise_combine.hpp:191
A generic random access iterator that delegates most operations to the range.
Definition random_access_iterator.hpp:288
Provides seqan3::common_tuple.
T floor(T... args)
@ offset
Sequence (seqan3::field::seq) relative start position (0-based), unsigned value.
seqan::stl::tuple< t... > common_tuple
A std::tuple implementation that incorporates most changes from C++23's standard library.
Definition common_tuple.hpp:24
typename transformation_trait_or< type_t, default_t >::type transformation_trait_or_t
Helper type of seqan3::detail::transformation_trait_or (transformation_trait shortcut).
Definition transformation_trait_or.hpp:48
constexpr auto pairwise_combine
A view adaptor that generates all pairwise combinations of the elements of the underlying range.
Definition pairwise_combine.hpp:648
Specifies requirements of an input range type for which the const version of that type satisfies the ...
Provides various transformation traits for use on iterators.
The internal SeqAn3 namespace.
Definition aligned_sequence_concept.hpp:26
The SeqAn namespace for views.
Definition char_strictly_to.hpp:19
SeqAn specific customisations in the standard namespace.
T sqrt(T... args)
Defines iterator_category member if underlying_iterator_t has a valid std::iterator_traits::iterator_...
Definition iterator_traits.hpp:39
T tie(T... args)
Provides seqan3::detail::transformation_trait_or.
Additional non-standard concepts for ranges.
Hide me