SeqAn3 3.3.0-rc.1
The Modern C++ library for sequence analysis.
 
Loading...
Searching...
No Matches
chunk.hpp
Go to the documentation of this file.
1// -----------------------------------------------------------------------------------------------------
2// Copyright (c) 2006-2022, Knut Reinert & Freie Universität Berlin
3// Copyright (c) 2016-2022, 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 <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 std::ranges::range_difference_t<urng_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 urng, std::ranges::range_difference_t<urng_t> const size_of_chunk) :
76 urange{std::move(urng)},
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 &&, std::ranges::range_difference_t<rng_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
253// https://github.com/seqan/seqan3/pull/3102
254#if SEQAN3_COMPILER_IS_GCC && (__GNUC__ > 12)
256 friend constexpr bool operator==(input_helper_iterator const & lhs, sentinel_t) noexcept
257 {
258 return lhs.outer_it->remaining == 0u || lhs.outer_it->urng_begin == lhs.outer_it->urng_end;
259 }
260#else
262 bool operator==(sentinel_t const & /* rhs */) noexcept
263 {
264 return this->outer_it->remaining == 0u || this->outer_it->urng_begin == this->outer_it->urng_end;
265 }
266#endif
267
269 outer_it_type * outer_it{nullptr};
270 };
271
273 using helper_it_t = input_helper_iterator<basic_input_iterator>;
274
275 // befriend the iterator on a const range
276 template <bool other_const_range>
277 friend class basic_input_iterator;
278
279 // befriend the inner iterator type
280 template <typename outer_it_type>
281 friend class input_helper_iterator;
282
283public:
288 using difference_type = typename std::iter_difference_t<urng_it_t>;
290 using value_type = std::ranges::subrange<helper_it_t, sentinel_t>;
292 using pointer = void;
294 using reference = value_type;
296 using iterator_concept = std::input_iterator_tag;
298
302 constexpr basic_input_iterator() = default;
303 constexpr basic_input_iterator(basic_input_iterator const &) = default;
304 constexpr basic_input_iterator(basic_input_iterator &&) = default;
305 constexpr basic_input_iterator & operator=(basic_input_iterator const &) = default;
306 constexpr basic_input_iterator & operator=(basic_input_iterator &&) = default;
307 ~basic_input_iterator() = default;
308
310 constexpr explicit basic_input_iterator(basic_input_iterator<!const_range> it) noexcept
311 requires const_range
312 :
313 chunk_size{std::move(it.chunk_size)},
314 remaining{std::move(it.remaining)},
315 urng_begin{std::move(it.urng_begin)},
316 urng_end{std::move(it.urng_end)},
317 current_chunk{std::move(it.current_chunk)}
318 {}
319
331 constexpr explicit basic_input_iterator(urng_it_t it_begin,
332 sentinel_t it_end,
333 std::ranges::range_difference_t<urng_t> const size_of_chunk) :
334 chunk_size{size_of_chunk},
335 remaining{size_of_chunk},
336 urng_begin{std::move(it_begin)},
337 urng_end{std::move(it_end)}
338 {
339 current_chunk = std::ranges::subrange<helper_it_t, sentinel_t>{helper_it_t{*this, it_begin}, it_end};
340 }
342
346
348 friend constexpr bool operator==(basic_input_iterator const & lhs, sentinel_t const & rhs) noexcept
349 {
350 return lhs.urng_begin == rhs;
351 }
352
354 friend constexpr bool operator==(basic_input_iterator const & lhs, basic_input_iterator const & rhs) noexcept
355 {
356 return (lhs.urng_begin == rhs.urng_begin) && (lhs.remaining == rhs.remaining);
357 }
358
360 constexpr basic_input_iterator & operator++() noexcept
361 {
362 while (remaining > 0u && urng_begin != urng_end) // if chunk was not fully consumed and range is not at end
363 {
364 ++urng_begin;
365 --remaining;
366 }
367 current_chunk = std::ranges::subrange<helper_it_t, sentinel_t>{helper_it_t{*this, urng_begin}, urng_end};
368 remaining = chunk_size;
369 return *this;
370 }
371
373 constexpr basic_input_iterator operator++(int) noexcept
374 {
375 basic_input_iterator tmp{*this};
376 ++(*this);
377 return tmp;
378 }
379
381 constexpr value_type operator*() const noexcept
382 {
383 return current_chunk;
384 }
385
386private:
388 std::ranges::range_difference_t<urng_t> chunk_size;
389
391 std::ranges::range_difference_t<urng_t> remaining;
392
394 urng_it_t urng_begin;
395
397 sentinel_t urng_end;
398
400 value_type current_chunk;
401};
402
419template <std::ranges::input_range urng_t>
420 requires std::ranges::view<urng_t>
421template <bool const_range>
422class chunk_view<urng_t>::basic_iterator : public maybe_iterator_category<maybe_const_iterator_t<const_range, urng_t>>
423{
424private:
426 using it_t = maybe_const_iterator_t<const_range, urng_t>;
428 using sentinel_t = maybe_const_sentinel_t<const_range, urng_t>;
429
430 // befriend the iterator on a const range
431 template <bool other_const_range>
432 friend class basic_iterator;
433
434public:
439 using difference_type = typename std::iter_difference_t<it_t>;
441 using value_type = std::ranges::subrange<it_t, it_t>;
443 using pointer = void;
445 using reference = value_type;
449 detail::iterator_concept_tag_t<it_t>>;
451
455 constexpr basic_iterator() = default;
456 constexpr basic_iterator(basic_iterator const &) = default;
457 constexpr basic_iterator(basic_iterator &&) = default;
458 constexpr basic_iterator & operator=(basic_iterator const &) = default;
459 constexpr basic_iterator & operator=(basic_iterator &&) = default;
460 ~basic_iterator() = default;
461
463 constexpr basic_iterator(basic_iterator<!const_range> const & it) noexcept
464 requires const_range
465 :
466 chunk_size{std::move(it.chunk_size)},
467 urng_begin{std::move(it.urng_begin)},
468 urng_end{std::move(it.urng_end)},
469 current_chunk{std::move(it.current_chunk)}
470 {}
471
483 constexpr explicit basic_iterator(it_t it_start,
484 sentinel_t it_end,
485 std::ranges::range_difference_t<urng_t> const size_of_chunk) :
486 chunk_size{size_of_chunk},
487 urng_begin{std::move(it_start)},
488 urng_end{std::move(it_end)}
489 {
490 current_chunk = value_type{it_start, get_next_end_of_chunk(it_start)};
491 }
493
497
499 friend constexpr bool operator==(basic_iterator const & lhs, sentinel_t const & rhs) noexcept
500 {
501 return lhs.current_chunk.begin() == rhs;
502 }
503
505 friend constexpr bool operator==(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
506 {
507 return (lhs.current_chunk.begin() == rhs.current_chunk.begin()) && (lhs.chunk_size == rhs.chunk_size);
508 }
509
511 friend constexpr bool operator!=(basic_iterator const & lhs, sentinel_t const & rhs) noexcept
512 {
513 return !(lhs == rhs);
514 }
515
517 friend constexpr bool operator!=(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
518 {
519 return !(lhs == rhs);
520 }
521
523 friend constexpr bool operator<(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
524 {
525 return lhs.current_chunk.begin() < rhs.current_chunk.begin();
526 }
527
529 friend constexpr bool operator>(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
530 {
531 return lhs.current_chunk.begin() > rhs.current_chunk.begin();
532 }
533
535 friend constexpr bool operator<=(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
536 {
537 return lhs.current_chunk.begin() <= rhs.current_chunk.begin();
538 }
539
541 friend constexpr bool operator>=(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
542 {
543 return lhs.current_chunk.begin() >= rhs.current_chunk.begin();
544 }
545
547
549 constexpr basic_iterator & operator++() noexcept
550 {
551 current_chunk = value_type{current_chunk.end(), get_next_end_of_chunk(current_chunk.end())};
552 return *this;
553 }
554
556 basic_iterator operator++(int) noexcept
557 {
558 basic_iterator tmp{*this};
559 ++(*this);
560 return tmp;
561 }
562
566 constexpr basic_iterator & operator--() noexcept
567 requires std::bidirectional_iterator<it_t>
568 {
569 current_chunk = value_type{get_former_start_of_chunk(current_chunk.begin()), current_chunk.begin()};
570 return *this;
571 }
572
576 constexpr basic_iterator operator--(int) noexcept
577 requires std::bidirectional_iterator<it_t>
578 {
579 basic_iterator tmp{*this};
580 --(*this);
581 return tmp;
582 }
583
587 constexpr basic_iterator & operator+=(difference_type const skip) noexcept
588 requires std::random_access_iterator<it_t>
589 {
590 auto new_start_it = current_chunk.begin() + (chunk_size * skip);
591 current_chunk = value_type{new_start_it, get_next_end_of_chunk(new_start_it)};
592 return *this;
593 }
594
598 constexpr basic_iterator operator+(difference_type const skip) const noexcept
599 requires std::random_access_iterator<it_t>
600 {
601 basic_iterator tmp{*this};
602 return tmp += skip;
603 }
604
608 friend constexpr basic_iterator operator+(difference_type const skip, basic_iterator const & it) noexcept
609 requires std::random_access_iterator<it_t>
610 {
611 return it + skip;
612 }
613
617 constexpr basic_iterator & operator-=(difference_type const skip) noexcept
618 requires std::random_access_iterator<it_t>
619 {
620 auto new_start_it = current_chunk.begin() - (chunk_size * skip);
621 current_chunk = value_type{new_start_it, get_next_end_of_chunk(new_start_it)};
622 return *this;
623 }
624
629 constexpr basic_iterator operator-(difference_type const skip) const noexcept
630 requires std::random_access_iterator<it_t>
631 {
632 basic_iterator tmp{*this};
633 return tmp -= skip;
634 }
635
639 friend constexpr basic_iterator operator-(difference_type const skip, basic_iterator const & it) noexcept
640 requires std::random_access_iterator<it_t>
641 {
642 return it - skip;
643 }
644
649 friend constexpr difference_type operator-(basic_iterator const & lhs, basic_iterator const & rhs) noexcept
650 requires std::sized_sentinel_for<it_t, it_t>
651 {
652 return static_cast<difference_type>((lhs.current_chunk.begin() - rhs.current_chunk.begin()) / lhs.chunk_size);
653 }
654
658 friend constexpr difference_type operator-(sentinel_t const & /* lhs */, basic_iterator const & rhs) noexcept
659 requires std::sized_sentinel_for<sentinel_t, it_t>
660 {
661 return static_cast<difference_type>((rhs.urng_end - rhs.current_chunk.begin() + rhs.chunk_size - 1)
662 / rhs.chunk_size);
663 }
664
668 friend constexpr difference_type operator-(basic_iterator const & lhs, sentinel_t const & rhs) noexcept
669 requires std::sized_sentinel_for<sentinel_t, it_t>
670 {
671 return -(rhs - lhs);
672 }
673
677 constexpr reference operator[](difference_type const n) const
678 requires std::random_access_iterator<it_t>
679 {
680 return *(*this + n);
681 }
682
684 constexpr value_type operator*() const noexcept
685 {
686 return current_chunk;
687 }
688
689private:
691 std::ranges::range_difference_t<urng_t> chunk_size;
692
694 it_t urng_begin;
695
697 sentinel_t urng_end;
698
700 value_type current_chunk;
701
703 constexpr it_t get_next_end_of_chunk(it_t start_of_chunk) const
704 {
705 // Very similar to `return std::ranges::next(start_of_chunk, chunk_size, urng_end);`.
706 // However, the STL checks that the direction of chunk_size and urng_end are the same for sized_sentinels when
707 // -D_GLIBCXX_ASSERTIONS is enabled.
708 // If start_of_chunk was moved past urng_end, we should constrain it.
709 // =========X===========Y============
710 // ^ ^
711 // urng_end new_start_it
712 // <----------- // direction from iterator to bound
713 // ---------> // direction from chunk_size
714 // See https://eel.is/c++draft/range.iter.op.advance (next just takes and returns a copy of the iterator)
715 // Note: n is chunk_size and always positive.
716 if constexpr (std::sized_sentinel_for<sentinel_t, it_t>) // We can check whether we can jump.
717 {
718 if (chunk_size >= std::abs(urng_end - start_of_chunk)) // Remaining range smaller than chunk_size
719 return std::ranges::next(start_of_chunk, urng_end); // Returns it_t which is equal to urng_end
720 else // We can jump chunk_size many times
721 return std::ranges::next(start_of_chunk, chunk_size);
722 }
723 else // We need to increment one by one to not cross urng_end.
724 {
725 for (std::ranges::range_difference_t<urng_t> increments{};
726 increments != chunk_size && start_of_chunk != urng_end;
727 ++increments)
728 {
729 ++start_of_chunk;
730 }
731
732 return start_of_chunk;
733 }
734 }
735
737 constexpr it_t get_former_start_of_chunk(it_t end_of_chunk) const
738 {
739 // Very similar to `return std::ranges::prev(end_of_chunk, chunk_size, urng_begin);`.
740 // However, the STL checks that the direction of chunk_size and urng_end are the same for sized_sentinels when
741 // -D_GLIBCXX_ASSERTIONS is enabled.
742 // If end_of_chunk was moved before urng_begin, we should constrain it.
743 // =========X===========Y============
744 // ^ ^
745 // end_of_chunk urng_begin
746 // <--------- // direction from chunk_size
747 // ---------> // direction from iterator to bound
748 // See https://eel.is/c++draft/range.iter.op.advance (prev(i, n, bound) is equal to advance(i, -n, bound))
749 // Note: n is chunk_size and always positive.
750 if constexpr (std::sized_sentinel_for<sentinel_t, it_t>) // We can check whether we can jump.
751 {
752 if (chunk_size >= std::abs(urng_begin - end_of_chunk)) // Remaining range smaller than chunk_size
753 return urng_begin;
754 else // We can jump chunk_size many times
755 return std::ranges::prev(end_of_chunk, chunk_size);
756 }
757 else // We need to decrement one by one to not cross urng_begin.
758 {
759 for (std::ranges::range_difference_t<urng_t> decrements{};
760 decrements != chunk_size && end_of_chunk != urng_begin;
761 ++decrements)
762 {
763 --end_of_chunk;
764 }
765
766 return end_of_chunk;
767 }
768 }
769};
770
771// ---------------------------------------------------------------------------------------------------------------------
772// chunk_fn (adaptor definition)
773// ---------------------------------------------------------------------------------------------------------------------
774
777struct chunk_fn
778{
780 constexpr auto operator()(std::ptrdiff_t const chunk_size) const
781 {
782 return adaptor_from_functor{*this, chunk_size};
783 }
784
790 template <std::ranges::range urng_t>
791 constexpr auto operator()(urng_t && urange, std::ranges::range_difference_t<urng_t> const chunk_size) const
792 {
793 static_assert(std::ranges::input_range<urng_t>,
794 "The range parameter to views::chunk must model std::ranges::input_range.");
795
796 return chunk_view{std::forward<urng_t>(urange), chunk_size};
797 }
798};
799
800} // namespace seqan3::detail
801
802namespace seqan3::views
803{
844inline constexpr auto chunk = detail::chunk_fn{};
845
846} // 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: type_pack/traits.hpp:146
constexpr auto chunk
Divide a range in chunks.
Definition: chunk.hpp:844
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.
Additional non-standard concepts for ranges.