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>
43 requires std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>
45class pairwise_combine_view : public std::ranges::view_interface<pairwise_combine_view<underlying_range_type>>
46{
47private:
48
50 template <typename range_type>
51 class basic_iterator;
52
61 void>;
63
64public:
74
91 explicit constexpr pairwise_combine_view(underlying_range_type range) : u_range{std::move(range)}
92 {
93 // Check if range is empty.
94 if (std::ranges::empty(u_range))
95 {
96 back_iterator = std::ranges::end(u_range);
97 }
98 else
99 {
100 if constexpr (std::ranges::bidirectional_range<underlying_range_type>)
101 { // Simply take one before the end. We can do this as we require underlying_range_type to be a common range.
102 back_iterator = std::ranges::prev(std::ranges::end(u_range));
103 }
104 else
105 { // For all other cases we need to set the back_iterator in linear time to the correct position.
106 back_iterator = std::ranges::begin(u_range);
107 if constexpr (std::ranges::sized_range<underlying_range_type>)
108 {
109 std::ranges::advance(back_iterator, std::ranges::size(u_range) - 1);
110 }
111 else // We don't have the size, so we need to increment until one before the end in a linear pass.
112 {
113 auto tmp_it = back_iterator;
114 do
115 {
116 back_iterator = tmp_it;
117 } while (++tmp_it != std::ranges::end(u_range));
118 }
119 }
120 }
121 }
122
142 template <typename other_range_t>
144 requires (!std::same_as<std::remove_cvref_t<other_range_t>, pairwise_combine_view>) &&
145 std::ranges::viewable_range<other_range_t> && // Must come after self type check to avoid conflicts with the move constructor.
146 std::constructible_from<underlying_range_type,
147 std::ranges::ref_view<std::remove_reference_t<other_range_t>>>
148 //TODO: Investigate: the following expression is equivalent to the one above but raises a weird assertion in
149 // the ranges adaptor suggesting that the pairwise_combine_view is not a viewable_range.
150 // std::constructible_from<underlying_range_type, decltype(std::views::all(std::declval<other_range_t &&>()))>
152 explicit constexpr pairwise_combine_view(other_range_t && range) :
153 pairwise_combine_view{std::views::all(std::forward<other_range_t>(range))}
154 {}
155
172 constexpr iterator begin() noexcept
173 {
174 return {std::ranges::begin(u_range), std::ranges::begin(u_range), std::ranges::end(u_range)};
175 }
176
178 constexpr const_iterator begin() const noexcept
180 requires const_iterable_range<underlying_range_type>
182 {
183 return {std::ranges::cbegin(u_range), std::ranges::cbegin(u_range), std::ranges::cend(u_range)};
184 }
185
199 constexpr iterator end() noexcept
200 {
201 return {back_iterator, std::ranges::begin(u_range), std::ranges::end(u_range)};
202 }
203
205 constexpr const_iterator end() const noexcept
207 requires const_iterable_range<underlying_range_type>
209 {
210 return {back_iterator, std::ranges::cbegin(u_range), std::ranges::cend(u_range)};
211 }
213
218#if SEQAN3_WORKAROUND_GCC_NON_TEMPLATE_REQUIRES
219 template <typename = underlying_range_type>
220#endif // SEQAN3_WORKAROUND_GCC_NON_TEMPLATE_REQUIRES
221 constexpr auto size() const noexcept
223 requires std::ranges::sized_range<underlying_range_type>
225 {
226 auto const size = std::ranges::size(u_range);
227 return (size * (size - 1)) / 2;
228 }
230
231private:
232
234 underlying_range_type u_range{};
236 std::ranges::iterator_t<underlying_range_type> back_iterator{};
237};
238
244template <std::ranges::viewable_range other_range_t>
245pairwise_combine_view(other_range_t && range) ->
248
262template <std::ranges::view underlying_range_type>
264 requires std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>
266template <typename range_type>
267class pairwise_combine_view<underlying_range_type>::basic_iterator
268 : public maybe_iterator_category<std::ranges::iterator_t<range_type>>
269{
270private:
271
273 template <typename other_range_type>
275#if !SEQAN3_WORKAROUND_FURTHER_CONSTRAIN_FRIEND_DECLARATION
276 requires std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
277#endif // !SEQAN3_WORKAROUND_FURTHER_CONSTRAIN_FRIEND_DECLARATION
279 friend class basic_iterator;
280
282 using underlying_iterator_type = std::ranges::iterator_t<range_type>;
287
288public:
300 using pointer = void;
304
308 basic_iterator() = default;
309 basic_iterator(basic_iterator const &) = default;
313 ~basic_iterator() = default;
314
329 underlying_iterator_type end_it) noexcept :
330 first_it{iter},
331 second_it{std::ranges::next(iter, 1, end_it)},
332 begin_it{begin_it},
333 end_it{end_it}
334 {}
335
344 template <typename other_range_type>
346 requires std::convertible_to<other_range_type, range_type &> &&
347 std::same_as<std::remove_const_t<other_range_type>, std::remove_const_t<range_type>>
350 basic_iterator{std::move(other.first_it), std::move(other.begin_it), std::move(other.end_it)}
351 {}
353
358 constexpr reference operator*() const
359 noexcept(noexcept(*std::declval<underlying_iterator_type>()))
360 {
361 return reference{*first_it, *second_it};
362 }
363
367 constexpr reference operator[](size_t const index) const
368 noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
370 requires std::random_access_iterator<underlying_iterator_type>
372 {
373 return *(*this + index);
374 }
376
381 constexpr basic_iterator & operator++(/*pre-increment*/)
382 noexcept(noexcept(++std::declval<underlying_iterator_type &>()))
383 {
384 if (++second_it == end_it)
385 {
386 ++first_it;
387 second_it = first_it;
388 ++second_it;
389 }
390 return *this;
391 }
392
394 constexpr basic_iterator operator++(int /*post-increment*/)
395 noexcept(noexcept(std::declval<underlying_iterator_type &>()++))
396 {
397 basic_iterator tmp{*this};
398 ++*this;
399 return tmp;
400 }
401
403 constexpr basic_iterator & operator--(/*pre-decrement*/)
404 noexcept(noexcept(--std::declval<underlying_iterator_type &>()))
406 requires std::bidirectional_iterator<underlying_iterator_type>
408 {
409 if (--second_it == first_it)
410 {
411 --first_it;
412 second_it = end_it;
413 --second_it;
414 }
415 return *this;
416 }
417
419 constexpr basic_iterator operator--(int /*post-decrement*/)
420 noexcept(noexcept(std::declval<underlying_iterator_type &>()--))
422 requires std::bidirectional_iterator<underlying_iterator_type>
424 {
425 basic_iterator tmp{*this};
426 --*this;
427 return tmp;
428 }
429
432 constexpr basic_iterator & operator+=(difference_type const offset)
433 noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
435 requires std::random_access_iterator<underlying_iterator_type>
437 {
438 from_index(to_index() + offset);
439 return *this;
440 }
441
444 constexpr basic_iterator operator+(difference_type const offset) const
445 noexcept(noexcept(std::declval<basic_iterator &>() += 1))
447 requires std::random_access_iterator<underlying_iterator_type>
449 {
450 basic_iterator tmp{*this};
451 return (tmp += offset);
452 }
453
456 constexpr friend basic_iterator operator+(difference_type const offset, basic_iterator iter)
457 noexcept(noexcept(std::declval<basic_iterator<range_type> &>().from_index(1)))
459 requires std::random_access_iterator<underlying_iterator_type>
461 {
462 iter.from_index(iter.to_index() + offset);
463 return iter;
464 }
465
468 constexpr basic_iterator & operator-=(difference_type const offset)
469 noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
471 requires std::random_access_iterator<underlying_iterator_type>
473 {
474 from_index(to_index() - offset);
475 return *this;
476 }
477
480 constexpr basic_iterator operator-(difference_type const offset) const
481 noexcept(noexcept(std::declval<basic_iterator &>() -= 1))
483 requires std::random_access_iterator<underlying_iterator_type>
485 {
486 basic_iterator tmp{*this};
487 return (tmp -= offset);
488 }
489
492 template <typename other_range_type>
494 requires std::random_access_iterator<underlying_iterator_type> &&
495 std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
498 noexcept(noexcept(std::declval<basic_iterator &>().to_index()))
499 {
500 return static_cast<difference_type>(to_index() - rhs.to_index());
501 }
503
509 //NOTE: The comparison operators should be implemented as friends, but due to a bug in gcc friend function
510 // cannot yet be constrained. To avoid unexpected errors with the comparison all operators are implemented as
511 // direct members and not as friends.
512
514 template <typename other_range_type>
516 requires std::equality_comparable_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>> &&
517 std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
519 constexpr bool operator==(basic_iterator<other_range_type> const & rhs) const
520 noexcept(noexcept(std::declval<underlying_iterator_type &>() == std::declval<underlying_iterator_type &>()))
521 {
522 return std::tie(first_it, second_it) == std::tie(rhs.first_it, rhs.second_it);
523 }
524
526 template <typename other_range_type>
528 requires std::equality_comparable_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>> &&
529 std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
531 constexpr bool operator!=(basic_iterator<other_range_type> const & rhs) const
532 noexcept(noexcept(std::declval<underlying_iterator_type &>() != std::declval<underlying_iterator_type &>()))
533 {
534 return !(*this == rhs);
535 }
536
538 template <typename other_range_type>
540 requires std::totally_ordered_with<underlying_iterator_type,
541 std::ranges::iterator_t<other_range_type>> &&
542 std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
544 constexpr bool operator<(basic_iterator<other_range_type> const & rhs) const
545 noexcept(noexcept(std::declval<underlying_iterator_type &>() < std::declval<underlying_iterator_type &>()))
546 {
547 return std::tie(first_it, second_it) < std::tie(rhs.first_it, rhs.second_it);
548 }
549
551 template <typename other_range_type>
553 requires std::totally_ordered_with<underlying_iterator_type,
554 std::ranges::iterator_t<other_range_type>> &&
555 std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
557 constexpr bool operator>(basic_iterator<other_range_type> const & rhs) const
558 noexcept(noexcept(std::declval<underlying_iterator_type &>() > std::declval<underlying_iterator_type &>()))
559
560 {
561 return std::tie(first_it, second_it) > std::tie(rhs.first_it, rhs.second_it);
562 }
563
565 template <typename other_range_type>
567 requires std::totally_ordered_with<underlying_iterator_type,
568 std::ranges::iterator_t<other_range_type>> &&
569 std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
571 constexpr bool operator<=(basic_iterator<other_range_type> const & rhs) const
572 noexcept(noexcept(std::declval<underlying_iterator_type &>() <= std::declval<underlying_iterator_type &>()))
573 {
574 return std::tie(first_it, second_it) <= std::tie(rhs.first_it, rhs.second_it);
575 }
576
578 template <typename other_range_type>
580 requires std::totally_ordered_with<underlying_iterator_type,
581 std::ranges::iterator_t<other_range_type>> &&
582 std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
584 constexpr bool operator>=(basic_iterator<other_range_type> const & rhs) const
585 noexcept(noexcept(std::declval<underlying_iterator_type &>() >= std::declval<underlying_iterator_type &>()))
586 {
587 return std::tie(first_it, second_it) >= std::tie(rhs.first_it, rhs.second_it);
588 }
590
591private:
592
605 constexpr size_t to_index() const
606 noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()))
610 {
611 size_t src_size = end_it - begin_it;
612 size_t index_i = first_it - begin_it;
613 size_t index_j = second_it - begin_it;
614 return (src_size * (src_size - 1)/2) - (src_size - index_i) * ((src_size - index_i) - 1)/2 +
615 index_j - index_i - 1;
616 }
617
622 constexpr void from_index(size_t const index)
623 noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()) &&
624 noexcept(std::declval<underlying_iterator_type &>() + 1))
626 requires std::random_access_iterator<underlying_iterator_type>
628 {
629 size_t src_size = end_it - begin_it;
630 size_t index_i = src_size - 2 -
631 std::floor(std::sqrt(-8 * index + 4 * src_size * (src_size - 1) - 7)/2.0 - 0.5);
632 size_t index_j = index + index_i + 1 - src_size * (src_size - 1)/2 + (src_size - index_i) *
633 ((src_size - index_i) - 1)/2;
634 first_it = begin_it + index_i;
635 second_it = begin_it + index_j;
636 }
637
646};
647
648} // namespace seqan3::detail
649
650namespace seqan3::views
651{
715
716} // namespace seqan3::views
Provides seqan3::detail::adaptor_for_view_without_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:49
The forward declared iterator type for pairwise_combine_view.
Definition: pairwise_combine.hpp:269
constexpr basic_iterator operator++(int) noexcept(noexcept(std::declval< underlying_iterator_type & >()++))
Post-increment operator.
Definition: pairwise_combine.hpp:394
constexpr reference operator*() const noexcept(noexcept(*std::declval< underlying_iterator_type >()))
Accesses the pointed-to element.
Definition: pairwise_combine.hpp:358
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:456
basic_iterator(basic_iterator const &)=default
Defaulted.
constexpr basic_iterator(basic_iterator< other_range_type > other) noexcept
Constructs const iterator from non-const iterator.
Definition: pairwise_combine.hpp:349
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:419
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:403
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:557
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:584
basic_iterator & operator=(basic_iterator const &)=default
Defaulted.
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:622
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:497
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:531
constexpr basic_iterator & operator++() noexcept(noexcept(++std::declval< underlying_iterator_type & >()))
Pre-increment operator.
Definition: pairwise_combine.hpp:381
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:519
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:605
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:432
common_tuple< underlying_ref_t, underlying_ref_t > reference
The reference type.
Definition: pairwise_combine.hpp:298
void pointer
The pointer type.
Definition: pairwise_combine.hpp:300
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:571
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:468
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:327
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:480
std::ranges::iterator_t< range_type > underlying_iterator_type
Alias type for the iterator over the passed range type.
Definition: pairwise_combine.hpp:282
basic_iterator & operator=(basic_iterator &&)=default
Defaulted.
basic_iterator(basic_iterator &&)=default
Defaulted.
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:367
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:544
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:444
Generates all pairwise combinations of the elements in the underlying range.
Definition: pairwise_combine.hpp:46
underlying_range_type u_range
The underling range.
Definition: pairwise_combine.hpp:234
pairwise_combine_view(pairwise_combine_view const &)=default
Defaulted.
constexpr const_iterator begin() const noexcept
Returns an iterator to the first element of the range.
Definition: pairwise_combine.hpp:178
pairwise_combine_view(pairwise_combine_view &&)=default
Defaulted.
constexpr pairwise_combine_view(underlying_range_type range)
Constructs from a view.
Definition: pairwise_combine.hpp:91
constexpr iterator begin() noexcept
Returns an iterator to the first element of the range.
Definition: pairwise_combine.hpp:172
constexpr pairwise_combine_view(other_range_t &&range)
Constructs from a view.
Definition: pairwise_combine.hpp:152
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:205
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:61
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:236
~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:221
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:199
A generic random access iterator that delegates most operations to the range.
Definition: random_access_iterator.hpp:312
Provides seqan3::common_tuple and seqan3::common_pair.
T floor(T... args)
@ offset
Sequence (seqan3::field::seq) relative start position (0-based), unsigned value.
::ranges::common_tuple common_tuple
A common tuple type that behaves like a regular std::tuple, but can be used as a reference type proxy...
Definition: common_tuple.hpp:30
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:151
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:714
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_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.