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 
57  using iterator = basic_iterator<underlying_range_type>;
59  using const_iterator = transformation_trait_or_t<std::type_identity<basic_iterator<underlying_range_type const>>,
60  void>;
62 
63 public:
67  pairwise_combine_view() = default;
68  pairwise_combine_view(pairwise_combine_view const &) = default;
69  pairwise_combine_view(pairwise_combine_view &&) = default;
70  pairwise_combine_view & operator=(pairwise_combine_view const &) = default;
71  pairwise_combine_view & operator=(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) ->
242  pairwise_combine_view<std::views::all_t<other_range_t>>;
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>;
275  using underlying_val_t = std::iter_value_t<underlying_iterator_type>;
277  using underlying_ref_t = std::iter_reference_t<underlying_iterator_type>;
278 
279 public:
285  using difference_type = std::ptrdiff_t;
289  using reference = common_tuple<underlying_ref_t, underlying_ref_t>;
291  using pointer = void;
293  using iterator_category = detail::iterator_category_tag_t<underlying_iterator_type>;
295  using iterator_concept = detail::iterator_concept_tag_t<underlying_iterator_type>;
297 
301  basic_iterator() = default;
302  basic_iterator(basic_iterator const &) = default;
303  basic_iterator(basic_iterator &&) = default;
304  basic_iterator & operator=(basic_iterator const &) = default;
305  basic_iterator & operator=(basic_iterator &&) = default;
306  ~basic_iterator() = default;
307 
320  constexpr basic_iterator(underlying_iterator_type iter,
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>>
342  constexpr basic_iterator(basic_iterator<other_range_type> other) noexcept :
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)))
363  requires std::random_access_iterator<underlying_iterator_type>
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)))
428  requires std::random_access_iterator<underlying_iterator_type>
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))
440  requires std::random_access_iterator<underlying_iterator_type>
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)))
452  requires std::random_access_iterator<underlying_iterator_type>
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)))
464  requires std::random_access_iterator<underlying_iterator_type>
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))
476  requires std::random_access_iterator<underlying_iterator_type>
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>>
490  constexpr difference_type operator-(basic_iterator<other_range_type> const & rhs) const
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 &>()))
601  requires std::random_access_iterator<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))
619  requires std::random_access_iterator<underlying_iterator_type>
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 
632  underlying_iterator_type first_it{};
634  underlying_iterator_type second_it{};
636  underlying_iterator_type begin_it{};
638  underlying_iterator_type end_it{};
639 };
640 
641 } // namespace seqan3::detail
642 
643 namespace seqan3::views
644 {
709 inline constexpr auto pairwise_combine = detail::adaptor_for_view_without_args<detail::pairwise_combine_view>{};
710 
712 } // namespace seqan3::views
T begin(T... args)
T declval(T... args)
T end(T... args)
T floor(T... args)
T forward(T... args)
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:150
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 SeqAn namespace for views.
SeqAn specific customisations in the standard namespace.
T operator!=(T... args)
Additional non-standard concepts for ranges.
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.