SeqAn3  3.0.0
The Modern C++ library for sequence analysis.
pairwise_combine.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2019, 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 
17 #include <seqan3/range/concept.hpp>
19 #include <seqan3/std/ranges>
20 
21 namespace seqan3::detail
22 {
38 template <std::ranges::View underlying_range_type>
41 // !\endcond
42 class pairwise_combine_view : public ranges::view_interface<pairwise_combine_view<underlying_range_type>>
43 {
44 private:
45 
59  template <typename range_type>
60  class iterator_type
61  {
62  private:
63 
65  template <typename other_range_type>
67  friend class iterator_type;
68 
70  using underlying_iterator_type = std::ranges::iterator_t<range_type>;
72  using underlying_val_t = typename std::iterator_traits<underlying_iterator_type>::value_type;
74  using underlying_ref_t = typename std::iterator_traits<underlying_iterator_type>::reference;
75  public:
76 
81  using difference_type = std::ptrdiff_t;
88  using pointer = void;
90  using iterator_category = iterator_tag_t<underlying_iterator_type>;
92 
96  constexpr iterator_type() noexcept = default;
97  constexpr iterator_type(iterator_type const &) noexcept = default;
98  constexpr iterator_type(iterator_type &&) noexcept = default;
99  constexpr iterator_type & operator=(iterator_type const &) noexcept = default;
100  constexpr iterator_type & operator=(iterator_type &&) noexcept = default;
101  ~iterator_type() noexcept = default;
102 
115  constexpr iterator_type(underlying_iterator_type iter,
116  underlying_iterator_type begin_it,
117  underlying_iterator_type end_it) noexcept :
118  first_it{iter},
119  second_it{++iter},
120  begin_it{begin_it},
121  end_it{end_it}
122  {}
123 
132  template <std::ConvertibleTo<range_type &> other_range_type>
134  constexpr iterator_type(iterator_type<other_range_type> other) noexcept :
135  iterator_type{std::move(other.first_it), std::move(other.begin_it), std::move(other.end_it)}
136  {}
138 
142  constexpr reference operator*() const
144  noexcept(noexcept(*std::declval<underlying_iterator_type>()))
145  {
146  return {*first_it, *second_it};
147  }
148 
152  constexpr reference operator[](size_t const index)
153  noexcept(noexcept(std::declval<iterator_type &>().from_index(1)))
155  requires std::RandomAccessIterator<underlying_iterator_type>
157  {
158  from_index(index);
159  return **this;
160  }
162 
166  constexpr iterator_type & operator++(/*pre-increment*/)
168  noexcept(noexcept(++std::declval<underlying_iterator_type &>()))
169  {
170  if (++second_it == end_it)
171  {
172  ++first_it;
173  second_it = first_it;
174  ++second_it;
175  }
176  return *this;
177  }
178 
180  constexpr iterator_type operator++(int /*post-increment*/)
181  noexcept(noexcept(std::declval<underlying_iterator_type &>()++))
182  {
183  iterator_type tmp{*this};
184  ++*this;
185  return tmp;
186  }
187 
189  constexpr iterator_type & operator--(/*pre-decrement*/)
190  noexcept(noexcept(--std::declval<underlying_iterator_type &>()))
192  requires std::BidirectionalIterator<underlying_iterator_type>
194  {
195  if (--second_it == first_it)
196  {
197  --first_it;
198  second_it = end_it;
199  --second_it;
200  }
201  return *this;
202  }
203 
205  constexpr iterator_type operator--(int /*post-decrement*/)
206  noexcept(noexcept(std::declval<underlying_iterator_type &>()--))
208  requires std::BidirectionalIterator<underlying_iterator_type>
210  {
211  iterator_type tmp{*this};
212  --*this;
213  return tmp;
214  }
215 
218  constexpr iterator_type & operator+=(difference_type const offset)
219  noexcept(noexcept(std::declval<iterator_type &>().from_index(1)))
221  requires std::RandomAccessIterator<underlying_iterator_type>
223  {
224  from_index(to_index() + offset);
225  return *this;
226  }
227 
230  constexpr iterator_type operator+(difference_type const offset)
231  noexcept(noexcept(std::declval<iterator_type &>() += 1))
233  requires std::RandomAccessIterator<underlying_iterator_type>
235  {
236  iterator_type tmp{*this};
237  return (tmp += offset);
238  }
239 
242  constexpr friend iterator_type operator+(difference_type const offset, iterator_type iter)
243  noexcept(noexcept(std::declval<iterator_type<range_type> &>().from_index(1)))
245  requires std::RandomAccessIterator<underlying_iterator_type>
247  {
248  iter.from_index(iter.to_index() + offset);
249  return iter;
250  }
251 
254  constexpr iterator_type & operator-=(difference_type const offset)
255  noexcept(noexcept(std::declval<iterator_type &>().from_index(1)))
257  requires std::RandomAccessIterator<underlying_iterator_type>
259  {
260  from_index(to_index() - offset);
261  return *this;
262  }
263 
266  constexpr iterator_type operator-(difference_type const offset)
267  noexcept(noexcept(std::declval<iterator_type &>() -= 1))
269  requires std::RandomAccessIterator<underlying_iterator_type>
271  {
272  iterator_type tmp{*this};
273  return (tmp -= offset);
274  }
275 
278  template <typename other_range_type>
283  constexpr difference_type operator-(iterator_type<other_range_type> const & rhs) const
284  noexcept(noexcept(std::declval<iterator_type &>().to_index()))
285  {
286  return static_cast<difference_type>(to_index() - rhs.to_index());
287  }
289 
295  //NOTE: The comparison operators should be implemented as friends, but due to a bug in gcc friend function
296  // cannot yet be constrained. To avoid unexpected errors with the comparison all operators are implemented as
297  // direct members and not as friends.
298 
300  template <typename other_range_type>
305  constexpr bool operator==(iterator_type<other_range_type> const & rhs) const
306  noexcept(noexcept(std::declval<underlying_iterator_type &>() == std::declval<underlying_iterator_type &>()))
307  {
308  return std::tie(first_it, second_it) == std::tie(rhs.first_it, rhs.second_it);
309  }
310 
312  template <typename other_range_type>
317  constexpr bool operator!=(iterator_type<other_range_type> const & rhs) const
318  noexcept(noexcept(std::declval<underlying_iterator_type &>() != std::declval<underlying_iterator_type &>()))
319  {
320  return !(*this == rhs);
321  }
322 
324  template <typename other_range_type>
326  requires std::StrictTotallyOrderedWith<underlying_iterator_type,
330  constexpr bool operator<(iterator_type<other_range_type> const & rhs) const
331  noexcept(noexcept(std::declval<underlying_iterator_type &>() < std::declval<underlying_iterator_type &>()))
332  {
333  return std::tie(first_it, second_it) < std::tie(rhs.first_it, rhs.second_it);
334  }
335 
337  template <typename other_range_type>
339  requires std::StrictTotallyOrderedWith<underlying_iterator_type,
343  constexpr bool operator>(iterator_type<other_range_type> const & rhs) const
344  noexcept(noexcept(std::declval<underlying_iterator_type &>() > std::declval<underlying_iterator_type &>()))
345 
346  {
347  return std::tie(first_it, second_it) > std::tie(rhs.first_it, rhs.second_it);
348  }
349 
351  template <typename other_range_type>
353  requires std::StrictTotallyOrderedWith<underlying_iterator_type,
357  constexpr bool operator<=(iterator_type<other_range_type> const & rhs) const
358  noexcept(noexcept(std::declval<underlying_iterator_type &>() <= std::declval<underlying_iterator_type &>()))
359  {
360  return std::tie(first_it, second_it) <= std::tie(rhs.first_it, rhs.second_it);
361  }
362 
364  template <typename other_range_type>
366  requires std::StrictTotallyOrderedWith<underlying_iterator_type,
370  constexpr bool operator>=(iterator_type<other_range_type> const & rhs) const
371  noexcept(noexcept(std::declval<underlying_iterator_type &>() >= std::declval<underlying_iterator_type &>()))
372  {
373  return std::tie(first_it, second_it) >= std::tie(rhs.first_it, rhs.second_it);
374  }
376 
377  private:
378 
391  constexpr size_t to_index() const
392  noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()))
394  requires std::RandomAccessIterator<underlying_iterator_type>
396  {
397  size_t src_size = end_it - begin_it;
398  size_t index_i = first_it - begin_it;
399  size_t index_j = second_it - begin_it;
400  return (src_size * (src_size - 1)/2) - (src_size - index_i) * ((src_size - index_i) - 1)/2 +
401  index_j - index_i - 1;
402  }
403 
408  constexpr void from_index(size_t const index)
409  noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()) &&
410  noexcept(std::declval<underlying_iterator_type &>() + 1))
412  requires std::RandomAccessIterator<underlying_iterator_type>
414  {
415  size_t src_size = end_it - begin_it;
416  size_t index_i = src_size - 2 -
417  std::floor(std::sqrt(-8 * index + 4 * src_size * (src_size - 1) - 7)/2.0 - 0.5);
418  size_t index_j = index + index_i + 1 - src_size * (src_size - 1)/2 + (src_size - index_i) *
419  ((src_size - index_i) - 1)/2;
420  first_it = begin_it + index_i;
421  second_it = begin_it + index_j;
422  }
423 
425  underlying_iterator_type first_it{};
427  underlying_iterator_type second_it{};
429  underlying_iterator_type begin_it{};
431  underlying_iterator_type end_it{};
432  };
433 
434 public:
435 
440  using iterator = iterator_type<underlying_range_type>;
443  using const_iterator = transformation_trait_or_t<std::type_identity<iterator_type<underlying_range_type const>>,
444  void>;
446  using reference = typename iterator::reference;
448  using const_reference = reference;
450  using value_type = typename iterator::value_type;
452  using size_type = detail::transformation_trait_or_t<seqan3::size_type<underlying_range_type>, void>;
454  using difference_type = difference_type_t<iterator>;
456 
460  constexpr pairwise_combine_view() = default;
463  constexpr pairwise_combine_view(pairwise_combine_view const &) = default;
465  constexpr pairwise_combine_view(pairwise_combine_view &&) = default;
467  constexpr pairwise_combine_view & operator=(pairwise_combine_view const &) = default;
469  constexpr pairwise_combine_view & operator=(pairwise_combine_view &&) = default;
471  ~pairwise_combine_view() = default;
472 
489  explicit constexpr pairwise_combine_view(underlying_range_type range) : u_range{std::move(range)}
490  {
491  // Check if range is empty.
492  if (std::ranges::empty(u_range))
493  {
494  back_iterator = std::ranges::end(u_range);
495  }
496  else
497  {
499  { // Simply take one before the end. We can do this as we require underlying_range_type to be a common range.
500  back_iterator = std::ranges::prev(std::ranges::end(u_range));
501  }
502  else
503  { // For all other cases we need to set the back_iterator in linear time to the correct position.
504  back_iterator = std::ranges::begin(u_range);
506  {
507  std::ranges::advance(back_iterator, std::ranges::size(u_range) - 1);
508  }
509  else // We don't have the size, so we need to increment until one before the end in a linear pass.
510  {
511  auto tmp_it = back_iterator;
512  do
513  {
514  back_iterator = tmp_it;
515  } while (++tmp_it != std::ranges::end(u_range));
516  }
517  }
518  }
519  }
520 
540  template <typename other_range_t>
542  requires !std::Same<remove_cvref_t<other_range_t>, pairwise_combine_view> &&
543  std::ranges::ViewableRange<other_range_t> && // Must come after self type check to avoid conflicts with the move constructor.
545  //TODO: Investigate: the following expression is equivalent to the one above but raises a weird assertion in
546  // the ranges adaptor suggesting that the pairwise_combine_view is not a ViewableRange.
547  // std::Constructible<underlying_range_type, decltype(std::ranges::view::all(std::declval<other_range_t &&>()))>
549  explicit constexpr pairwise_combine_view(other_range_t && range) :
550  pairwise_combine_view{std::ranges::view::all(std::forward<other_range_t>(range))}
551  {}
552 
569  constexpr iterator begin() noexcept
570  {
571  return {std::ranges::begin(u_range), std::ranges::begin(u_range), std::ranges::end(u_range)};
572  }
573 
575  constexpr const_iterator begin() const noexcept
577  requires ConstIterableRange<underlying_range_type>
579  {
580  return {std::ranges::begin(u_range), std::ranges::begin(u_range), std::ranges::end(u_range)};
581  }
582 
584  constexpr const_iterator cbegin() const noexcept
586  requires ConstIterableRange<underlying_range_type>
588  {
589  return begin();
590  }
591 
605  constexpr iterator end() noexcept
606  {
607  return {back_iterator, std::ranges::begin(u_range), std::ranges::end(u_range)};
608  }
609 
611  constexpr const_iterator end() const noexcept
613  requires ConstIterableRange<underlying_range_type>
615  {
616  return {back_iterator, std::ranges::begin(u_range), std::ranges::end(u_range)};
617  }
618 
620  constexpr const_iterator cend() const noexcept
622  requires ConstIterableRange<underlying_range_type>
624  {
625  return end();
626  }
628 
632  constexpr size_type size() const noexcept
635  requires std::ranges::SizedRange<underlying_range_type>
637  {
638  return (std::ranges::size(u_range) * (std::ranges::size(u_range) - 1) / 2);
639  }
641 
642 private:
643 
645  underlying_range_type u_range{};
648 };
649 
654 template <std::ranges::ViewableRange other_range_t>
656 pairwise_combine_view(other_range_t && range) ->
657  pairwise_combine_view<std::ranges::all_view<other_range_t>>;
659 
660 } // namespace seqan3::detail
661 
662 namespace seqan3::view
663 {
731 inline constexpr auto pairwise_combine = detail::adaptor_for_view_without_args<detail::pairwise_combine_view>{};
732 
734 } // namespace seqan3::view
::ranges::cbegin cbegin
Alias for ranges::cbegin. Returns an iterator to the beginning of a range.
Definition: ranges:209
Specifies the requirements of a Range type that is either a std::ranges::View or an lvalue-reference...
::ranges::prev prev
Alias for ranges::prev. Returns the nth predecessor of the given iterator.
Definition: iterator:326
T tie(T... args)
T operator!=(T... args)
Specifies requirements of a Range type for which begin and end return objects of the same type...
constexpr auto pairwise_combine
A view adaptor that generates all pairwise combinations of the elements of the underlying range...
Definition: pairwise_combine.hpp:731
SeqAn specific customisations in the standard namespace.
constexpr auto all
A view adaptor that behaves like std::view:all, but type erases contiguous ranges.
Definition: view_all.hpp:160
T floor(T... args)
::ranges::size size
Alias for ranges::size. Obtains the size of a range whose size can be calculated in constant time...
Definition: ranges:189
::ranges::view_interface< urng_t > view_interface
Alias for ranges::view_interface.
Definition: ranges:220
Requires std::EqualityComparable and all remaing comparison operators (<, <=, >, >=).
Specifies the requirements of a Range type that knows its size in constant time with the size functio...
Specifies requirements of a Range type for which begin returns a type that models std::BidirectionalI...
Additional non-standard concepts for ranges.
Auxiliary header for the view submodule .
::ranges::iterator_t iterator_t
Alias for ranges::iterator_t. Obtains the iterator type of a range.
Definition: ranges:204
T declval(T... args)
Adaptations of concepts from the Ranges TS.
::ranges::begin begin
Alias for ranges::begin. Returns an iterator to the beginning of a range.
Definition: ranges:174
::ranges::advance advance
Alias for ranges::advance. Advances the iterator by the given distance.
Definition: iterator:316
The SeqAn3 namespace for views.
Definition: aligned_sequence_concept.hpp:35
The concept RandomAccessIterator refines std::BidirectionalIterator by adding support for constant ti...
The std::Constructible concept specifies that a variable of type T can be initialized with the given ...
Requires std::detail::WeaklyEqualityComparableWitht<t1,t2>, but also that t1 and t2, as well as their common_reference_t satisfy std::EqualityComparable.
Definition: ranges:240
::ranges::empty empty
Alias for ranges::empty. Checks whether a range is empty.
Definition: ranges:194
T sqrt(T... args)
Specifies requirements of a Range type for which begin returns a type that models std::ForwardIterato...
::ranges::cend cend
Alias for ranges::cend. Returns an iterator to the end of a range.
Definition: ranges:214
The concept std::Same<T, U> is satisfied if and only if T and U denote the same type.
::ranges::end end
Alias for ranges::end. Returns an iterator to the end of a range.
Definition: ranges:179