SeqAn3  3.0.3
The Modern C++ library for sequence analysis.
pairwise_combine.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2020, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2020, 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 
19 #include <seqan3/range/concept.hpp>
22 
23 namespace seqan3::detail
24 {
40 template <std::ranges::view underlying_range_type>
42  requires std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>
44 class pairwise_combine_view : public std::ranges::view_interface<pairwise_combine_view<underlying_range_type>>
45 {
46 private:
47 
49  template <typename range_type>
50  class basic_iterator;
51 
60  void>;
62 
63 public:
67  pairwise_combine_view() = default;
72  ~pairwise_combine_view() = default;
73 
90  explicit constexpr pairwise_combine_view(underlying_range_type range) : u_range{std::move(range)}
91  {
92  // Check if range is empty.
93  if (std::ranges::empty(u_range))
94  {
95  back_iterator = std::ranges::end(u_range);
96  }
97  else
98  {
99  if constexpr (std::ranges::bidirectional_range<underlying_range_type>)
100  { // Simply take one before the end. We can do this as we require underlying_range_type to be a common range.
101  back_iterator = std::ranges::prev(std::ranges::end(u_range));
102  }
103  else
104  { // For all other cases we need to set the back_iterator in linear time to the correct position.
105  back_iterator = std::ranges::begin(u_range);
106  if constexpr (std::ranges::sized_range<underlying_range_type>)
107  {
108  std::ranges::advance(back_iterator, std::ranges::size(u_range) - 1);
109  }
110  else // We don't have the size, so we need to increment until one before the end in a linear pass.
111  {
112  auto tmp_it = back_iterator;
113  do
114  {
115  back_iterator = tmp_it;
116  } while (++tmp_it != std::ranges::end(u_range));
117  }
118  }
119  }
120  }
121 
141  template <typename other_range_t>
143  requires (!std::same_as<std::remove_cvref_t<other_range_t>, pairwise_combine_view>) &&
144  std::ranges::viewable_range<other_range_t> && // Must come after self type check to avoid conflicts with the move constructor.
145  std::constructible_from<underlying_range_type,
146  std::ranges::ref_view<std::remove_reference_t<other_range_t>>>
147  //TODO: Investigate: the following expression is equivalent to the one above but raises a weird assertion in
148  // the ranges adaptor suggesting that the pairwise_combine_view is not a viewable_range.
149  // std::constructible_from<underlying_range_type, decltype(std::views::all(std::declval<other_range_t &&>()))>
151  explicit constexpr pairwise_combine_view(other_range_t && range) :
152  pairwise_combine_view{std::views::all(std::forward<other_range_t>(range))}
153  {}
154 
171  constexpr iterator begin() noexcept
172  {
173  return {std::ranges::begin(u_range), std::ranges::begin(u_range), std::ranges::end(u_range)};
174  }
175 
177  constexpr const_iterator begin() const noexcept
179  requires const_iterable_range<underlying_range_type>
181  {
182  return {std::ranges::cbegin(u_range), std::ranges::cbegin(u_range), std::ranges::cend(u_range)};
183  }
184 
198  constexpr iterator end() noexcept
199  {
200  return {back_iterator, std::ranges::begin(u_range), std::ranges::end(u_range)};
201  }
202 
204  constexpr const_iterator end() const noexcept
206  requires const_iterable_range<underlying_range_type>
208  {
209  return {back_iterator, std::ranges::cbegin(u_range), std::ranges::cend(u_range)};
210  }
212 
217  constexpr auto size() const noexcept
219  requires std::ranges::sized_range<underlying_range_type>
221  {
222  auto const size = std::ranges::size(u_range);
223  return (size * (size - 1)) / 2;
224  }
226 
227 private:
228 
230  underlying_range_type u_range{};
232  std::ranges::iterator_t<underlying_range_type> back_iterator{};
233 };
234 
240 template <std::ranges::viewable_range other_range_t>
241 pairwise_combine_view(other_range_t && range) ->
244 
258 template <std::ranges::view underlying_range_type>
260  requires std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>
262 template <typename range_type>
263 class pairwise_combine_view<underlying_range_type>::basic_iterator
264 {
265 private:
266 
268  template <typename other_range_type>
269  requires std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
270  friend class basic_iterator;
271 
273  using underlying_iterator_type = std::ranges::iterator_t<range_type>;
278 
279 public:
291  using pointer = void;
297 
301  basic_iterator() = default;
302  basic_iterator(basic_iterator const &) = default;
304  basic_iterator & operator=(basic_iterator const &) = default;
306  ~basic_iterator() = default;
307 
321  underlying_iterator_type begin_it,
322  underlying_iterator_type end_it) noexcept :
323  first_it{iter},
324  second_it{std::ranges::next(iter, 1, end_it)},
325  begin_it{begin_it},
326  end_it{end_it}
327  {}
328 
337  template <typename other_range_type>
339  requires std::convertible_to<other_range_type, range_type &> &&
340  std::same_as<std::remove_const_t<other_range_type>, std::remove_const_t<range_type>>
343  basic_iterator{std::move(other.first_it), std::move(other.begin_it), std::move(other.end_it)}
344  {}
346 
351  constexpr reference operator*() const
352  noexcept(noexcept(*std::declval<underlying_iterator_type>()))
353  {
354  return reference{*first_it, *second_it};
355  }
356 
360  constexpr reference operator[](size_t const index) const
361  noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
365  {
366  return *(*this + index);
367  }
369 
374  constexpr basic_iterator & operator++(/*pre-increment*/)
375  noexcept(noexcept(++std::declval<underlying_iterator_type &>()))
376  {
377  if (++second_it == end_it)
378  {
379  ++first_it;
380  second_it = first_it;
381  ++second_it;
382  }
383  return *this;
384  }
385 
387  constexpr basic_iterator operator++(int /*post-increment*/)
388  noexcept(noexcept(std::declval<underlying_iterator_type &>()++))
389  {
390  basic_iterator tmp{*this};
391  ++*this;
392  return tmp;
393  }
394 
396  constexpr basic_iterator & operator--(/*pre-decrement*/)
397  noexcept(noexcept(--std::declval<underlying_iterator_type &>()))
399  requires std::bidirectional_iterator<underlying_iterator_type>
401  {
402  if (--second_it == first_it)
403  {
404  --first_it;
405  second_it = end_it;
406  --second_it;
407  }
408  return *this;
409  }
410 
412  constexpr basic_iterator operator--(int /*post-decrement*/)
413  noexcept(noexcept(std::declval<underlying_iterator_type &>()--))
415  requires std::bidirectional_iterator<underlying_iterator_type>
417  {
418  basic_iterator tmp{*this};
419  --*this;
420  return tmp;
421  }
422 
425  constexpr basic_iterator & operator+=(difference_type const offset)
426  noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
430  {
431  from_index(to_index() + offset);
432  return *this;
433  }
434 
437  constexpr basic_iterator operator+(difference_type const offset) const
438  noexcept(noexcept(std::declval<basic_iterator &>() += 1))
442  {
443  basic_iterator tmp{*this};
444  return (tmp += offset);
445  }
446 
449  constexpr friend basic_iterator operator+(difference_type const offset, basic_iterator iter)
450  noexcept(noexcept(std::declval<basic_iterator<range_type> &>().from_index(1)))
454  {
455  iter.from_index(iter.to_index() + offset);
456  return iter;
457  }
458 
461  constexpr basic_iterator & operator-=(difference_type const offset)
462  noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
466  {
467  from_index(to_index() - offset);
468  return *this;
469  }
470 
473  constexpr basic_iterator operator-(difference_type const offset) const
474  noexcept(noexcept(std::declval<basic_iterator &>() -= 1))
478  {
479  basic_iterator tmp{*this};
480  return (tmp -= offset);
481  }
482 
485  template <typename other_range_type>
487  requires std::random_access_iterator<underlying_iterator_type> &&
488  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
491  noexcept(noexcept(std::declval<basic_iterator &>().to_index()))
492  {
493  return static_cast<difference_type>(to_index() - rhs.to_index());
494  }
496 
502  //NOTE: The comparison operators should be implemented as friends, but due to a bug in gcc friend function
503  // cannot yet be constrained. To avoid unexpected errors with the comparison all operators are implemented as
504  // direct members and not as friends.
505 
507  template <typename other_range_type>
509  requires std::equality_comparable_with<underlying_iterator_type, std::ranges::iterator_t<other_range_type>> &&
510  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
512  constexpr bool operator==(basic_iterator<other_range_type> const & rhs) const
513  noexcept(noexcept(std::declval<underlying_iterator_type &>() == std::declval<underlying_iterator_type &>()))
514  {
515  return std::tie(first_it, second_it) == std::tie(rhs.first_it, rhs.second_it);
516  }
517 
519  template <typename other_range_type>
521  requires std::equality_comparable_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>>
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 !(*this == rhs);
528  }
529 
531  template <typename other_range_type>
533  requires std::totally_ordered_with<underlying_iterator_type,
534  std::ranges::iterator_t<other_range_type>> &&
535  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
537  constexpr bool operator<(basic_iterator<other_range_type> const & rhs) const
538  noexcept(noexcept(std::declval<underlying_iterator_type &>() < std::declval<underlying_iterator_type &>()))
539  {
540  return std::tie(first_it, second_it) < std::tie(rhs.first_it, rhs.second_it);
541  }
542 
544  template <typename other_range_type>
546  requires std::totally_ordered_with<underlying_iterator_type,
547  std::ranges::iterator_t<other_range_type>> &&
548  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
550  constexpr bool operator>(basic_iterator<other_range_type> const & rhs) const
551  noexcept(noexcept(std::declval<underlying_iterator_type &>() > std::declval<underlying_iterator_type &>()))
552 
553  {
554  return std::tie(first_it, second_it) > std::tie(rhs.first_it, rhs.second_it);
555  }
556 
558  template <typename other_range_type>
560  requires std::totally_ordered_with<underlying_iterator_type,
561  std::ranges::iterator_t<other_range_type>> &&
562  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
564  constexpr bool operator<=(basic_iterator<other_range_type> const & rhs) const
565  noexcept(noexcept(std::declval<underlying_iterator_type &>() <= std::declval<underlying_iterator_type &>()))
566  {
567  return std::tie(first_it, second_it) <= std::tie(rhs.first_it, rhs.second_it);
568  }
569 
571  template <typename other_range_type>
573  requires std::totally_ordered_with<underlying_iterator_type,
574  std::ranges::iterator_t<other_range_type>> &&
575  std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>
577  constexpr bool operator>=(basic_iterator<other_range_type> const & rhs) const
578  noexcept(noexcept(std::declval<underlying_iterator_type &>() >= std::declval<underlying_iterator_type &>()))
579  {
580  return std::tie(first_it, second_it) >= std::tie(rhs.first_it, rhs.second_it);
581  }
583 
584 private:
585 
598  constexpr size_t to_index() const
599  noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()))
603  {
604  size_t src_size = end_it - begin_it;
605  size_t index_i = first_it - begin_it;
606  size_t index_j = second_it - begin_it;
607  return (src_size * (src_size - 1)/2) - (src_size - index_i) * ((src_size - index_i) - 1)/2 +
608  index_j - index_i - 1;
609  }
610 
615  constexpr void from_index(size_t const index)
616  noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()) &&
617  noexcept(std::declval<underlying_iterator_type &>() + 1))
621  {
622  size_t src_size = end_it - begin_it;
623  size_t index_i = src_size - 2 -
624  std::floor(std::sqrt(-8 * index + 4 * src_size * (src_size - 1) - 7)/2.0 - 0.5);
625  size_t index_j = index + index_i + 1 - src_size * (src_size - 1)/2 + (src_size - index_i) *
626  ((src_size - index_i) - 1)/2;
627  first_it = begin_it + index_i;
628  second_it = begin_it + index_j;
629  }
630 
639 };
640 
641 } // namespace seqan3::detail
642 
643 namespace seqan3::views
644 {
710 
712 } // namespace seqan3::views
Template for range adaptor closure objects that store no arguments and always delegate to the view co...
Definition: detail.hpp:292
The forward declared iterator type for pairwise_combine_view.
Definition: pairwise_combine.hpp:264
constexpr basic_iterator operator++(int) noexcept(noexcept(std::declval< underlying_iterator_type & >()++))
Post-increment operator.
Definition: pairwise_combine.hpp:387
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:461
detail::iterator_category_tag_t< underlying_iterator_type > iterator_category
The iterator category tag.
Definition: pairwise_combine.hpp:293
constexpr reference operator*() const noexcept(noexcept(*std::declval< underlying_iterator_type >()))
Accesses the pointed-to element.
Definition: pairwise_combine.hpp:351
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:449
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:342
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:412
constexpr basic_iterator & operator++() noexcept(noexcept(++std::declval< underlying_iterator_type & >()))
Pre-increment operator.
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:550
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:577
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:615
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:490
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:524
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:512
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:598
common_tuple< underlying_ref_t, underlying_ref_t > reference
The reference type.
Definition: pairwise_combine.hpp:289
void pointer
The pointer type.
Definition: pairwise_combine.hpp:291
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:564
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:320
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:473
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:396
std::ranges::iterator_t< range_type > underlying_iterator_type
Alias type for the iterator over the passed range type.
Definition: pairwise_combine.hpp:273
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:425
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:360
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:537
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:437
Generates all pairwise combinations of the elements in the underlying range.
Definition: pairwise_combine.hpp:45
underlying_range_type u_range
The underling range.
Definition: pairwise_combine.hpp:230
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:177
pairwise_combine_view(pairwise_combine_view &&)=default
Defaulted.
constexpr pairwise_combine_view(underlying_range_type range)
Constructs from a view.
Definition: pairwise_combine.hpp:90
constexpr iterator begin() noexcept
Returns an iterator to the first element of the range.
Definition: pairwise_combine.hpp:171
constexpr pairwise_combine_view(other_range_t &&range)
Constructs from a view.
Definition: pairwise_combine.hpp:151
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:204
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:60
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:232
~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:217
constexpr iterator end() noexcept
Returns an iterator to the element following the last element of the range.
Definition: pairwise_combine.hpp:198
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
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:150
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
auto const move
A view that turns lvalue-references into rvalue-references.
Definition: move.hpp:70
constexpr auto pairwise_combine
A view adaptor that generates all pairwise combinations of the elements of the underlying range.
Definition: pairwise_combine.hpp:709
Specifies requirements of an input range type for which the const version of that type satisfies the ...
The internal SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:29
typename std::iterator_traits< it_t >::iterator_category iterator_category_tag_t
Exposes the iterator_category from the modelled concept.
Definition: iterator_traits.hpp:200
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.
Additional non-standard concepts for ranges.
Auxiliary header for the views submodule .
Adaptations of concepts from the Ranges TS.
T sqrt(T... args)
T tie(T... args)
Provides seqan3::common_tuple and seqan3::common_pair.
Provides seqan3::detail::transformation_trait_or.