SeqAn3  3.0.1
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 
18 #include <seqan3/range/concept.hpp>
20 #include <seqan3/std/ranges>
21 
22 namespace seqan3::detail
23 {
39 template <std::ranges::view underlying_range_type>
41  requires std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>
42 // !\endcond
43 class pairwise_combine_view : public std::ranges::view_interface<pairwise_combine_view<underlying_range_type>>
44 {
45 private:
46 
60  template <typename range_type>
61  class iterator_type
62  {
63  private:
64 
66  template <typename other_range_type>
68  friend class iterator_type;
69 
71  using underlying_iterator_type = std::ranges::iterator_t<range_type>;
73  using underlying_val_t = typename std::iterator_traits<underlying_iterator_type>::value_type;
75  using underlying_ref_t = typename std::iterator_traits<underlying_iterator_type>::reference;
76 
77  public:
82  using difference_type = std::ptrdiff_t;
87  using reference = common_tuple<underlying_ref_t, underlying_ref_t>;
89  using pointer = void;
91  using iterator_category = iterator_tag_t<underlying_iterator_type>;
93 
97  constexpr iterator_type() noexcept = default;
98  constexpr iterator_type(iterator_type const &) noexcept = default;
99  constexpr iterator_type(iterator_type &&) noexcept = default;
100  constexpr iterator_type & operator=(iterator_type const &) noexcept = default;
101  constexpr iterator_type & operator=(iterator_type &&) noexcept = default;
102  ~iterator_type() noexcept = default;
103 
116  constexpr iterator_type(underlying_iterator_type iter,
117  underlying_iterator_type begin_it,
118  underlying_iterator_type end_it) noexcept :
119  first_it{iter},
120  second_it{++iter},
121  begin_it{begin_it},
122  end_it{end_it}
123  {}
124 
133  template <typename other_range_type>
138  constexpr iterator_type(iterator_type<other_range_type> other) noexcept :
139  iterator_type{std::move(other.first_it), std::move(other.begin_it), std::move(other.end_it)}
140  {}
142 
146  constexpr reference operator*() const
148  noexcept(noexcept(*std::declval<underlying_iterator_type>()))
149  {
150  return reference{*first_it, *second_it};
151  }
152 
156  constexpr reference operator[](size_t const index)
157  noexcept(noexcept(std::declval<iterator_type &>().from_index(1)))
159  requires std::random_access_iterator<underlying_iterator_type>
161  {
162  from_index(index);
163  return **this;
164  }
166 
170  constexpr iterator_type & operator++(/*pre-increment*/)
172  noexcept(noexcept(++std::declval<underlying_iterator_type &>()))
173  {
174  if (++second_it == end_it)
175  {
176  ++first_it;
177  second_it = first_it;
178  ++second_it;
179  }
180  return *this;
181  }
182 
184  constexpr iterator_type operator++(int /*post-increment*/)
185  noexcept(noexcept(std::declval<underlying_iterator_type &>()++))
186  {
187  iterator_type tmp{*this};
188  ++*this;
189  return tmp;
190  }
191 
193  constexpr iterator_type & operator--(/*pre-decrement*/)
194  noexcept(noexcept(--std::declval<underlying_iterator_type &>()))
196  requires std::bidirectional_iterator<underlying_iterator_type>
198  {
199  if (--second_it == first_it)
200  {
201  --first_it;
202  second_it = end_it;
203  --second_it;
204  }
205  return *this;
206  }
207 
209  constexpr iterator_type operator--(int /*post-decrement*/)
210  noexcept(noexcept(std::declval<underlying_iterator_type &>()--))
212  requires std::bidirectional_iterator<underlying_iterator_type>
214  {
215  iterator_type tmp{*this};
216  --*this;
217  return tmp;
218  }
219 
222  constexpr iterator_type & operator+=(difference_type const offset)
223  noexcept(noexcept(std::declval<iterator_type &>().from_index(1)))
225  requires std::random_access_iterator<underlying_iterator_type>
227  {
228  from_index(to_index() + offset);
229  return *this;
230  }
231 
234  constexpr iterator_type operator+(difference_type const offset)
235  noexcept(noexcept(std::declval<iterator_type &>() += 1))
237  requires std::random_access_iterator<underlying_iterator_type>
239  {
240  iterator_type tmp{*this};
241  return (tmp += offset);
242  }
243 
246  constexpr friend iterator_type operator+(difference_type const offset, iterator_type iter)
247  noexcept(noexcept(std::declval<iterator_type<range_type> &>().from_index(1)))
249  requires std::random_access_iterator<underlying_iterator_type>
251  {
252  iter.from_index(iter.to_index() + offset);
253  return iter;
254  }
255 
258  constexpr iterator_type & operator-=(difference_type const offset)
259  noexcept(noexcept(std::declval<iterator_type &>().from_index(1)))
261  requires std::random_access_iterator<underlying_iterator_type>
263  {
264  from_index(to_index() - offset);
265  return *this;
266  }
267 
270  constexpr iterator_type operator-(difference_type const offset)
271  noexcept(noexcept(std::declval<iterator_type &>() -= 1))
273  requires std::random_access_iterator<underlying_iterator_type>
275  {
276  iterator_type tmp{*this};
277  return (tmp -= offset);
278  }
279 
282  template <typename other_range_type>
284  requires std::random_access_iterator<underlying_iterator_type> &&
287  constexpr difference_type operator-(iterator_type<other_range_type> const & rhs) const
288  noexcept(noexcept(std::declval<iterator_type &>().to_index()))
289  {
290  return static_cast<difference_type>(to_index() - rhs.to_index());
291  }
293 
299  //NOTE: The comparison operators should be implemented as friends, but due to a bug in gcc friend function
300  // cannot yet be constrained. To avoid unexpected errors with the comparison all operators are implemented as
301  // direct members and not as friends.
302 
304  template <typename other_range_type>
309  constexpr bool operator==(iterator_type<other_range_type> const & rhs) const
310  noexcept(noexcept(std::declval<underlying_iterator_type &>() == std::declval<underlying_iterator_type &>()))
311  {
312  return std::tie(first_it, second_it) == std::tie(rhs.first_it, rhs.second_it);
313  }
314 
316  template <typename other_range_type>
321  constexpr bool operator!=(iterator_type<other_range_type> const & rhs) const
322  noexcept(noexcept(std::declval<underlying_iterator_type &>() != std::declval<underlying_iterator_type &>()))
323  {
324  return !(*this == rhs);
325  }
326 
328  template <typename other_range_type>
330  requires std::totally_ordered_with<underlying_iterator_type,
331  std::ranges::iterator_t<other_range_type>> &&
334  constexpr bool operator<(iterator_type<other_range_type> const & rhs) const
335  noexcept(noexcept(std::declval<underlying_iterator_type &>() < std::declval<underlying_iterator_type &>()))
336  {
337  return std::tie(first_it, second_it) < std::tie(rhs.first_it, rhs.second_it);
338  }
339 
341  template <typename other_range_type>
343  requires std::totally_ordered_with<underlying_iterator_type,
344  std::ranges::iterator_t<other_range_type>> &&
347  constexpr bool operator>(iterator_type<other_range_type> const & rhs) const
348  noexcept(noexcept(std::declval<underlying_iterator_type &>() > std::declval<underlying_iterator_type &>()))
349 
350  {
351  return std::tie(first_it, second_it) > std::tie(rhs.first_it, rhs.second_it);
352  }
353 
355  template <typename other_range_type>
357  requires std::totally_ordered_with<underlying_iterator_type,
358  std::ranges::iterator_t<other_range_type>> &&
361  constexpr bool operator<=(iterator_type<other_range_type> const & rhs) const
362  noexcept(noexcept(std::declval<underlying_iterator_type &>() <= std::declval<underlying_iterator_type &>()))
363  {
364  return std::tie(first_it, second_it) <= std::tie(rhs.first_it, rhs.second_it);
365  }
366 
368  template <typename other_range_type>
370  requires std::totally_ordered_with<underlying_iterator_type,
371  std::ranges::iterator_t<other_range_type>> &&
374  constexpr bool operator>=(iterator_type<other_range_type> const & rhs) const
375  noexcept(noexcept(std::declval<underlying_iterator_type &>() >= std::declval<underlying_iterator_type &>()))
376  {
377  return std::tie(first_it, second_it) >= std::tie(rhs.first_it, rhs.second_it);
378  }
380 
381  private:
382 
395  constexpr size_t to_index() const
396  noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()))
398  requires std::random_access_iterator<underlying_iterator_type>
400  {
401  size_t src_size = end_it - begin_it;
402  size_t index_i = first_it - begin_it;
403  size_t index_j = second_it - begin_it;
404  return (src_size * (src_size - 1)/2) - (src_size - index_i) * ((src_size - index_i) - 1)/2 +
405  index_j - index_i - 1;
406  }
407 
412  constexpr void from_index(size_t const index)
413  noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()) &&
414  noexcept(std::declval<underlying_iterator_type &>() + 1))
416  requires std::random_access_iterator<underlying_iterator_type>
418  {
419  size_t src_size = end_it - begin_it;
420  size_t index_i = src_size - 2 -
421  std::floor(std::sqrt(-8 * index + 4 * src_size * (src_size - 1) - 7)/2.0 - 0.5);
422  size_t index_j = index + index_i + 1 - src_size * (src_size - 1)/2 + (src_size - index_i) *
423  ((src_size - index_i) - 1)/2;
424  first_it = begin_it + index_i;
425  second_it = begin_it + index_j;
426  }
427 
429  underlying_iterator_type first_it{};
431  underlying_iterator_type second_it{};
433  underlying_iterator_type begin_it{};
435  underlying_iterator_type end_it{};
436  };
437 
438 public:
439 
444  using iterator = iterator_type<underlying_range_type>;
447  using const_iterator = transformation_trait_or_t<std::type_identity<iterator_type<underlying_range_type const>>,
448  void>;
450  using reference = typename iterator::reference;
452  using const_reference = reference;
454  using value_type = typename iterator::value_type;
456  using size_type = detail::transformation_trait_or_t<seqan3::size_type<underlying_range_type>, void>;
458  using difference_type = difference_type_t<iterator>;
460 
464  constexpr pairwise_combine_view() = default;
467  constexpr pairwise_combine_view(pairwise_combine_view const &) = default;
469  constexpr pairwise_combine_view(pairwise_combine_view &&) = default;
471  constexpr pairwise_combine_view & operator=(pairwise_combine_view const &) = default;
473  constexpr pairwise_combine_view & operator=(pairwise_combine_view &&) = default;
475  ~pairwise_combine_view() = default;
476 
493  explicit constexpr pairwise_combine_view(underlying_range_type range) : u_range{std::move(range)}
494  {
495  // Check if range is empty.
496  if (std::ranges::empty(u_range))
497  {
498  back_iterator = std::ranges::end(u_range);
499  }
500  else
501  {
502  if constexpr (std::ranges::bidirectional_range<underlying_range_type>)
503  { // Simply take one before the end. We can do this as we require underlying_range_type to be a common range.
504  back_iterator = std::ranges::prev(std::ranges::end(u_range));
505  }
506  else
507  { // For all other cases we need to set the back_iterator in linear time to the correct position.
508  back_iterator = std::ranges::begin(u_range);
509  if constexpr (std::ranges::sized_range<underlying_range_type>)
510  {
511  std::ranges::advance(back_iterator, std::ranges::size(u_range) - 1);
512  }
513  else // We don't have the size, so we need to increment until one before the end in a linear pass.
514  {
515  auto tmp_it = back_iterator;
516  do
517  {
518  back_iterator = tmp_it;
519  } while (++tmp_it != std::ranges::end(u_range));
520  }
521  }
522  }
523  }
524 
544  template <typename other_range_t>
546  requires !std::same_as<remove_cvref_t<other_range_t>, pairwise_combine_view> &&
547  std::ranges::viewable_range<other_range_t> && // Must come after self type check to avoid conflicts with the move constructor.
549  //TODO: Investigate: the following expression is equivalent to the one above but raises a weird assertion in
550  // the ranges adaptor suggesting that the pairwise_combine_view is not a viewable_range.
551  // std::constructible_from<underlying_range_type, decltype(std::views::all(std::declval<other_range_t &&>()))>
553  explicit constexpr pairwise_combine_view(other_range_t && range) :
554  pairwise_combine_view{std::views::all(std::forward<other_range_t>(range))}
555  {}
556 
573  constexpr iterator begin() noexcept
574  {
575  return {std::ranges::begin(u_range), std::ranges::begin(u_range), std::ranges::end(u_range)};
576  }
577 
579  constexpr const_iterator begin() const noexcept
581  requires const_iterable_range<underlying_range_type>
583  {
584  return {std::ranges::begin(u_range), std::ranges::begin(u_range), std::ranges::end(u_range)};
585  }
586 
588  constexpr const_iterator cbegin() const noexcept
590  requires const_iterable_range<underlying_range_type>
592  {
593  return begin();
594  }
595 
609  constexpr iterator end() noexcept
610  {
611  return {back_iterator, std::ranges::begin(u_range), std::ranges::end(u_range)};
612  }
613 
615  constexpr const_iterator end() const noexcept
617  requires const_iterable_range<underlying_range_type>
619  {
620  return {back_iterator, std::ranges::begin(u_range), std::ranges::end(u_range)};
621  }
622 
624  constexpr const_iterator cend() const noexcept
626  requires const_iterable_range<underlying_range_type>
628  {
629  return end();
630  }
632 
636  constexpr size_type size() const noexcept
639  requires std::ranges::sized_range<underlying_range_type>
641  {
642  return (std::ranges::size(u_range) * (std::ranges::size(u_range) - 1) / 2);
643  }
645 
646 private:
647 
649  underlying_range_type u_range{};
651  std::ranges::iterator_t<underlying_range_type> back_iterator{};
652 };
653 
658 template <std::ranges::viewable_range other_range_t>
660 pairwise_combine_view(other_range_t && range) ->
661  pairwise_combine_view<std::ranges::all_view<other_range_t>>;
663 
664 } // namespace seqan3::detail
665 
666 namespace seqan3::views
667 {
732 inline constexpr auto pairwise_combine = detail::adaptor_for_view_without_args<detail::pairwise_combine_view>{};
733 
735 } // namespace seqan3::views
std::floor
T floor(T... args)
seqan3::views
The SeqAn namespace for views.
Definition: view_to_simd.hpp:672
seqan3::field::offset
Sequence (SEQ) relative start position (0-based), unsigned value.
constructible_from
The std::constructible_from concept specifies that a variable of type T can be initialized with the g...
std::rel_ops::operator!=
T operator!=(T... args)
seqan3::views::move
const auto move
A view that turns lvalue-references into rvalue-references.
Definition: move.hpp:68
std::tuple
totally_ordered_with
Requires std::equality_comparable and all remaing comparison operators (<, <=, >, >=).
cmath
std::sqrt
T sqrt(T... args)
concept.hpp
Additional non-standard concepts for ranges.
std::tie
T tie(T... args)
same_as
The concept std::same_as<T, U> is satisfied if and only if T and U denote the same type.
seqan3::views::pairwise_combine
constexpr auto pairwise_combine
A view adaptor that generates all pairwise combinations of the elements of the underlying range.
Definition: pairwise_combine.hpp:732
std::iterator_traits
const_iterable_range
Specifies requirements of an input range type for which the const version of that type satisfies the ...
convertible_to
The concept std::convertible_to<From, To> specifies that an expression of the type and value category...
seqan3::search_cfg::all
constexpr detail::search_mode_all all
Configuration element to receive all hits within the error bounds.
Definition: mode.hpp:43
seqan3::pack_traits::size
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:116
ranges
Adaptations of concepts from the Ranges TS.
std::ranges::begin
T begin(T... args)
std
SeqAn specific customisations in the standard namespace.
std::ptrdiff_t
equality_comparable_with
Requires seqan3::detail::weakly_equality_comparable_witht<t1,t2>, but also that t1 and t2,...
std::remove_const_t
common_tuple.hpp
Provides seqan3::common_tuple and seqan3::common_pair.
std::end
T end(T... args)
detail.hpp
Auxiliary header for the views submodule .
std::declval
T declval(T... args)