SeqAn3  3.1.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 
24 namespace seqan3::detail
25 {
41 template <std::ranges::view underlying_range_type>
43  requires std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>
45 class pairwise_combine_view : public std::ranges::view_interface<pairwise_combine_view<underlying_range_type>>
46 {
47 private:
48 
50  template <typename range_type>
51  class basic_iterator;
52 
61  void>;
63 
64 public:
68  pairwise_combine_view() = default;
73  ~pairwise_combine_view() = default;
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 
231 private:
232 
234  underlying_range_type u_range{};
236  std::ranges::iterator_t<underlying_range_type> back_iterator{};
237 };
238 
244 template <std::ranges::viewable_range other_range_t>
245 pairwise_combine_view(other_range_t && range) ->
248 
262 template <std::ranges::view underlying_range_type>
264  requires std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>
266 template <typename range_type>
267 class pairwise_combine_view<underlying_range_type>::basic_iterator
268  : public maybe_iterator_category<std::ranges::iterator_t<range_type>>
269 {
270 private:
271 
273  template <typename other_range_type>
274  requires std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
275  friend class basic_iterator;
276 
278  using underlying_iterator_type = std::ranges::iterator_t<range_type>;
283 
284 public:
296  using pointer = void;
300 
304  basic_iterator() = default;
305  basic_iterator(basic_iterator const &) = default;
307  basic_iterator & operator=(basic_iterator const &) = default;
309  ~basic_iterator() = default;
310 
324  underlying_iterator_type begin_it,
325  underlying_iterator_type end_it) noexcept :
326  first_it{iter},
327  second_it{std::ranges::next(iter, 1, end_it)},
328  begin_it{begin_it},
329  end_it{end_it}
330  {}
331 
340  template <typename other_range_type>
342  requires std::convertible_to<other_range_type, range_type &> &&
343  std::same_as<std::remove_const_t<other_range_type>, std::remove_const_t<range_type>>
346  basic_iterator{std::move(other.first_it), std::move(other.begin_it), std::move(other.end_it)}
347  {}
349 
354  constexpr reference operator*() const
355  noexcept(noexcept(*std::declval<underlying_iterator_type>()))
356  {
357  return reference{*first_it, *second_it};
358  }
359 
363  constexpr reference operator[](size_t const index) const
364  noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
368  {
369  return *(*this + index);
370  }
372 
377  constexpr basic_iterator & operator++(/*pre-increment*/)
378  noexcept(noexcept(++std::declval<underlying_iterator_type &>()))
379  {
380  if (++second_it == end_it)
381  {
382  ++first_it;
383  second_it = first_it;
384  ++second_it;
385  }
386  return *this;
387  }
388 
390  constexpr basic_iterator operator++(int /*post-increment*/)
391  noexcept(noexcept(std::declval<underlying_iterator_type &>()++))
392  {
393  basic_iterator tmp{*this};
394  ++*this;
395  return tmp;
396  }
397 
399  constexpr basic_iterator & operator--(/*pre-decrement*/)
400  noexcept(noexcept(--std::declval<underlying_iterator_type &>()))
402  requires std::bidirectional_iterator<underlying_iterator_type>
404  {
405  if (--second_it == first_it)
406  {
407  --first_it;
408  second_it = end_it;
409  --second_it;
410  }
411  return *this;
412  }
413 
415  constexpr basic_iterator operator--(int /*post-decrement*/)
416  noexcept(noexcept(std::declval<underlying_iterator_type &>()--))
418  requires std::bidirectional_iterator<underlying_iterator_type>
420  {
421  basic_iterator tmp{*this};
422  --*this;
423  return tmp;
424  }
425 
428  constexpr basic_iterator & operator+=(difference_type const offset)
429  noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
433  {
434  from_index(to_index() + offset);
435  return *this;
436  }
437 
440  constexpr basic_iterator operator+(difference_type const offset) const
441  noexcept(noexcept(std::declval<basic_iterator &>() += 1))
445  {
446  basic_iterator tmp{*this};
447  return (tmp += offset);
448  }
449 
452  constexpr friend basic_iterator operator+(difference_type const offset, basic_iterator iter)
453  noexcept(noexcept(std::declval<basic_iterator<range_type> &>().from_index(1)))
457  {
458  iter.from_index(iter.to_index() + offset);
459  return iter;
460  }
461 
464  constexpr basic_iterator & operator-=(difference_type const offset)
465  noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
469  {
470  from_index(to_index() - offset);
471  return *this;
472  }
473 
476  constexpr basic_iterator operator-(difference_type const offset) const
477  noexcept(noexcept(std::declval<basic_iterator &>() -= 1))
481  {
482  basic_iterator tmp{*this};
483  return (tmp -= offset);
484  }
485 
488  template <typename other_range_type>
490  requires std::random_access_iterator<underlying_iterator_type> &&
491  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
494  noexcept(noexcept(std::declval<basic_iterator &>().to_index()))
495  {
496  return static_cast<difference_type>(to_index() - rhs.to_index());
497  }
499 
505  //NOTE: The comparison operators should be implemented as friends, but due to a bug in gcc friend function
506  // cannot yet be constrained. To avoid unexpected errors with the comparison all operators are implemented as
507  // direct members and not as friends.
508 
510  template <typename other_range_type>
512  requires std::equality_comparable_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>> &&
513  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
515  constexpr bool operator==(basic_iterator<other_range_type> const & rhs) const
516  noexcept(noexcept(std::declval<underlying_iterator_type &>() == std::declval<underlying_iterator_type &>()))
517  {
518  return std::tie(first_it, second_it) == std::tie(rhs.first_it, rhs.second_it);
519  }
520 
522  template <typename other_range_type>
524  requires std::equality_comparable_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>> &&
525  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
527  constexpr bool operator!=(basic_iterator<other_range_type> const & rhs) const
528  noexcept(noexcept(std::declval<underlying_iterator_type &>() != std::declval<underlying_iterator_type &>()))
529  {
530  return !(*this == rhs);
531  }
532 
534  template <typename other_range_type>
536  requires std::totally_ordered_with<underlying_iterator_type,
537  std::ranges::iterator_t<other_range_type>> &&
538  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
540  constexpr bool operator<(basic_iterator<other_range_type> const & rhs) const
541  noexcept(noexcept(std::declval<underlying_iterator_type &>() < std::declval<underlying_iterator_type &>()))
542  {
543  return std::tie(first_it, second_it) < std::tie(rhs.first_it, rhs.second_it);
544  }
545 
547  template <typename other_range_type>
549  requires std::totally_ordered_with<underlying_iterator_type,
550  std::ranges::iterator_t<other_range_type>> &&
551  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
553  constexpr bool operator>(basic_iterator<other_range_type> const & rhs) const
554  noexcept(noexcept(std::declval<underlying_iterator_type &>() > std::declval<underlying_iterator_type &>()))
555 
556  {
557  return std::tie(first_it, second_it) > std::tie(rhs.first_it, rhs.second_it);
558  }
559 
561  template <typename other_range_type>
563  requires std::totally_ordered_with<underlying_iterator_type,
564  std::ranges::iterator_t<other_range_type>> &&
565  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
567  constexpr bool operator<=(basic_iterator<other_range_type> const & rhs) const
568  noexcept(noexcept(std::declval<underlying_iterator_type &>() <= std::declval<underlying_iterator_type &>()))
569  {
570  return std::tie(first_it, second_it) <= std::tie(rhs.first_it, rhs.second_it);
571  }
572 
574  template <typename other_range_type>
576  requires std::totally_ordered_with<underlying_iterator_type,
577  std::ranges::iterator_t<other_range_type>> &&
578  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
580  constexpr bool operator>=(basic_iterator<other_range_type> const & rhs) const
581  noexcept(noexcept(std::declval<underlying_iterator_type &>() >= std::declval<underlying_iterator_type &>()))
582  {
583  return std::tie(first_it, second_it) >= std::tie(rhs.first_it, rhs.second_it);
584  }
586 
587 private:
588 
601  constexpr size_t to_index() const
602  noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()))
606  {
607  size_t src_size = end_it - begin_it;
608  size_t index_i = first_it - begin_it;
609  size_t index_j = second_it - begin_it;
610  return (src_size * (src_size - 1)/2) - (src_size - index_i) * ((src_size - index_i) - 1)/2 +
611  index_j - index_i - 1;
612  }
613 
618  constexpr void from_index(size_t const index)
619  noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()) &&
620  noexcept(std::declval<underlying_iterator_type &>() + 1))
624  {
625  size_t src_size = end_it - begin_it;
626  size_t index_i = src_size - 2 -
627  std::floor(std::sqrt(-8 * index + 4 * src_size * (src_size - 1) - 7)/2.0 - 0.5);
628  size_t index_j = index + index_i + 1 - src_size * (src_size - 1)/2 + (src_size - index_i) *
629  ((src_size - index_i) - 1)/2;
630  first_it = begin_it + index_i;
631  second_it = begin_it + index_j;
632  }
633 
642 };
643 
644 } // namespace seqan3::detail
645 
646 namespace seqan3::views
647 {
715 
717 } // 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:390
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:464
constexpr reference operator*() const noexcept(noexcept(*std::declval< underlying_iterator_type >()))
Accesses the pointed-to element.
Definition: pairwise_combine.hpp:354
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:452
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:345
basic_iterator & operator=(basic_iterator const &)=default
Defaulted.
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:415
constexpr basic_iterator & operator++() noexcept(noexcept(++std::declval< underlying_iterator_type & >()))
Pre-increment operator.
Definition: pairwise_combine.hpp:377
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:553
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:580
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:618
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:493
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:527
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:515
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:601
common_tuple< underlying_ref_t, underlying_ref_t > reference
The reference type.
Definition: pairwise_combine.hpp:294
void pointer
The pointer type.
Definition: pairwise_combine.hpp:296
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:567
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:323
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:476
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:399
std::ranges::iterator_t< range_type > underlying_iterator_type
Alias type for the iterator over the passed range type.
Definition: pairwise_combine.hpp:278
basic_iterator(basic_iterator &&)=default
Defaulted.
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:428
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:363
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:540
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:440
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.
pairwise_combine_view & operator=(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
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
constexpr iterator end() noexcept
Returns an iterator to the element following the last element of the range.
Definition: pairwise_combine.hpp:199
pairwise_combine_view & operator=(pairwise_combine_view const &)=default
Defaulted.
A generic random access iterator that delegates most operations to the range.
Definition: random_access_iterator.hpp:310
Provides seqan3::common_tuple and seqan3::common_pair.
T declval(T... args)
T floor(T... args)
@ 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: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
::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:29
SeqAn specific customisations in the standard namespace.
Adaptations of concepts from the Ranges TS.
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.