BioC++ core-0.7.0
The Modern C++ libraries for Bioinformatics.
 
Loading...
Searching...
No Matches
pairwise_combine.hpp
Go to the documentation of this file.
1// -----------------------------------------------------------------------------------------------------
2// Copyright (c) 2022 deCODE Genetics
3// Copyright (c) 2006-2020, Knut Reinert & Freie Universität Berlin
4// Copyright (c) 2016-2020, Knut Reinert & MPI für molekulare Genetik
5// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
6// shipped with this file and also available at: https://github.com/biocpp/biocpp-core/blob/main/LICENSE.md
7// -----------------------------------------------------------------------------------------------------
8
14#pragma once
15
16#include <cmath>
17#include <ranges>
18
22
23namespace bio::ranges::detail
24{
40template <std::ranges::view underlying_range_type>
41 requires(std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>)
42class pairwise_combine_view : public std::ranges::view_interface<pairwise_combine_view<underlying_range_type>>
43{
44private:
46 template <typename range_type2>
47 class basic_iterator;
48
54 using iterator = basic_iterator<underlying_range_type>;
56 using const_iterator =
57 meta::transformation_trait_or_t<std::type_identity<basic_iterator<underlying_range_type const>>, void>;
59
60public:
64 pairwise_combine_view() = default;
65 pairwise_combine_view(pairwise_combine_view const &) = default;
66 pairwise_combine_view(pairwise_combine_view &&) noexcept = default;
67 pairwise_combine_view & operator=(pairwise_combine_view const &) = default;
68 pairwise_combine_view & operator=(pairwise_combine_view &&) noexcept = default;
69 ~pairwise_combine_view() = default;
70
87 explicit constexpr pairwise_combine_view(underlying_range_type range) : u_range{std::move(range)}
88 {
89 // Check if range is empty.
90 if (std::ranges::empty(u_range))
91 {
92 back_iterator = std::ranges::end(u_range);
93 }
94 else
95 {
96 if constexpr (std::ranges::bidirectional_range<underlying_range_type>)
97 { // Simply take one before the end. We can do this as we require underlying_range_type to be a common range.
98 back_iterator = std::ranges::prev(std::ranges::end(u_range));
99 }
100 else
101 { // For all other cases we need to set the back_iterator in linear time to the correct position.
102 back_iterator = std::ranges::begin(u_range);
103 if constexpr (std::ranges::sized_range<underlying_range_type>)
104 {
105 std::ranges::advance(back_iterator, std::ranges::size(u_range) - 1);
106 }
107 else // We don't have the size, so we need to increment until one before the end in a linear pass.
108 {
109 auto tmp_it = back_iterator;
110 do
111 {
112 back_iterator = tmp_it;
113 }
114 while (++tmp_it != std::ranges::end(u_range));
115 }
116 }
117 }
118 }
119
139 template <meta::different_from<pairwise_combine_view> other_range_t>
140 requires(std::ranges::viewable_range<other_range_t> &&
141 std::constructible_from<underlying_range_type, std::views::all_t<other_range_t>>)
142 explicit constexpr pairwise_combine_view(other_range_t && range) :
143 pairwise_combine_view{std::views::all(std::forward<other_range_t>(range))}
144 {}
145
162 constexpr iterator begin() noexcept
163 {
164 return {std::ranges::begin(u_range), std::ranges::begin(u_range), std::ranges::end(u_range)};
165 }
166
168 constexpr const_iterator begin() const noexcept
169 requires const_iterable_range<underlying_range_type>
170 {
171 return {std::ranges::cbegin(u_range), std::ranges::cbegin(u_range), std::ranges::cend(u_range)};
172 }
173
187 constexpr iterator end() noexcept
188 {
189 return {back_iterator, std::ranges::begin(u_range), std::ranges::end(u_range)};
190 }
191
193 constexpr const_iterator end() const noexcept
194 requires const_iterable_range<underlying_range_type>
195 {
196 return {back_iterator, std::ranges::cbegin(u_range), std::ranges::cend(u_range)};
197 }
199
204 constexpr auto size() const noexcept
205 requires std::ranges::sized_range<underlying_range_type>
206 {
207 return (std::ranges::size(u_range) * (std::ranges::size(u_range) - 1) / 2);
208 }
210
211private:
213 underlying_range_type u_range;
215 std::ranges::iterator_t<underlying_range_type> back_iterator;
216};
217
223template <std::ranges::viewable_range other_range_t>
224pairwise_combine_view(other_range_t && range) -> pairwise_combine_view<std::views::all_t<other_range_t>>;
226
240template <std::ranges::view underlying_range_type>
241 requires(std::ranges::forward_range<underlying_range_type> && std::ranges::common_range<underlying_range_type>)
242template <typename range_type>
243class pairwise_combine_view<underlying_range_type>::basic_iterator
244{
245private:
247 template <typename range_type2>
248 friend class basic_iterator;
249
251 using underlying_iterator_type = std::ranges::iterator_t<range_type>;
253 using underlying_val_t = std::iter_value_t<underlying_iterator_type>;
256
257public:
263 using difference_type = std::ptrdiff_t;
269 using pointer = void;
271 using iterator_category = detail::iterator_category_tag_t<underlying_iterator_type>;
273 using iterator_concept = detail::iterator_concept_tag_t<underlying_iterator_type>;
275
279 basic_iterator() = default;
280 basic_iterator(basic_iterator const &) = default;
281 basic_iterator(basic_iterator &&) noexcept = default;
282 basic_iterator & operator=(basic_iterator const &) = default;
283 basic_iterator & operator=(basic_iterator &&) noexcept = default;
284 ~basic_iterator() = default;
285
298 constexpr basic_iterator(underlying_iterator_type iter,
299 underlying_iterator_type begin_it,
300 underlying_iterator_type end_it) noexcept :
301 first_it{iter}, second_it{std::ranges::next(iter, 1, end_it)}, begin_it{begin_it}, end_it{end_it}
302 {}
303
312 template <typename other_range_type>
313 requires(std::convertible_to<other_range_type, range_type &> &&
314 std::same_as<std::remove_const_t<other_range_type>, std::remove_const_t<range_type>>)
315 constexpr basic_iterator(basic_iterator<other_range_type> other) noexcept :
316 basic_iterator{std::move(other.first_it), std::move(other.begin_it), std::move(other.end_it)}
317 {}
319
324 constexpr reference operator*() const noexcept(noexcept(*std::declval<underlying_iterator_type>()))
325 {
326 return reference{*first_it, *second_it};
327 }
328
332 constexpr reference operator[](size_t const index) const
333 noexcept(noexcept(std::declval<basic_iterator &>().from_index(1)))
334 requires std::random_access_iterator<underlying_iterator_type>
335 {
336 return *(*this + index);
337 }
339
344 constexpr basic_iterator & operator++(/*pre-increment*/) noexcept(
345 noexcept(++std::declval<underlying_iterator_type &>()))
346 {
347 if (++second_it == end_it)
348 {
349 ++first_it;
350 second_it = first_it;
351 ++second_it;
352 }
353 return *this;
354 }
355
357 constexpr basic_iterator operator++(int /*post-increment*/) noexcept(
358 noexcept(std::declval<underlying_iterator_type &>()++))
359 {
360 basic_iterator tmp{*this};
361 ++*this;
362 return tmp;
363 }
364
366 constexpr basic_iterator & operator--(/*pre-decrement*/) noexcept(
367 noexcept(--std::declval<underlying_iterator_type &>()))
368 requires std::bidirectional_iterator<underlying_iterator_type>
369 {
370 if (--second_it == first_it)
371 {
372 --first_it;
373 second_it = end_it;
374 --second_it;
375 }
376 return *this;
377 }
378
380 constexpr basic_iterator operator--(int /*post-decrement*/) noexcept(
381 noexcept(std::declval<underlying_iterator_type &>()--))
382 requires std::bidirectional_iterator<underlying_iterator_type>
383 {
384 basic_iterator tmp{*this};
385 --*this;
386 return tmp;
387 }
388
391 constexpr basic_iterator & operator+=(difference_type const offset) noexcept(
392 noexcept(std::declval<basic_iterator &>().from_index(1)))
393 requires std::random_access_iterator<underlying_iterator_type>
394 {
395 from_index(to_index() + offset);
396 return *this;
397 }
398
401 constexpr basic_iterator operator+(difference_type const offset) const
402 noexcept(noexcept(std::declval<basic_iterator &>() += 1))
403 requires std::random_access_iterator<underlying_iterator_type>
404 {
405 basic_iterator tmp{*this};
406 return (tmp += offset);
407 }
408
411 constexpr friend basic_iterator operator+(difference_type const offset, basic_iterator iter) noexcept(
412 noexcept(std::declval<basic_iterator<range_type> &>().from_index(1)))
413 requires std::random_access_iterator<underlying_iterator_type>
414 {
415 iter.from_index(iter.to_index() + offset);
416 return iter;
417 }
418
421 constexpr basic_iterator & operator-=(difference_type const offset) noexcept(
422 noexcept(std::declval<basic_iterator &>().from_index(1)))
423 requires std::random_access_iterator<underlying_iterator_type>
424 {
425 from_index(to_index() - offset);
426 return *this;
427 }
428
431 constexpr basic_iterator operator-(difference_type const offset) const
432 noexcept(noexcept(std::declval<basic_iterator &>() -= 1))
433 requires std::random_access_iterator<underlying_iterator_type>
434 {
435 basic_iterator tmp{*this};
436 return (tmp -= offset);
437 }
438
441 template <typename other_range_type>
442 requires(std::random_access_iterator<underlying_iterator_type> &&
443 std::same_as<std::remove_const_t<range_type>, std::remove_const_t<other_range_type>>)
444 constexpr difference_type operator-(basic_iterator<other_range_type> const & rhs) const
445 noexcept(noexcept(std::declval<basic_iterator &>().to_index()))
446 {
447 return static_cast<difference_type>(to_index() - rhs.to_index());
448 }
450
457 constexpr friend bool operator==(basic_iterator const lhs, basic_iterator const rhs) noexcept(
458 noexcept(std::declval<underlying_iterator_type &>() == std::declval<underlying_iterator_type &>()))
459 {
460 return std::tie(lhs.first_it, lhs.second_it) == std::tie(rhs.first_it, rhs.second_it);
461 }
462
464 constexpr friend auto operator<=>(basic_iterator const lhs, basic_iterator const rhs) noexcept(
465 noexcept(std::declval<underlying_iterator_type &>() < std::declval<underlying_iterator_type &>()))
466 {
467 return std::tie(lhs.first_it, lhs.second_it) <=> std::tie(rhs.first_it, rhs.second_it);
468 }
470
471private:
484 constexpr size_t to_index() const
485 noexcept(noexcept(std::declval<underlying_iterator_type &>() - std::declval<underlying_iterator_type &>()))
486 requires std::random_access_iterator<underlying_iterator_type>
487 {
488 size_t src_size = end_it - begin_it;
489 size_t index_i = first_it - begin_it;
490 size_t index_j = second_it - begin_it;
491 return (src_size * (src_size - 1) / 2) - (src_size - index_i) * ((src_size - index_i) - 1) / 2 + index_j -
492 index_i - 1;
493 }
494
499 constexpr void from_index(size_t const index) noexcept(
500 noexcept(std::declval<underlying_iterator_type &>() -
501 std::declval<underlying_iterator_type &>()) && noexcept(std::declval<underlying_iterator_type &>() + 1))
502 requires std::random_access_iterator<underlying_iterator_type>
503 {
504 size_t src_size = end_it - begin_it;
505 size_t index_i =
506 src_size - 2 - static_cast<size_t>(std::sqrt(-8 * index + 4 * src_size * (src_size - 1) - 7) / 2.0 - 0.5);
507 size_t index_j =
508 index + index_i + 1 - src_size * (src_size - 1) / 2 + (src_size - index_i) * ((src_size - index_i) - 1) / 2;
509 first_it = begin_it + index_i;
510 second_it = begin_it + index_j;
511 }
512
514 underlying_iterator_type first_it{};
516 underlying_iterator_type second_it{};
518 underlying_iterator_type begin_it{};
520 underlying_iterator_type end_it{};
521};
522
523} // namespace bio::ranges::detail
524
525namespace bio::ranges::views
526{
591inline constexpr auto pairwise_combine = detail::adaptor_for_view_without_args<detail::pairwise_combine_view>{};
592
594} // namespace bio::ranges::views
T begin(T... args)
T end(T... args)
constexpr auto size
A type trait that holds the size of a (semi-)alphabet.
Definition: concept.hpp:517
constexpr auto pairwise_combine
A view adaptor that generates all pairwise combinations of the elements of the underlying range.
Definition: pairwise_combine.hpp:591
The BioC++ namespace for views.
Additional non-standard concepts for ranges.
Auxiliary header for the views submodule .
T sqrt(T... args)
T tie(T... args)
Provides bio::meta::transformation_trait_or.