SeqAn3 3.2.0-rc.1
The Modern C++ library for sequence analysis.
chunk.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
13#pragma once
14
15#include <seqan3/std/ranges>
16
24
25namespace seqan3::detail
26{
27// ---------------------------------------------------------------------------------------------------------------------
28// chunk_view class
29// ---------------------------------------------------------------------------------------------------------------------
30
39template <std::ranges::input_range urng_t>
40 requires std::ranges::view<urng_t>
41class chunk_view : public std::ranges::view_interface<chunk_view<urng_t>>
42{
43private:
45 urng_t urange;
46
48 uint16_t chunk_size;
49
50 // The iterator type if `urng_t` is a pure input range. See class definition for details.
51 template <bool const_range>
52 class basic_input_iterator;
53
54 // The iterator type if `urng_t` is at least a forward range. See class definition for details.
55 template <bool const_range>
56 class basic_iterator;
57
58public:
62 chunk_view()
63 requires std::default_initializable<urng_t>
64 = default;
65 chunk_view(chunk_view const & rhs) = default;
66 chunk_view(chunk_view && rhs) = default;
67 chunk_view & operator=(chunk_view const & rhs) = default;
68 chunk_view & operator=(chunk_view && rhs) = default;
69 ~chunk_view() = default;
70
75 constexpr explicit chunk_view(urng_t underlying_range, uint16_t const size_of_chunk) :
76 urange{std::move(underlying_range)},
77 chunk_size{size_of_chunk}
78 {}
80
97 auto begin() noexcept
98 {
99 if constexpr (std::ranges::forward_range<urng_t>)
100 return basic_iterator<false>{std::ranges::begin(urange), std::ranges::end(urange), chunk_size};
101 else
102 return basic_input_iterator<false>{std::ranges::begin(urange), std::ranges::end(urange), chunk_size};
103 }
104
106 auto begin() const noexcept
107 requires const_iterable_range<urng_t>
108 {
109 if constexpr (std::ranges::forward_range<urng_t>)
110 return basic_iterator<true>{std::ranges::cbegin(urange), std::ranges::cend(urange), chunk_size};
111 else
112 return basic_input_iterator<true>{std::ranges::cbegin(urange), std::ranges::cend(urange), chunk_size};
113 }
114
130 auto end() noexcept
131 {
132 return std::ranges::end(urange);
133 }
134
136 auto end() const noexcept
137 requires const_iterable_range<urng_t>
138 {
139 return std::ranges::cend(urange);
140 }
142
146 auto size()
147 requires std::ranges::sized_range<urng_t>
148 {
149 using size_type = std::ranges::range_size_t<urng_t>;
150 return static_cast<size_type>((std::ranges::size(urange) + chunk_size - 1) / chunk_size); // round up
151 }
152
154 auto size() const
155 requires std::ranges::sized_range<urng_t const>
156 {
157 using size_type = std::ranges::range_size_t<urng_t const>;
158 return static_cast<size_type>((std::ranges::size(urange) + chunk_size - 1) / chunk_size); // round up
159 }
160};
161
163template <std::ranges::range rng_t>
164chunk_view(rng_t &&, uint16_t const &) -> chunk_view<seqan3::detail::all_t<rng_t>>;
165
166// ---------------------------------------------------------------------------------------------------------------------
167// chunk_view iterators (basic_input_iterator and basic_iterator)
168// ---------------------------------------------------------------------------------------------------------------------
169
187template <std::ranges::input_range urng_t>
188 requires std::ranges::view<urng_t>
189template <bool const_range>
190class chunk_view<urng_t>::basic_input_iterator :
191 public maybe_iterator_category<maybe_const_iterator_t<const_range, urng_t>>
192{
193private:
195 using urng_it_t = maybe_const_iterator_t<const_range, urng_t>;
196
198 using sentinel_t = maybe_const_sentinel_t<const_range, urng_t>;
199
212 template <typename outer_it_type>
213 class input_helper_iterator : public urng_it_t
214 {
215 public:
219 constexpr input_helper_iterator() = default;
220 constexpr input_helper_iterator(input_helper_iterator const &) = default;
221 constexpr input_helper_iterator(input_helper_iterator &&) = default;
222 constexpr input_helper_iterator & operator=(input_helper_iterator const &) = default;
223 constexpr input_helper_iterator & operator=(input_helper_iterator &&) = default;
224 ~input_helper_iterator() = default;
225
227 constexpr explicit input_helper_iterator(outer_it_type & outer_iterator, urng_it_t urng_it) :
228 urng_it_t(std::move(urng_it)),
229 outer_it(std::addressof(outer_iterator))
230 {}
231
233 constexpr explicit input_helper_iterator(urng_it_t urng_it) : urng_it_t(std::move(urng_it))
234 {}
236
238 input_helper_iterator & operator++() noexcept
239 {
240 --(outer_it->remaining);
241 urng_it_t::operator++();
242 return *this;
243 }
244
246 input_helper_iterator operator++(int) noexcept
247 {
248 input_helper_iterator tmp{*this};
249 ++(*this);
250 return tmp;
251 }
252
254 bool operator==(sentinel_t const & /* rhs */) noexcept
255 {
256 return this->outer_it->remaining == 0u || this->outer_it->urng_begin == this->outer_it->urng_end;
257 }
258
260 outer_it_type * outer_it{nullptr};
261 };
262
264 using helper_it_t = input_helper_iterator<basic_input_iterator>;
265
266 // befriend the iterator on a const range
267 template <bool other_const_range>
268 friend class basic_input_iterator;
269
270 // befriend the inner iterator type
271 template <typename outer_it_type>
272 friend class input_helper_iterator;
273
274public:
279 using difference_type = typename std::iter_difference_t<urng_it_t>;
281 using value_type = std::ranges::subrange<helper_it_t, sentinel_t>;
283 using pointer = void;
285 using reference = value_type;
287 using iterator_concept = std::input_iterator_tag;
289
293 constexpr basic_input_iterator() = default;
294 constexpr basic_input_iterator(basic_input_iterator const &) = default;
295 constexpr basic_input_iterator(basic_input_iterator &&) = default;
296 constexpr basic_input_iterator & operator=(basic_input_iterator const &) = default;
297 constexpr basic_input_iterator & operator=(basic_input_iterator &&) = default;
298 ~basic_input_iterator() = default;
299
301 constexpr explicit basic_input_iterator(basic_input_iterator<!const_range> it) noexcept
302 requires const_range
303 :
304 chunk_size{std::move(it.chunk_size)},
305 remaining{std::move(it.remaining)},
306 urng_begin{std::move(it.urng_begin)},
307 urng_end{std::move(it.urng_end)},
308 current_chunk{std::move(it.current_chunk)}
309 {}
310
322 constexpr explicit basic_input_iterator(urng_it_t it_begin, sentinel_t it_end, uint16_t const size_of_chunk) :
323 chunk_size{size_of_chunk},
324 remaining{size_of_chunk},
325 urng_begin{std::move(it_begin)},
326 urng_end{std::move(it_end)}
327 {
328 current_chunk = std::ranges::subrange<helper_it_t, sentinel_t>{helper_it_t{*this, it_begin}, it_end};
329 }
331
335
337 friend constexpr bool operator==(basic_input_iterator const & lhs, sentinel_t const & rhs) noexcept
338 {
339 return lhs.urng_begin == rhs;
340 }
341
343 friend constexpr bool operator==(basic_input_iterator const & lhs, basic_input_iterator const & rhs) noexcept
344 {
345 return (lhs.urng_begin == rhs.urng_begin) && (lhs.remaining == rhs.remaining);
346 }
347
349 constexpr basic_input_iterator & operator++() noexcept
350 {
351 while (remaining > 0u && urng_begin != urng_end) // if chunk was not fully consumed and range is not at end
352 {
353 ++urng_begin;
354 --remaining;
355 }
356 current_chunk = std::ranges::subrange<helper_it_t, sentinel_t>{helper_it_t{*this, urng_begin}, urng_end};
357 remaining = chunk_size;
358 return *this;
359 }
360
362 constexpr basic_input_iterator operator++(int) noexcept
363 {
364 basic_input_iterator tmp{*this};
365 ++(*this);
366 return tmp;
367 }
368
370 constexpr value_type operator*() const noexcept
371 {
372 return current_chunk;
373 }
374
375private:
377 uint16_t chunk_size;
378
380 uint16_t remaining;
381
383 urng_it_t urng_begin;
384
386 sentinel_t urng_end;
387
389 value_type current_chunk;
390};
391
408template <std::ranges::input_range urng_t>
409 requires std::ranges::view<urng_t>
410template <bool const_range>
411class chunk_view<urng_t>::basic_iterator : public maybe_iterator_category<maybe_const_iterator_t<const_range, urng_t>>
412{
413private:
415 using it_t = maybe_const_iterator_t<const_range, urng_t>;
417 using sentinel_t = maybe_const_sentinel_t<const_range, urng_t>;
418
419 // befriend the iterator on a const range
420 template <bool other_const_range>
421 friend class basic_iterator;
422
423public:
428 using difference_type = typename std::iter_difference_t<it_t>;
430 using value_type = std::ranges::subrange<it_t, it_t>;
432 using pointer = void;
434 using reference = value_type;
438 detail::iterator_concept_tag_t<it_t>>;
440
444 constexpr basic_iterator() = default;
445 constexpr basic_iterator(basic_iterator const &) = default;
446 constexpr basic_iterator(basic_iterator &&) = default;
447 constexpr basic_iterator & operator=(basic_iterator const &) = default;
448 constexpr basic_iterator & operator=(basic_iterator &&) = default;
449 ~basic_iterator() = default;
450
452 constexpr basic_iterator(basic_iterator<!const_range> const & it) noexcept
453 requires const_range
454 :
455 chunk_size{std::move(it.chunk_size)},
456 urng_begin{std::move(it.urng_begin)},
457 urng_end{std::move(it.urng_end)},
458 current_chunk{std::move(it.current_chunk)}
459 {}
460
472 constexpr explicit basic_iterator(it_t it_start, sentinel_t it_end, uint16_t const size_of_chunk) :
473 chunk_size{size_of_chunk},
474 urng_begin{std::move(it_start)},
475 urng_end{std::move(it_end)}
476 {
477 current_chunk = value_type{it_start, get_next_end_of_chunk(it_start)};
478 }
480
484
486 friend constexpr bool operator==(basic_iterator const & lhs, sentinel_t const & rhs) noexcept
487 {
488 return lhs.current_chunk.begin() == rhs;
489 }
490
492 friend constexpr bool operator==(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
493 {
494 return (lhs.current_chunk.begin() == rhs.current_chunk.begin()) && (lhs.chunk_size == rhs.chunk_size);
495 }
496
498 friend constexpr bool operator!=(basic_iterator const & lhs, sentinel_t const & rhs) noexcept
499 {
500 return !(lhs == rhs);
501 }
502
504 friend constexpr bool operator!=(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
505 {
506 return !(lhs == rhs);
507 }
508
510 friend constexpr bool operator<(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
511 {
512 return lhs.current_chunk.begin() < rhs.current_chunk.begin();
513 }
514
516 friend constexpr bool operator>(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
517 {
518 return lhs.current_chunk.begin() > rhs.current_chunk.begin();
519 }
520
522 friend constexpr bool operator<=(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
523 {
524 return lhs.current_chunk.begin() <= rhs.current_chunk.begin();
525 }
526
528 friend constexpr bool operator>=(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
529 {
530 return lhs.current_chunk.begin() >= rhs.current_chunk.begin();
531 }
532
534
536 constexpr basic_iterator & operator++() noexcept
537 {
538 current_chunk = value_type{current_chunk.end(), get_next_end_of_chunk(current_chunk.end())};
539 return *this;
540 }
541
543 basic_iterator operator++(int) noexcept
544 {
545 basic_iterator tmp{*this};
546 ++(*this);
547 return tmp;
548 }
549
553 constexpr basic_iterator & operator--() noexcept
554 requires std::bidirectional_iterator<it_t>
555 {
556 current_chunk = value_type{get_former_start_of_chunk(current_chunk.begin()), current_chunk.begin()};
557 return *this;
558 }
559
563 constexpr basic_iterator operator--(int) noexcept
564 requires std::bidirectional_iterator<it_t>
565 {
566 basic_iterator tmp{*this};
567 --(*this);
568 return tmp;
569 }
570
574 constexpr basic_iterator & operator+=(difference_type const skip) noexcept
575 requires std::random_access_iterator<it_t>
576 {
577 auto new_start_it = current_chunk.begin() + (chunk_size * skip);
578 current_chunk = value_type{new_start_it, get_next_end_of_chunk(new_start_it)};
579 return *this;
580 }
581
585 constexpr basic_iterator operator+(difference_type const skip) const noexcept
586 requires std::random_access_iterator<it_t>
587 {
588 basic_iterator tmp{*this};
589 return tmp += skip;
590 }
591
595 friend constexpr basic_iterator operator+(difference_type const skip, basic_iterator const & it) noexcept
596 requires std::random_access_iterator<it_t>
597 {
598 return it + skip;
599 }
600
604 constexpr basic_iterator & operator-=(difference_type const skip) noexcept
605 requires std::random_access_iterator<it_t>
606 {
607 auto new_start_it = current_chunk.begin() - (chunk_size * skip);
608 current_chunk = value_type{new_start_it, get_next_end_of_chunk(new_start_it)};
609 return *this;
610 }
611
616 constexpr basic_iterator operator-(difference_type const skip) const noexcept
617 requires std::random_access_iterator<it_t>
618 {
619 basic_iterator tmp{*this};
620 return tmp -= skip;
621 }
622
626 friend constexpr basic_iterator operator-(difference_type const skip, basic_iterator const & it) noexcept
627 requires std::random_access_iterator<it_t>
628 {
629 return it - skip;
630 }
631
636 friend constexpr difference_type operator-(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
637 requires std::sized_sentinel_for<it_t, it_t>
638 {
639 return static_cast<difference_type>((lhs.current_chunk.begin() - rhs.current_chunk.begin()) / lhs.chunk_size);
640 }
641
645 friend constexpr difference_type operator-(sentinel_t const & /* lhs */, basic_iterator const & rhs) noexcept
646 requires std::sized_sentinel_for<sentinel_t, it_t>
647 {
648 return static_cast<difference_type>((rhs.urng_end - rhs.current_chunk.begin() + rhs.chunk_size - 1)
649 / rhs.chunk_size);
650 }
651
655 friend constexpr difference_type operator-(basic_iterator const & lhs, sentinel_t const & rhs) noexcept
656 requires std::sized_sentinel_for<sentinel_t, it_t>
657 {
658 return -(rhs - lhs);
659 }
660
664 constexpr reference operator[](difference_type const n) const
665 requires std::random_access_iterator<it_t>
666 {
667 return *(*this + n);
668 }
669
671 constexpr value_type operator*() const noexcept
672 {
673 return current_chunk;
674 }
675
676private:
678 uint16_t chunk_size;
679
681 it_t urng_begin;
682
684 sentinel_t urng_end;
685
687 value_type current_chunk;
688
690 constexpr it_t get_next_end_of_chunk(it_t start_of_chunk) const
691 {
692 // Very similar to `return std::ranges::next(start_of_chunk, chunk_size, urng_end);`.
693 // However, the STL checks that the direction of chunk_size and urng_end are the same for sized_sentinels when
694 // -D_GLIBCXX_ASSERTIONS is enabled.
695 // If start_of_chunk was moved past urng_end, we should constrain it.
696 // =========X===========Y============
697 // ^ ^
698 // urng_end new_start_it
699 // <----------- // direction from iterator to bound
700 // ---------> // direction from chunk_size
701 // See https://eel.is/c++draft/range.iter.op.advance (next just takes and returns a copy of the iterator)
702 // Note: n is chunk_size and always positive.
703 if constexpr (std::sized_sentinel_for<sentinel_t, it_t>) // We can check whether we can jump.
704 {
705 if (chunk_size >= std::abs(urng_end - start_of_chunk)) // Remaining range smaller than chunk_size
706 return std::ranges::next(start_of_chunk, urng_end); // Returns it_t which is equal to urng_end
707 else // We can jump chunk_size many times
708 return std::ranges::next(start_of_chunk, chunk_size);
709 }
710 else // We need to increment one by one to not cross urng_end.
711 {
712 for (uint16_t increments{}; increments != chunk_size && start_of_chunk != urng_end; ++increments)
713 ++start_of_chunk;
714
715 return start_of_chunk;
716 }
717 }
718
720 constexpr it_t get_former_start_of_chunk(it_t end_of_chunk) const
721 {
722 // Very similar to `return std::ranges::prev(end_of_chunk, chunk_size, urng_begin);`.
723 // However, the STL checks that the direction of chunk_size and urng_end are the same for sized_sentinels when
724 // -D_GLIBCXX_ASSERTIONS is enabled.
725 // If end_of_chunk was moved before urng_begin, we should constrain it.
726 // =========X===========Y============
727 // ^ ^
728 // end_of_chunk urng_begin
729 // <--------- // direction from chunk_size
730 // ---------> // direction from iterator to bound
731 // See https://eel.is/c++draft/range.iter.op.advance (prev(i, n, bound) is equal to advance(i, -n, bound))
732 // Note: n is chunk_size and always positive.
733 if constexpr (std::sized_sentinel_for<sentinel_t, it_t>) // We can check whether we can jump.
734 {
735 if (chunk_size >= std::abs(urng_begin - end_of_chunk)) // Remaining range smaller than chunk_size
736 return urng_begin;
737 else // We can jump chunk_size many times
738 return std::ranges::prev(end_of_chunk, chunk_size);
739 }
740 else // We need to decrement one by one to not cross urng_begin.
741 {
742 for (uint16_t decrements{}; decrements != chunk_size && end_of_chunk != urng_begin; ++decrements)
743 --end_of_chunk;
744
745 return end_of_chunk;
746 }
747 }
748};
749
750// ---------------------------------------------------------------------------------------------------------------------
751// chunk_fn (adaptor definition)
752// ---------------------------------------------------------------------------------------------------------------------
753
756struct chunk_fn
757{
759 constexpr auto operator()(uint16_t const chunk_size) const
760 {
761 return adaptor_from_functor{*this, chunk_size};
762 }
763
769 template <std::ranges::range underlying_range_t>
770 constexpr auto operator()(underlying_range_t && urange, uint16_t const chunk_size) const
771 {
772 static_assert(std::ranges::input_range<underlying_range_t>,
773 "The range parameter to views::chunk must model std::ranges::input_range.");
774
775 return chunk_view{std::forward<underlying_range_t>(urange), chunk_size};
776 }
777};
778
779} // namespace seqan3::detail
780
781namespace seqan3::views
782{
823inline constexpr auto chunk = detail::chunk_fn{};
824
825} // namespace seqan3::views
Provides seqan3::detail::adaptor_from_functor.
T addressof(T... args)
Provides seqan3::detail::all.
Core alphabet concept and free function/type trait wrappers.
T begin(T... args)
Provides various transformation traits used by the range module.
T end(T... args)
constexpr size_t size
The size of a type pack.
Definition: traits.hpp:146
constexpr auto chunk
Divide a range in chunks.
Definition: chunk.hpp:823
Specifies requirements of an input range type for which the const version of that type satisfies the ...
Provides various transformation traits for use on iterators.
T move(T... args)
The SeqAn namespace for views.
Definition: char_strictly_to.hpp:22
SeqAn specific customisations in the standard namespace.
T operator!=(T... args)
Provides platform and dependency checks.
The <ranges> header from C++20's standard library.
Additional non-standard concepts for ranges.