SeqAn3 3.2.0-rc.1
The Modern C++ library for sequence analysis.
pairwise_combine.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 <cmath>
16#include <seqan3/std/ranges>
17
23
24namespace seqan3::detail
25{
41template <std::ranges::view underlying_range_type>
42 requires std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>
43class pairwise_combine_view : public std::ranges::view_interface<pairwise_combine_view<underlying_range_type>>
44{
45private:
47 template <typename range_type>
48 class basic_iterator;
49
60
61public:
71
88 explicit constexpr pairwise_combine_view(underlying_range_type range) : u_range{std::move(range)}
89 {
90 // Check if range is empty.
91 if (std::ranges::empty(u_range))
92 {
93 back_iterator = std::ranges::end(u_range);
94 }
95 else
96 {
97 if constexpr (std::ranges::bidirectional_range<underlying_range_type>)
98 { // Simply take one before the end. We can do this as we require underlying_range_type to be a common range.
99 back_iterator = std::ranges::prev(std::ranges::end(u_range));
100 }
101 else
102 { // For all other cases we need to set the back_iterator in linear time to the correct position.
103 back_iterator = std::ranges::begin(u_range);
104 if constexpr (std::ranges::sized_range<underlying_range_type>)
105 {
106 std::ranges::advance(back_iterator, std::ranges::size(u_range) - 1);
107 }
108 else // We don't have the size, so we need to increment until one before the end in a linear pass.
109 {
110 auto tmp_it = back_iterator;
111 do
112 {
113 back_iterator = tmp_it;
114 }
115 while (++tmp_it != std::ranges::end(u_range));
116 }
117 }
118 }
119 }
120
140 template <typename other_range_t>
141 requires (!std::same_as<std::remove_cvref_t<other_range_t>, pairwise_combine_view>)
142 && std::ranges::viewable_range<other_range_t>
143 && // Must come after self type check to avoid conflicts with the move constructor.
144 std::constructible_from<underlying_range_type,
145 std::ranges::ref_view<std::remove_reference_t<other_range_t>>>
146 //TODO: Investigate: the following expression is equivalent to the one above but raises a weird assertion in
147 // the ranges adaptor suggesting that the pairwise_combine_view is not a viewable_range.
148 // std::constructible_from<underlying_range_type, decltype(std::views::all(std::declval<other_range_t &&>()))>
149 explicit constexpr pairwise_combine_view(other_range_t && range) :
150 pairwise_combine_view{std::views::all(std::forward<other_range_t>(range))}
151 {}
152
169 constexpr iterator begin() noexcept
170 {
171 return {std::ranges::begin(u_range), std::ranges::begin(u_range), std::ranges::end(u_range)};
172 }
173
175 constexpr const_iterator begin() const noexcept
176 requires const_iterable_range<underlying_range_type>
177 {
178 return {std::ranges::cbegin(u_range), std::ranges::cbegin(u_range), std::ranges::cend(u_range)};
179 }
180
194 constexpr iterator end() noexcept
195 {
196 return {back_iterator, std::ranges::begin(u_range), std::ranges::end(u_range)};
197 }
198
200 constexpr const_iterator end() const noexcept
201 requires const_iterable_range<underlying_range_type>
202 {
203 return {back_iterator, std::ranges::cbegin(u_range), std::ranges::cend(u_range)};
204 }
206
211#if SEQAN3_WORKAROUND_GCC_NON_TEMPLATE_REQUIRES
212 template <typename = underlying_range_type>
213#endif // SEQAN3_WORKAROUND_GCC_NON_TEMPLATE_REQUIRES
214 constexpr auto size() const noexcept
215 requires std::ranges::sized_range<underlying_range_type>
216 {
217 auto const size = std::ranges::size(u_range);
218 return (size * (size - 1)) / 2;
219 }
221
222private:
224 underlying_range_type u_range{};
226 std::ranges::iterator_t<underlying_range_type> back_iterator{};
227};
228
234template <std::ranges::viewable_range other_range_t>
237
251template <std::ranges::view underlying_range_type>
252 requires std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>
253template <typename range_type>
254class pairwise_combine_view<underlying_range_type>::basic_iterator :
255 public maybe_iterator_category<std::ranges::iterator_t<range_type>>
256{
257private:
259 template <typename other_range_type>
261#if !SEQAN3_WORKAROUND_FURTHER_CONSTRAIN_FRIEND_DECLARATION
262 requires std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
263#endif // !SEQAN3_WORKAROUND_FURTHER_CONSTRAIN_FRIEND_DECLARATION
265 friend class basic_iterator;
266
268 using underlying_iterator_type = std::ranges::iterator_t<range_type>;
273
274public:
286 using pointer = void;
290
294 basic_iterator() = default;
295 basic_iterator(basic_iterator const &) = default;
299 ~basic_iterator() = default;
300
315 underlying_iterator_type end_it) noexcept :
316 first_it{iter},
317 second_it{std::ranges::next(iter, 1, end_it)},
318 begin_it{begin_it},
319 end_it{end_it}
320 {}
321
330 template <typename other_range_type>
331 requires std::convertible_to<other_range_type, range_type &>
332 && std::same_as<std::remove_const_t<other_range_type>, std::remove_const_t<range_type>>
334 basic_iterator{std::move(other.first_it), std::move(other.begin_it), std::move(other.end_it)}
335 {}
337
342 constexpr reference operator*() const noexcept(noexcept(*std::declval<underlying_iterator_type>()))
343 {
344 return reference{*first_it, *second_it};
345 }
346
350 constexpr reference operator[](size_t const index) const
351 noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
352 requires std::random_access_iterator<underlying_iterator_type>
353 {
354 return *(*this + index);
355 }
357
362 constexpr basic_iterator &
363 operator++(/*pre-increment*/) noexcept(noexcept(++std::declval<underlying_iterator_type &>()))
364 {
365 if (++second_it == end_it)
366 {
367 ++first_it;
368 second_it = first_it;
369 ++second_it;
370 }
371 return *this;
372 }
373
375 constexpr basic_iterator
376 operator++(int /*post-increment*/) noexcept(noexcept(std::declval<underlying_iterator_type &>()++))
377 {
378 basic_iterator tmp{*this};
379 ++*this;
380 return tmp;
381 }
382
384 constexpr basic_iterator &
385 operator--(/*pre-decrement*/) noexcept(noexcept(--std::declval<underlying_iterator_type &>()))
386 requires std::bidirectional_iterator<underlying_iterator_type>
387 {
388 if (--second_it == first_it)
389 {
390 --first_it;
391 second_it = end_it;
392 --second_it;
393 }
394 return *this;
395 }
396
398 constexpr basic_iterator
399 operator--(int /*post-decrement*/) noexcept(noexcept(std::declval<underlying_iterator_type &>()--))
400 requires std::bidirectional_iterator<underlying_iterator_type>
401 {
402 basic_iterator tmp{*this};
403 --*this;
404 return tmp;
405 }
406
409 constexpr basic_iterator &
410 operator+=(difference_type const offset) noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
411 requires std::random_access_iterator<underlying_iterator_type>
412 {
413 from_index(to_index() + offset);
414 return *this;
415 }
416
419 constexpr basic_iterator operator+(difference_type const offset) const
420 noexcept(noexcept(std::declval<basic_iterator &>() += 1))
421 requires std::random_access_iterator<underlying_iterator_type>
422 {
423 basic_iterator tmp{*this};
424 return (tmp += offset);
425 }
426
429 constexpr friend basic_iterator
431 basic_iterator iter) noexcept(noexcept(std::declval<basic_iterator<range_type> &>().from_index(1)))
432 requires std::random_access_iterator<underlying_iterator_type>
433 {
434 iter.from_index(iter.to_index() + offset);
435 return iter;
436 }
437
440 constexpr basic_iterator &
441 operator-=(difference_type const offset) noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
442 requires std::random_access_iterator<underlying_iterator_type>
443 {
444 from_index(to_index() - offset);
445 return *this;
446 }
447
450 constexpr basic_iterator operator-(difference_type const offset) const
451 noexcept(noexcept(std::declval<basic_iterator &>() -= 1))
452 requires std::random_access_iterator<underlying_iterator_type>
453 {
454 basic_iterator tmp{*this};
455 return (tmp -= offset);
456 }
457
460 template <typename other_range_type>
461 requires std::random_access_iterator<underlying_iterator_type>
462 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
464 noexcept(noexcept(std::declval<basic_iterator &>().to_index()))
465 {
466 return static_cast<difference_type>(to_index() - rhs.to_index());
467 }
469
475 //NOTE: The comparison operators should be implemented as friends, but due to a bug in gcc friend function
476 // cannot yet be constrained. To avoid unexpected errors with the comparison all operators are implemented as
477 // direct members and not as friends.
478
480 template <typename other_range_type>
481 requires std::equality_comparable_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>>
482 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
483 constexpr bool operator==(basic_iterator<other_range_type> const & rhs) const
484 noexcept(noexcept(std::declval<underlying_iterator_type &>() == std::declval<underlying_iterator_type &>()))
485 {
486 return std::tie(first_it, second_it) == std::tie(rhs.first_it, rhs.second_it);
487 }
488
490 template <typename other_range_type>
491 requires std::equality_comparable_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>>
492 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
493 constexpr bool operator!=(basic_iterator<other_range_type> const & rhs) const
494 noexcept(noexcept(std::declval<underlying_iterator_type &>() != std::declval<underlying_iterator_type &>()))
495 {
496 return !(*this == rhs);
497 }
498
500 template <typename other_range_type>
501 requires std::totally_ordered_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>>
502 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
503 constexpr bool operator<(basic_iterator<other_range_type> const & rhs) const
504 noexcept(noexcept(std::declval<underlying_iterator_type &>() < std::declval<underlying_iterator_type &>()))
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 {
517 return std::tie(first_it, second_it) > std::tie(rhs.first_it, rhs.second_it);
518 }
519
521 template <typename other_range_type>
522 requires std::totally_ordered_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>>
523 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
524 constexpr bool operator<=(basic_iterator<other_range_type> const & rhs) const
525 noexcept(noexcept(std::declval<underlying_iterator_type &>() <= std::declval<underlying_iterator_type &>()))
526 {
527 return std::tie(first_it, second_it) <= std::tie(rhs.first_it, rhs.second_it);
528 }
529
531 template <typename other_range_type>
532 requires std::totally_ordered_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>>
533 && std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
534 constexpr bool operator>=(basic_iterator<other_range_type> const & rhs) const
535 noexcept(noexcept(std::declval<underlying_iterator_type &>() >= std::declval<underlying_iterator_type &>()))
536 {
537 return std::tie(first_it, second_it) >= std::tie(rhs.first_it, rhs.second_it);
538 }
540
541private:
554 constexpr size_t to_index() const
555 noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()))
557 {
558 size_t src_size = end_it - begin_it;
559 size_t index_i = first_it - begin_it;
560 size_t index_j = second_it - begin_it;
561 return (src_size * (src_size - 1) / 2) - (src_size - index_i) * ((src_size - index_i) - 1) / 2 + index_j
562 - index_i - 1;
563 }
564
569 constexpr void from_index(size_t const index) noexcept(noexcept(
570 std::declval<underlying_iterator_type &>()
571 - std::declval<underlying_iterator_type &>()) && noexcept(std::declval<underlying_iterator_type &>() + 1))
572 requires std::random_access_iterator<underlying_iterator_type>
573 {
574 size_t src_size = end_it - begin_it;
575 size_t index_i =
576 src_size - 2 - std::floor(std::sqrt(-8 * index + 4 * src_size * (src_size - 1) - 7) / 2.0 - 0.5);
577 size_t index_j =
578 index + index_i + 1 - src_size * (src_size - 1) / 2 + (src_size - index_i) * ((src_size - index_i) - 1) / 2;
579 first_it = begin_it + index_i;
580 second_it = begin_it + index_j;
581 }
582
591};
592
593} // namespace seqan3::detail
594
595namespace seqan3::views
596{
660
661} // namespace seqan3::views
Provides seqan3::detail::adaptor_for_view_without_args.
A std::tuple implementation that incorporates most changes from C++23's standard library.
Definition: common_tuple.hpp:31
Template for range adaptor closure objects that store no arguments and always delegate to the view co...
Definition: adaptor_for_view_without_args.hpp:49
The forward declared iterator type for pairwise_combine_view.
Definition: pairwise_combine.hpp:256
constexpr basic_iterator operator++(int) noexcept(noexcept(std::declval< underlying_iterator_type & >()++))
Post-increment operator.
Definition: pairwise_combine.hpp:376
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:569
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:441
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:410
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:350
constexpr reference operator*() const noexcept(noexcept(*std::declval< underlying_iterator_type >()))
Accesses the pointed-to element.
Definition: pairwise_combine.hpp:342
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:493
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:554
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:463
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:483
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:430
constexpr basic_iterator & operator++() noexcept(noexcept(++std::declval< underlying_iterator_type & >()))
Pre-increment operator.
Definition: pairwise_combine.hpp:363
void pointer
The pointer type.
Definition: pairwise_combine.hpp:286
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:524
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:313
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:419
constexpr basic_iterator(basic_iterator< other_range_type > other) noexcept
Constructs const iterator from non-const iterator.
Definition: pairwise_combine.hpp:333
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:503
std::ranges::iterator_t< range_type > underlying_iterator_type
Alias type for the iterator over the passed range type.
Definition: pairwise_combine.hpp:268
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:450
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:534
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:399
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:385
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:513
Generates all pairwise combinations of the elements in the underlying range.
Definition: pairwise_combine.hpp:44
underlying_range_type u_range
The underling range.
Definition: pairwise_combine.hpp:224
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:149
pairwise_combine_view(pairwise_combine_view &&)=default
Defaulted.
constexpr pairwise_combine_view(underlying_range_type range)
Constructs from a view.
Definition: pairwise_combine.hpp:88
constexpr iterator begin() noexcept
Returns an iterator to the first element of the range.
Definition: pairwise_combine.hpp:169
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:214
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:58
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:226
~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:200
constexpr const_iterator begin() const noexcept
Returns an iterator to the first element of the range.
Definition: pairwise_combine.hpp:175
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:194
A generic random access iterator that delegates most operations to the range.
Definition: random_access_iterator.hpp:298
Provides seqan3::common_tuple.
T floor(T... args)
constexpr auto all
Returns a view that includes all elements of the range argument.
Definition: all_view.hpp:204
@ offset
Sequence (seqan3::field::seq) relative start position (0-based), unsigned value.
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:146
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:51
constexpr auto pairwise_combine
A view adaptor that generates all pairwise combinations of the elements of the underlying range.
Definition: pairwise_combine.hpp:659
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:29
pairwise_combine_view(other_range_t &&range) -> pairwise_combine_view< std::views::all_t< other_range_t > >
Deduces the correct template type from a non-view lvalue range by wrapping the range in std::views::a...
The SeqAn namespace for views.
Definition: char_strictly_to.hpp:22
SeqAn specific customisations in the standard namespace.
The <ranges> header from C++20's standard library.
T sqrt(T... args)
Defines iterator_category member if underlying_iterator_t has a valid std::iterator_traits::iterator_...
Definition: iterator_traits.hpp:42
T tie(T... args)
Provides seqan3::detail::transformation_trait_or.
Additional non-standard concepts for ranges.