SeqAn3 3.1.0
The Modern C++ library for sequence analysis.
gap_decorator.hpp
Go to the documentation of this file.
1// -----------------------------------------------------------------------------------------------------
2// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
3// Copyright (c) 2016-2021, 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
14#pragma once
15
16#include <seqan3/std/algorithm>
17#include <limits>
18#include <seqan3/std/ranges>
19#include <set>
20#include <tuple>
21#include <type_traits>
22
28
29namespace seqan3
30{
31
77template <std::ranges::viewable_range inner_type>
79 requires std::ranges::random_access_range<inner_type> && std::ranges::sized_range<inner_type> &&
80 (std::is_const_v<std::remove_reference_t<inner_type>> || std::ranges::view<inner_type>)
83{
84private:
85 // Declaration of class's iterator types; for the definition see below.
87
93
95 using ungapped_view_type = decltype(views::type_reduce(std::declval<inner_type &&>()));
96
97public:
106
112
119
124 using size_type = std::ranges::range_size_t<inner_type>;
125
130 using difference_type = std::ranges::range_difference_t<inner_type>;
132
137 using unaligned_sequence_type = inner_type;
138
149 gap_decorator() = default;
150 gap_decorator(gap_decorator const &) = default;
151 gap_decorator & operator=(gap_decorator const &) = default;
152 gap_decorator(gap_decorator && rhs) = default;
154 ~gap_decorator() = default;
155
160 template <typename other_range_t>
162 requires (!std::same_as<other_range_t, gap_decorator>) &&
164 std::ranges::viewable_range<other_range_t> // at end, otherwise it competes with the move ctor
166 gap_decorator(other_range_t && range) : ungapped_view{views::type_reduce(std::forward<inner_type>(range))}
167 {} // TODO (@smehringer) only works for copyable views. Has to be changed once views are not required to be copyable anymore.
168 // !\}
169
184 {
185 if (anchors.size())
186 return anchors.rbegin()->second + ungapped_view.size();
187
188 return ungapped_view.size();
189 }
190
207 {
208 if (!count) // [[unlikely]]
209 return it;
210
211 size_type const pos = it - begin();
212 assert(pos <= size());
213
214 set_iterator_type it_set = anchors.upper_bound(anchor_gap_t{pos, bound_dummy});
215
216 if (it_set == anchors.begin()) // will also catch if anchors is empty since begin() == end()
217 {
218 anchors.emplace_hint(anchors.begin(), anchor_gap_t{pos, count});
219 }
220 else // there are gaps before pos
221 {
222 --it_set;
223 auto gap_len{it_set->second};
224 if (it_set != anchors.begin())
225 gap_len -= (*(std::prev(it_set))).second;
226
227 if (it_set->first + gap_len >= pos) // extend existing gap
228 {
229 anchor_gap_t gap{it_set->first, it_set->second + count};
230 it_set = anchors.erase(it_set);
231 anchors.insert(it_set, gap);
232 }
233 else // insert new gap
234 {
235 anchor_gap_t gap{pos, it_set->second + count};
236 ++it_set;
237 anchors.insert(it_set, gap);
238 }
239 }
240
241 // post-processing: reverse update of succeeding gaps
242 rupdate(pos, count);
243 return iterator{*this, pos};
244 }
245
260 {
261 // check if [it, it+gap_len[ covers [first, last[
262 if ((*it) != gap{}) // [[unlikely]]
263 throw gap_erase_failure("The range to be erased does not correspond to a consecutive gap.");
264
265 return erase_gap(it, std::next(it));
266 }
267
284 {
285 size_type const pos1 = first - begin();
286 size_type const pos2 = last - begin();
287 set_iterator_type it = anchors.upper_bound(anchor_gap_t{pos1, bound_dummy}); // first element greater than pos1
288
289 if (it == anchors.begin())
290 throw gap_erase_failure{"There is no gap to erase in range [" + std::to_string(pos1) + "," +
291 std::to_string(pos2) + "]."};
292
293 --it;
294 size_type const gap_len = gap_length(it);
295
296 // check if [it, it+gap_len[ covers [first, last[
297 if ((it->first + gap_len) < pos2) // [[unlikely]]
298 {
299 throw gap_erase_failure{"The range to be erased does not correspond to a consecutive gap."};
300 }
301 // case 1: complete gap is deleted
302 else if (gap_len == pos2 - pos1)
303 {
304 it = anchors.erase(it);
305 }
306 // case 2: gap to be deleted in tail or larger than 1 (equiv. to shift tail left, i.e. pos remains unchanged)
307 else
308 {
309 anchor_gap_t gap{it->first, it->second - pos2 + pos1};
310 it = anchors.erase(it);
311 it = anchors.insert(it, gap); // amortized constant because of hint
312 ++it; // update node after the current
313 }
314
315 // post-processing: forward update of succeeding gaps
316 update(it, pos2 - pos1);
317
318 return iterator{*this, pos1};
319 }
320
328 template <typename unaligned_sequence_t> // generic template to use forwarding reference
330 requires std::assignable_from<gap_decorator &, unaligned_sequence_t>
332 friend void assign_unaligned(gap_decorator & dec, unaligned_sequence_t && unaligned)
333 {
334 dec = unaligned;
335 }
337
356 const_iterator begin() const noexcept
357 {
358 return iterator{*this};
359 }
360
362 const_iterator cbegin() const noexcept
363 {
364 return const_iterator{*this};
365 }
366
382 const_iterator end() const noexcept
383 {
384 return iterator{*this, size()};
385 }
386
388 const_iterator cend() const noexcept
389 {
390 return const_iterator{*this, size()};
391 }
393
412 {
413 if (i >= size()) // [[unlikely]]
414 throw std::out_of_range{"Trying to access element behind the last in gap_decorator."};
415 return (*this)[i];
416 }
417
420 {
421 if (i >= size()) // [[unlikely]]
422 throw std::out_of_range{"Trying to access element behind the last in gap_decorator."};
423 return (*this)[i];
424 }
425
439 {
440 return *iterator{*this, i};
441 }
443
467 friend bool operator==(gap_decorator const & lhs, gap_decorator const & rhs)
468 {
469 if (lhs.size() == rhs.size() &&
470 lhs.anchors == rhs.anchors &&
471 std::ranges::equal(lhs.ungapped_view, rhs.ungapped_view))
472 {
473 return true;
474 }
475
476 return false;
477 }
478
483 friend bool operator!=(gap_decorator const & lhs, gap_decorator const & rhs)
484 {
485 return !(lhs == rhs);
486 }
487
492 friend bool operator<(gap_decorator const & lhs, gap_decorator const & rhs)
493 {
494 auto lit = lhs.begin();
495 auto rit = rhs.begin();
496
497 while (lit != lhs.end() && rit != rhs.end() && *lit == *rit)
498 ++lit, ++rit;
499
500 if (rit == rhs.end())
501 return false; // lhs == rhs, or rhs prefix of lhs
502 else if (lit == lhs.end())
503 return true; // lhs prefix of rhs
504
505 return *lit < *rit;
506 }
507
512 friend bool operator<=(gap_decorator const & lhs, gap_decorator const & rhs)
513 {
514 auto lit = lhs.begin();
515 auto rit = rhs.begin();
516
517 while (lit != lhs.end() && rit != rhs.end() && *lit == *rit)
518 ++lit, ++rit;
519
520 if (lit == lhs.end())
521 return true; // lhs == rhs, or lhs prefix of rhs
522 else if (rit == rhs.end())
523 return false; // rhs prefix of lhs
524
525 return *lit < *rit;
526 }
527
532 friend bool operator>(gap_decorator const & lhs, gap_decorator const & rhs)
533 {
534 return !(lhs <= rhs);
535 }
536
541 friend bool operator>=(gap_decorator const & lhs, gap_decorator const & rhs)
542 {
543 return !(lhs < rhs);
544 }
546
547private:
549 using anchor_gap_t = typename std::pair<size_t, size_t>;
550
552 using anchor_set_type = std::set<anchor_gap_t>;
553
555 using set_iterator_type = typename anchor_set_type::iterator;
556
558 constexpr static size_t bound_dummy{std::numeric_limits<size_t>::max()};
559
572 size_type gap_length(set_iterator_type it) const
573 {
574 return (it == anchors.begin()) ? it->second : it->second - (*std::prev(it)).second;
575 }
576
589 void rupdate(size_type const pos, size_type const offset)
590 {
591 for (auto it = std::prev(anchors.end(), 1); it->first > pos;)
592 {
593 anchors.emplace_hint(it, anchor_gap_t{it->first + offset, it->second + offset});
594 anchors.erase(*it--);
595 }
596 }
597
610 void update(set_iterator_type it, size_type const offset)
611 {
612 while (it != anchors.end())
613 {
614 anchor_gap_t gap{it->first - offset, it->second - offset};
615 it = anchors.erase(it);
616 it = anchors.insert(it, gap);
617 ++it;
618 }
619 }
620
622 ungapped_view_type ungapped_view{};
623
625 anchor_set_type anchors{};
626};
627
635template <std::ranges::viewable_range urng_t>
637 requires (!std::ranges::view<std::remove_reference_t<urng_t>>)
640
645template <std::ranges::view urng_t>
648
665template <std::ranges::viewable_range inner_type>
667 requires std::ranges::random_access_range<inner_type> && std::ranges::sized_range<inner_type> &&
668 (std::is_const_v<std::remove_reference_t<inner_type>> || std::ranges::view<inner_type>)
671{
672protected:
676 typename gap_decorator::size_type pos{0u};
678 int64_t ungapped_view_pos{0}; // must be signed because we need this value to be -1 in case of leading gaps.
681 typename gap_decorator::size_type left_gap_end{0};
684 typename gap_decorator::set_iterator_type anchor_set_it{};
686 bool is_at_gap{true};
687
689 void jump(typename gap_decorator::size_type const new_pos)
690 {
691 assert(new_pos <= host->size());
692 pos = new_pos;
693
694 anchor_set_it = host->anchors.upper_bound(anchor_gap_t{pos, host->bound_dummy});
695 ungapped_view_pos = pos;
696
697 if (anchor_set_it != host->anchors.begin())
698 {
699 typename gap_decorator::set_iterator_type prev{std::prev(anchor_set_it)};
700 size_type gap_len{prev->second};
701
702 if (prev != host->anchors.begin())
703 gap_len -= std::prev(prev)->second;
704
705 ungapped_view_pos -= prev->second;
706 left_gap_end = prev->first + gap_len;
707 }
708
709 if (ungapped_view_pos != static_cast<int64_t>(host->ungapped_view.size()) &&
710 pos >= left_gap_end && (anchor_set_it == host->anchors.end() || pos < anchor_set_it->first))
711 is_at_gap = false;
712 else
713 is_at_gap = true;
714 }
715
716public:
731
741
743 explicit gap_decorator_iterator(gap_decorator const & host_) :
744 host(&host_), anchor_set_it{host_.anchors.begin()}
745 {
746 if (host_.anchors.size() && (*host_.anchors.begin()).first == 0) // there are gaps at the very front
747 {
748 --ungapped_view_pos; // set ungapped_view_pos to -1 so operator++ works without an extra if-branch.
749 left_gap_end = anchor_set_it->second;
750 ++anchor_set_it;
751 }
752 else
753 {
754 is_at_gap = false;
755 }
756 }
757
759 gap_decorator_iterator(gap_decorator const & host_, typename gap_decorator::size_type const pos_) : host(&host_)
760 {
761 jump(pos_); // random access to pos
762 }
764
770 {
771 assert(host); // host is set
772 ++pos;
773
774 if (pos < left_gap_end) // we stay within the preceding gap stretch
775 return *this;
776
777 if (anchor_set_it == host->anchors.end() || pos < anchor_set_it->first)
778 { // proceed within the view since we are right of the previous gap but didn't arrive at the right gap yet
779 ++ungapped_view_pos;
780 if (ungapped_view_pos != static_cast<int64_t>(host->ungapped_view.size()))
781 is_at_gap = false;
782 }
783 else
784 { // we arrived at the right gap and have to update the variables. ungapped_view_pos remains unchanged.
785 left_gap_end = anchor_set_it->first + anchor_set_it->second -
786 ((anchor_set_it != host->anchors.begin()) ? (std::prev(anchor_set_it))->second : 0);
787 ++anchor_set_it;
788 is_at_gap = true;
789
790 if (left_gap_end == host->size()) // very last gap
791 ++ungapped_view_pos;
792 }
793
794 return *this;
795 }
796
799 {
800 gap_decorator_iterator cpy{*this};
801 ++(*this);
802 return cpy;
803 }
804
807 {
808 this->jump(this->pos + skip);
809 return *this;
810 }
811
814 {
815 return gap_decorator_iterator{*(this->host), this->pos + skip};
816 }
817
820 {
821 return it + skip;
822 }
823
826 {
827 assert(host); // host is set
828 --pos;
829
830 if (pos < left_gap_end)
831 { // there was no gap before but we arrive at the left gap and have to update the variables.
832 (anchor_set_it != host->anchors.begin()) ? --anchor_set_it : anchor_set_it;
833
834 if (anchor_set_it != host->anchors.begin())
835 {
836 auto prev = std::prev(anchor_set_it);
837 left_gap_end = prev->first + prev->second -
838 ((prev != host->anchors.begin()) ? std::prev(prev)->second : 0);
839 }
840 else // [[unlikely]]
841 {
842 left_gap_end = 0;
843 }
844 is_at_gap = true;
845 }
846 else if (anchor_set_it == host->anchors.end() || pos < anchor_set_it->first)
847 { // we are neither at the left nor right gap
848 --ungapped_view_pos;
849 is_at_gap = false;
850 }
851 // else -> no op (we are still within the right gap stretch)
852
853 return *this;
854 }
855
858 {
859 gap_decorator_iterator cpy{*this};
860 --(*this);
861 return cpy;
862 }
863
866 {
867 this->jump(this->pos - skip);
868 return *this;
869 }
870
873 {
874 return gap_decorator_iterator{*(this->host), this->pos - skip};
875 }
876
879 {
880 return it - skip;
881 }
882
885 {
886 return static_cast<difference_type>(this->pos - lhs.pos);
887 }
889
895 {
896 return (is_at_gap) ? reference{gap{}} : reference{host->ungapped_view[ungapped_view_pos]};
897 }
898
901 {
902 return *(*this + n);
903 }
905
912 friend bool operator==(gap_decorator_iterator const & lhs, gap_decorator_iterator const & rhs) noexcept
913 {
914 return lhs.pos == rhs.pos;
915 }
916
918 friend bool operator!=(gap_decorator_iterator const & lhs, gap_decorator_iterator const & rhs) noexcept
919 {
920 return lhs.pos != rhs.pos;
921 }
922
924 friend bool operator<(gap_decorator_iterator const & lhs, gap_decorator_iterator const & rhs) noexcept
925 {
926 return lhs.pos < rhs.pos;
927 }
928
930 friend bool operator>(gap_decorator_iterator const & lhs, gap_decorator_iterator const & rhs) noexcept
931 {
932 return lhs.pos > rhs.pos;
933 }
934
936 friend bool operator<=(gap_decorator_iterator const & lhs, gap_decorator_iterator const & rhs) noexcept
937 {
938 return lhs.pos <= rhs.pos;
939 }
940
942 friend bool operator>=(gap_decorator_iterator const & lhs, gap_decorator_iterator const & rhs) noexcept
943 {
944 return lhs.pos >= rhs.pos;
945 }
947};
948
949} // namespace seqan
The <algorithm> header from C++20's standard library.
Includes customized exception types for the alignment module .
Core alphabet concept and free function/type trait wrappers.
T begin(T... args)
A combined alphabet that can hold values of either of its alternatives..
Definition: alphabet_variant.hpp:129
The iterator type over a seqan3::gap_decorator.
Definition: gap_decorator.hpp:671
gap_decorator_iterator & operator+=(difference_type const skip)
Advances iterator by skip many positions.
Definition: gap_decorator.hpp:806
friend bool operator<=(gap_decorator_iterator const &lhs, gap_decorator_iterator const &rhs) noexcept
Checks whether *this is less than or equal to rhs.
Definition: gap_decorator.hpp:936
reference operator*() const
Dereference operator returns a copy of the element currently pointed at.
Definition: gap_decorator.hpp:894
typename gap_decorator::difference_type difference_type
The difference type.
Definition: gap_decorator.hpp:721
friend gap_decorator_iterator operator+(difference_type const skip, gap_decorator_iterator const &it)
Returns an iterator copy advanced by skip many positions.
Definition: gap_decorator.hpp:819
gap_decorator_iterator operator--(int)
Returns a decremented iterator copy.
Definition: gap_decorator.hpp:857
gap_decorator_iterator operator+(difference_type const skip) const
Returns an iterator copy advanced by skip many positions.
Definition: gap_decorator.hpp:813
gap_decorator_iterator & operator=(gap_decorator_iterator const &)=default
Defaulted.
difference_type operator-(gap_decorator_iterator const lhs) const noexcept
Returns the distance between two iterators.
Definition: gap_decorator.hpp:884
gap_decorator_iterator operator++(int)
Returns an incremented iterator copy.
Definition: gap_decorator.hpp:798
gap_decorator_iterator & operator--()
Decrements iterator.
Definition: gap_decorator.hpp:825
gap_decorator_iterator & operator=(gap_decorator_iterator &&)=default
Defaulted.
typename gap_decorator::const_reference reference
The reference type.
Definition: gap_decorator.hpp:725
gap_decorator_iterator operator-(difference_type const skip) const
Returns an iterator copy advanced by skip many positions.
Definition: gap_decorator.hpp:872
friend bool operator<(gap_decorator_iterator const &lhs, gap_decorator_iterator const &rhs) noexcept
Checks whether *this is less than rhs.
Definition: gap_decorator.hpp:924
gap_decorator_iterator & operator++()
Increments iterator.
Definition: gap_decorator.hpp:769
gap_decorator_iterator(gap_decorator const &host_)
Construct from seqan3::gap_decorator and initialising to first position.
Definition: gap_decorator.hpp:743
gap_decorator_iterator(gap_decorator_iterator &&)=default
Defaulted.
friend gap_decorator_iterator operator-(difference_type const skip, gap_decorator_iterator const &it)
Returns an iterator copy advanced by skip many positions.
Definition: gap_decorator.hpp:878
friend bool operator!=(gap_decorator_iterator const &lhs, gap_decorator_iterator const &rhs) noexcept
Checks whether *this is not equal to rhs.
Definition: gap_decorator.hpp:918
gap_decorator_iterator(gap_decorator const &host_, typename gap_decorator::size_type const pos_)
Construct from seqan3::gap_decorator and explicit position.
Definition: gap_decorator.hpp:759
friend bool operator>=(gap_decorator_iterator const &lhs, gap_decorator_iterator const &rhs) noexcept
Checks whether *this is greater than or equal to rhs.
Definition: gap_decorator.hpp:942
friend bool operator>(gap_decorator_iterator const &lhs, gap_decorator_iterator const &rhs) noexcept
Checks whether *this is greater than rhs.
Definition: gap_decorator.hpp:930
gap_decorator_iterator(gap_decorator_iterator const &)=default
Defaulted.
friend bool operator==(gap_decorator_iterator const &lhs, gap_decorator_iterator const &rhs) noexcept
Checks whether *this is equal to rhs.
Definition: gap_decorator.hpp:912
value_type * pointer
The pointer type.
Definition: gap_decorator.hpp:727
gap_decorator_iterator & operator-=(difference_type const skip)
Advances iterator by skip many positions.
Definition: gap_decorator.hpp:865
typename gap_decorator::value_type value_type
The value type.
Definition: gap_decorator.hpp:723
void jump(typename gap_decorator::size_type const new_pos)
A helper function that performs the random access into the anchor set, updating all member variables.
Definition: gap_decorator.hpp:689
reference operator[](difference_type const n) const
Return underlying container value currently pointed at.
Definition: gap_decorator.hpp:900
A gap decorator allows the annotation of sequences with gap symbols while leaving the underlying sequ...
Definition: gap_decorator.hpp:83
reference at(size_type const i)
Return the i-th element as a reference.
Definition: gap_decorator.hpp:411
friend bool operator==(gap_decorator const &lhs, gap_decorator const &rhs)
Checks whether lhs is equal to rhs.
Definition: gap_decorator.hpp:467
gap_decorator & operator=(gap_decorator const &)=default
Defaulted.
gap_decorator(gap_decorator &&rhs)=default
Defaulted.
const_reference at(size_type const i) const
Return the i-th element as a reference.
Definition: gap_decorator.hpp:419
std::ranges::range_difference_t< inner_type > difference_type
The difference type of the underlying sequence.
Definition: gap_decorator.hpp:130
const_iterator cend() const noexcept
Returns an iterator pointing behind the last element of the decorator.
Definition: gap_decorator.hpp:388
const_iterator end() const noexcept
Returns an iterator pointing behind the last element of the decorator.
Definition: gap_decorator.hpp:382
inner_type unaligned_sequence_type
The underlying ungapped range type.
Definition: gap_decorator.hpp:137
gap_decorator(other_range_t &&range)
Construct with the ungapped range type.
Definition: gap_decorator.hpp:166
reference operator[](size_type const i) const
Return the i-th element as a reference.
Definition: gap_decorator.hpp:438
friend bool operator!=(gap_decorator const &lhs, gap_decorator const &rhs)
Checks whether lhs is not equal to rhs.
Definition: gap_decorator.hpp:483
iterator erase_gap(const_iterator const it)
Erase one gap symbol at the indicated iterator postion.
Definition: gap_decorator.hpp:259
const_iterator begin() const noexcept
Returns an iterator to the first element of the container.
Definition: gap_decorator.hpp:356
iterator insert_gap(const_iterator const it, size_type const count=1)
Insert a gap of length count at the aligned sequence iterator position.
Definition: gap_decorator.hpp:206
~gap_decorator()=default
Defaulted.
iterator erase_gap(const_iterator const first, const_iterator const last)
Erase gap symbols at the iterator postions [first, last[.
Definition: gap_decorator.hpp:283
gap_decorator(gap_decorator const &)=default
Defaulted.
friend bool operator<=(gap_decorator const &lhs, gap_decorator const &rhs)
Checks whether lhs is less than or equal to rhs.
Definition: gap_decorator.hpp:512
size_type size() const
Returns the total length of the aligned sequence.
Definition: gap_decorator.hpp:183
reference const_reference
const_reference type equals reference type equals value type because the underlying sequence must not...
Definition: gap_decorator.hpp:118
gap_decorator & operator=(gap_decorator &&rhs)=default
Defaulted.
gapped< std::ranges::range_value_t< inner_type > > value_type
The variant type of the alphabet type and gap symbol type (see seqan3::gapped).
Definition: gap_decorator.hpp:105
friend bool operator>(gap_decorator const &lhs, gap_decorator const &rhs)
Checks whether lhs is greater than rhs.
Definition: gap_decorator.hpp:532
friend bool operator>=(gap_decorator const &lhs, gap_decorator const &rhs)
Checks whether lhs is greater than or equal to rhs.
Definition: gap_decorator.hpp:541
gap_decorator()=default
Default constructor.
friend bool operator<(gap_decorator const &lhs, gap_decorator const &rhs)
Checks whether lhs is less than rhs.
Definition: gap_decorator.hpp:492
std::ranges::range_size_t< inner_type > size_type
The size_type of the underlying sequence.
Definition: gap_decorator.hpp:124
friend void assign_unaligned(gap_decorator &dec, unaligned_sequence_t &&unaligned)
Assigns a new sequence of type seqan3::gap_decorator::unaligned_sequence_type to the decorator.
Definition: gap_decorator.hpp:332
const_iterator cbegin() const noexcept
Returns an iterator to the first element of the container.
Definition: gap_decorator.hpp:362
Thrown in function seqan3::erase_gap, if a position does not contain a gap.
Definition: exception.hpp:24
The alphabet of a gap character '-'.
Definition: gap.hpp:39
Provides seqan3::gap.
Provides seqan3::gapped.
@ offset
Sequence (seqan3::field::seq) relative start position (0-based), unsigned value.
constexpr ptrdiff_t count
Count the occurrences of a type in a pack.
Definition: traits.hpp:169
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:151
constexpr auto type_reduce
A view adaptor that behaves like std::views::all, but type erases certain ranges.
Definition: type_reduce.hpp:153
T max(T... args)
The main SeqAn3 namespace.
Definition: cigar_operation_table.hpp:2
gap_decorator(urng_t &&range) -> gap_decorator< std::remove_reference_t< urng_t > const & >
Ranges (not views!) always deduce to const & range_type since they are access-only anyway.
T next(T... args)
T prev(T... args)
The <ranges> header from C++20's standard library.
T size(T... args)
T to_string(T... args)
Provides seqan3::views::type_reduce.