Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
functional_adapters.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <concepts>
5#include <cstddef>
6#include <functional>
7#include <iterator>
8#include <optional>
9#include <ranges>
10#include <tuple>
11#include <type_traits>
12#include <unordered_map>
13#include <utility>
14#include <vector>
15
16namespace helios::utils {
17
18namespace details {
19
20/// @brief Checks if the iterator yields a genuine reference (lvalue ref), not a
21/// proxy/value.
22template <typename Iter>
24 std::is_lvalue_reference_v<std::iter_reference_t<Iter>>;
25
26/// @brief Selects pointer vs optional depending on whether the iterator yields
27/// a real ref.
28template <typename Iter>
29using find_result_t = std::conditional_t<
31 std::remove_reference_t<std::iter_reference_t<Iter>>*, // T* / const T*
32 std::optional<std::iter_value_t<Iter>> // owned copy
33 >;
34
35/// @brief Traits for `Iter`, selecting iterator concept from `Iter` if it has
36/// one, else forward.
37template <typename Iter>
39 // C++20 concept: propagate from Iter if it has one, else forward
41 std::conditional_t<IterYieldsReference<Iter>, std::forward_iterator_tag,
42 std::input_iterator_tag>; // proxy — demote for C++17
43 // compat
44
45 // C++17 category: always input if proxy, forward if real ref
47 std::conditional_t<IterYieldsReference<Iter>, std::forward_iterator_tag,
48 std::input_iterator_tag>;
49};
50
51/// @brief Gets the iterator concept from `Iter`.
52template <typename Iter>
55
56/// @brief Gets the iterator category from `Iter`.
57template <typename Iter>
60
61/**
62 * @brief Concept to check if a type is tuple-like (has tuple_size and get).
63 * @details This is used to determine if std::tuple_cat can be applied to a
64 * type.
65 * @tparam T Type to check
66 */
67template <typename T>
68concept TupleLike = requires {
69 typename std::tuple_size<std::remove_cvref_t<T>>::type;
70 requires std::tuple_size_v<std::remove_cvref_t<T>> >= 0;
71};
72
73/// @brief Helper to extract tuple element types and check if a folder is
74/// invocable with accumulator + tuple elements
75template <typename Folder, typename Accumulator, typename Tuple>
76struct is_folder_applicable_impl : std::false_type {};
77
78template <typename Folder, typename Accumulator, typename... TupleArgs>
79struct is_folder_applicable_impl<Folder, Accumulator, std::tuple<TupleArgs...>>
80 : std::bool_constant<std::invocable<Folder, Accumulator, TupleArgs...>> {};
81
82template <typename Folder, typename Accumulator, typename Tuple>
85
86/// @brief Helper to get the result type of folder applied with accumulator +
87/// tuple elements
88template <typename Folder, typename Accumulator, typename Tuple>
90
91template <typename Folder, typename Accumulator, typename... TupleArgs>
92struct folder_apply_result<Folder, Accumulator, std::tuple<TupleArgs...>> {
93 using type = std::invoke_result_t<Folder, Accumulator, TupleArgs...>;
94};
95
96template <typename Folder, typename Accumulator, typename Tuple>
99
100/// @brief Helper to get the result type of either invoke or apply
101template <typename Func, typename... Args>
102consteval auto GetCallOrApplyResultType() noexcept {
103 if constexpr (std::invocable<Func, Args...>) {
104 return std::type_identity<std::invoke_result_t<Func, Args...>>{};
105 } else if (sizeof...(Args) == 1) {
106 return std::type_identity<decltype(std::apply(std::declval<Func>(),
107 std::declval<Args>()...))>{};
108 } else {
109 static_assert(sizeof...(Args) == 1,
110 "Callable must be invocable with Args or via std::apply with "
111 "a single tuple argument");
112 }
113}
114
115template <typename Func, typename... Args>
117 typename decltype(GetCallOrApplyResultType<Func, Args...>())::type;
118
119template <typename Func, typename... Args>
121 std::invocable<Func, Args...> ||
122 (sizeof...(Args) == 1 && requires(Func func, Args&&... args) {
123 std::apply(func, std::forward<Args>(args)...);
124 });
125
126template <typename Func, typename ReturnType, typename... Args>
128 CallableOrApplicable<Func, Args...> &&
129 std::convertible_to<call_or_apply_result_t<Func, Args...>, ReturnType>;
130
131} // namespace details
132
133template <typename Derived>
135
136/// @brief Concept for types that are adapter traits (derived from
137/// `FunctionalAdapterBase`).
138template <typename T>
139concept AdapterTrait = std::derived_from<T, FunctionalAdapterBase<T>>;
140
141/// @brief Concept for types that are external ranges (not adapters).
142template <typename R>
144 !AdapterTrait<std::remove_cvref_t<R>> && std::ranges::input_range<R>;
145
146/**
147 * @brief Concept for types that can be used as base iterators in adapters.
148 * @tparam T Type to check
149 */
150template <typename T>
151concept IteratorLike = requires(T iter, const T const_iter) {
152 typename std::iter_value_t<T>;
153 {
154 ++iter
155 } -> std::same_as<std::add_lvalue_reference_t<std::remove_cvref_t<T>>>;
156 { iter++ } -> std::same_as<std::remove_cvref_t<T>>;
157 { *const_iter } -> std::convertible_to<std::iter_value_t<T>>;
158 { const_iter == const_iter } -> std::convertible_to<bool>;
159 { const_iter != const_iter } -> std::convertible_to<bool>;
160};
161
162/**
163 * @brief Concept for bidirectional iterators that support decrement operations.
164 * @tparam T Type to check
165 */
166template <typename T>
167concept BidirectionalIteratorLike = IteratorLike<T> && requires(T iter) {
168 {
169 --iter
170 } -> std::same_as<std::add_lvalue_reference_t<std::remove_cvref_t<T>>>;
171};
172
173/**
174 * @brief Concept for predicate functions that can be applied to iterator
175 * values.
176 * @details The predicate must be invocable with the value type and return a
177 * boolean-testable result. Supports both direct invocation and `std::apply` for
178 * tuple unpacking. The result must be usable in boolean contexts (if
179 * statements, etc.). Allows const ref consumption even if the original type is
180 * just ref or value.
181 * @tparam Pred Predicate type
182 * @tparam ValueType Type of values to test
183 */
184template <typename Pred, typename ValueType>
188 Pred, bool,
189 std::add_lvalue_reference_t<
190 std::add_const_t<std::remove_cvref_t<ValueType>>>> ||
192 std::remove_cvref_t<ValueType>>;
193
194/**
195 * @brief Concept for transformation functions that can be applied to iterator
196 * values.
197 * @details The function must be invocable with the value type and return a
198 * non-void result. Supports both direct invocation and `std::apply` for tuple
199 * unpacking. Allows const ref consumption even if the original type is just ref
200 * or value.
201 * @tparam Func Function type
202 * @tparam ValueType Type of values to transform
203 */
204template <typename Func, typename ValueType>
207 !std::is_void_v<details::call_or_apply_result_t<Func, ValueType>>) ||
209 Func, std::add_lvalue_reference_t<
210 std::add_const_t<std::remove_cvref_t<ValueType>>>> &&
211 !std::is_void_v<details::call_or_apply_result_t<
212 Func, std::add_lvalue_reference_t<
213 std::add_const_t<std::remove_cvref_t<ValueType>>>>>) ||
215 !std::is_void_v<details::call_or_apply_result_t<
216 Func, std::remove_cvref_t<ValueType>>>);
217
218/**
219 * @brief Concept for inspection functions that observe but don't modify values.
220 * @details The function must be invocable with the value type and return void.
221 * Supports both direct invocation and `std::apply` for tuple unpacking.
222 * Allows const ref consumption even if the original type is just ref or value.
223 * @tparam Func Function type
224 * @tparam ValueType Type of values to inspect
225 */
226template <typename Func, typename ValueType>
230 Func, void,
231 std::add_lvalue_reference_t<
232 std::add_const_t<std::remove_cvref_t<ValueType>>>> ||
234 std::remove_cvref_t<ValueType>>;
235
236/**
237 * @brief Concept for action functions that process values.
238 * @details The function must be invocable with the value type.
239 * Supports both direct invocation and `std::apply` for tuple unpacking.
240 * Allows const ref consumption even if the original type is just ref or value.
241 * @tparam Action Action function type
242 * @tparam ValueType Type of values to process
243 */
244template <typename Action, typename ValueType>
245concept ActionFor =
248 Action, void,
249 std::add_lvalue_reference_t<
250 std::add_const_t<std::remove_cvref_t<ValueType>>>> ||
252 std::remove_cvref_t<ValueType>>;
253
254/**
255 * @brief Concept for folder functions that accumulate values.
256 * @details The function must be invocable with an accumulator and a value type.
257 * Supports both direct invocation and `std::apply` for tuple unpacking.
258 * @tparam Folder Folder function type
259 * @tparam Accumulator Accumulator type
260 * @tparam ValueType Type of values to fold
261 */
262template <typename Folder, typename Accumulator, typename ValueType>
263concept FolderFor =
264 (std::invocable<Folder, Accumulator, ValueType> &&
265 std::convertible_to<std::invoke_result_t<Folder, Accumulator, ValueType>,
266 Accumulator>) ||
267 (details::FolderApplicable<Folder, Accumulator,
268 std::remove_cvref_t<ValueType>> &&
269 std::convertible_to<
270 details::folder_apply_result_t<Folder, Accumulator,
271 std::remove_cvref_t<ValueType>>,
272 Accumulator>);
273
274/**
275 * @brief Concept to validate `FilterAdapter` requirements.
276 * @tparam Iter Iterator type
277 * @tparam Pred Predicate type
278 */
279template <typename Iter, typename Pred>
282
283/**
284 * @brief Concept to validate `MapAdapter` requirements.
285 * @tparam Iter Iterator type
286 * @tparam Func Transform function type
287 */
288template <typename Iter, typename Func>
291
292/**
293 * @brief Concept to validate `TakeAdapter` requirements.
294 * @tparam Iter Iterator type
295 */
296template <typename Iter>
298
299/**
300 * @brief Concept to validate `SkipAdapter` requirements.
301 * @tparam Iter Iterator type
302 */
303template <typename Iter>
305
306/**
307 * @brief Concept to validate `TakeWhileAdapter` requirements.
308 * @tparam Iter Iterator type
309 * @tparam Pred Predicate type
310 */
311template <typename Iter, typename Pred>
314
315/**
316 * @brief Concept to validate `SkipWhileAdapter` requirements.
317 * @tparam Iter Iterator type
318 * @tparam Pred Predicate type
319 */
320template <typename Iter, typename Pred>
323
324/**
325 * @brief Concept to validate `EnumerateAdapter` requirements.
326 * @tparam Iter Iterator type
327 */
328template <typename Iter>
330
331/**
332 * @brief Concept to validate `InspectAdapter` requirements.
333 * @tparam Iter Iterator type
334 * @tparam Func Inspector function type
335 */
336template <typename Iter, typename Func>
339
340/**
341 * @brief Concept to validate `StepByAdapter` requirements.
342 * @tparam Iter Iterator type
343 */
344template <typename Iter>
346
347/**
348 * @brief Concept to validate `ChainAdapter` requirements.
349 * @tparam Iter1 First iterator type
350 * @tparam Iter2 Second iterator type
351 */
352template <typename Iter1, typename Iter2>
355 std::same_as<std::iter_value_t<Iter1>, std::iter_value_t<Iter2>>;
356
357/**
358 * @brief Concept to validate `ReverseAdapter` requirements.
359 * @tparam Iter Iterator type
360 */
361template <typename Iter>
363
364/**
365 * @brief Concept to validate `JoinAdapter` requirements.
366 * @tparam Iter Iterator type
367 */
368template <typename Iter>
370 IteratorLike<Iter> && std::ranges::range<std::iter_value_t<Iter>>;
371
372/**
373 * @brief Concept to validate `SlideAdapter` requirements.
374 * @tparam Iter Iterator type
375 */
376template <typename Iter>
378
379/**
380 * @brief Concept to validate `StrideAdapter` requirements.
381 * @tparam Iter Iterator type
382 */
383template <typename Iter>
385
386/**
387 * @brief Concept to validate `ZipAdapter` requirements.
388 * @tparam Iter1 First iterator type
389 * @tparam Iter2 Second iterator type
390 */
391template <typename Iter1, typename Iter2>
393
394template <typename Iter, typename Pred>
396class FilterAdapter;
397
398template <typename Iter, typename Func>
400class MapAdapter;
401
402template <typename Iter>
404class TakeAdapter;
405
406template <typename Iter>
408class SkipAdapter;
409
410template <typename Iter, typename Pred>
412class TakeWhileAdapter;
413
414template <typename Iter, typename Pred>
416class SkipWhileAdapter;
417
418template <typename Iter>
420class EnumerateAdapter;
421
422template <typename Iter, typename Func>
424class InspectAdapter;
425
426template <typename Iter>
428class StepByAdapter;
429
430template <typename Iter1, typename Iter2>
432class ChainAdapter;
433
434template <typename Iter>
436class ReverseAdapter;
437
438template <typename Iter>
441
442template <typename Iter>
444class SlideAdapter;
445
446template <typename Iter>
448class StrideAdapter;
449
450template <typename Iter1, typename Iter2>
452class ZipAdapter;
453
454/**
455 * @brief Iterator adapter that filters elements based on a predicate function.
456 * @details This adapter provides lazy filtering of iterator values.
457 * The filtering happens during iteration, not upfront, making it memory
458 * efficient for large sequences.
459 *
460 * Supports chaining with other adapters for complex data transformations.
461 *
462 * @note This adapter maintains forward iterator semantics.
463 * @note The adapter is constexpr-compatible for compile-time evaluation.
464 * @tparam Iter Underlying iterator type that satisfies IteratorLike concept
465 * @tparam Pred Predicate function type
466 *
467 * @code
468 * auto enemies = query.Filter([](const Health& h) {
469 * return h.points > 0;
470 * });
471 * @endcode
472 */
473template <typename Iter, typename Pred>
475class FilterAdapter final
476 : public FunctionalAdapterBase<FilterAdapter<Iter, Pred>> {
477public:
480 using value_type = std::iter_value_t<Iter>;
482 using pointer = void;
483 using difference_type = std::iter_difference_t<Iter>;
484
485 /**
486 * @brief Constructs a filter adapter with the given iterator range and
487 * predicate.
488 * @param begin Start of the iterator range
489 * @param end End of the iterator range
490 * @param predicate Function to filter elements
491 */
492 constexpr FilterAdapter(Iter begin, Iter end, Pred predicate) noexcept(
493 std::is_nothrow_copy_constructible_v<Iter> &&
494 std::is_nothrow_move_constructible_v<Iter> &&
495 std::is_nothrow_move_constructible_v<Pred> && noexcept(AdvanceToValid()))
496 : begin_(std::move(begin)),
497 current_(begin_),
498 end_(std::move(end)),
499 predicate_(std::move(predicate)) {
500 AdvanceToValid();
501 }
502
503 /**
504 * @brief Constructs a filter adapter from a range.
505 * @tparam R Range type
506 * @param range Range to filter
507 * @param predicate Function to filter elements
508 */
509 template <ExternalRange R>
511 constexpr FilterAdapter(R& range, Pred predicate) noexcept(
512 noexcept(FilterAdapter(std::ranges::begin(range), std::ranges::end(range),
513 std::move(predicate))))
514 : FilterAdapter(std::ranges::begin(range), std::ranges::end(range),
515 std::move(predicate)) {}
516
517 /**
518 * @brief Constructs a filter adapter from a const range.
519 * @tparam R Range type
520 * @param range Range to filter
521 * @param predicate Function to filter elements
522 */
523 template <ExternalRange R>
525 constexpr FilterAdapter(const R& range, Pred predicate) noexcept(
526 noexcept(FilterAdapter(std::ranges::cbegin(range),
527 std::ranges::cend(range), std::move(predicate))))
528 : FilterAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
529 std::move(predicate)) {}
530
531 constexpr FilterAdapter(const FilterAdapter&) noexcept(
532 std::is_nothrow_copy_constructible_v<Iter> &&
533 std::is_nothrow_copy_constructible_v<Pred>) = default;
534 constexpr FilterAdapter(FilterAdapter&&) noexcept(
535 std::is_nothrow_move_constructible_v<Iter> &&
536 std::is_nothrow_move_constructible_v<Pred>) = default;
537 constexpr ~FilterAdapter() noexcept(std::is_nothrow_destructible_v<Iter> &&
538 std::is_nothrow_destructible_v<Pred>) =
539 default;
540
541 constexpr FilterAdapter& operator=(const FilterAdapter&) noexcept(
542 std::is_nothrow_copy_assignable_v<Iter> &&
543 std::is_nothrow_copy_assignable_v<Pred>) = default;
544 constexpr FilterAdapter& operator=(FilterAdapter&&) noexcept(
545 std::is_nothrow_move_assignable_v<Iter> &&
546 std::is_nothrow_move_assignable_v<Pred>) = default;
547
548 constexpr FilterAdapter& operator++() noexcept(
549 noexcept(++std::declval<Iter&>()) && noexcept(AdvanceToValid()));
550 [[nodiscard]] constexpr FilterAdapter operator++(int) noexcept(
551 std::is_nothrow_copy_constructible_v<FilterAdapter> &&
552 noexcept(++std::declval<FilterAdapter&>()));
553
554 [[nodiscard]] constexpr reference operator*() const
555 noexcept(noexcept(*std::declval<const Iter&>())) {
556 return *current_;
557 }
558
559 pointer operator->() const = delete;
560
561 [[nodiscard]] constexpr bool operator==(const FilterAdapter& other) const
562 noexcept(noexcept(std::declval<const Iter&>() ==
563 std::declval<const Iter&>())) {
564 return current_ == other.current_;
565 }
566
567 [[nodiscard]] constexpr bool operator!=(const FilterAdapter& other) const
568 noexcept(noexcept(std::declval<const FilterAdapter&>() ==
569 std::declval<const FilterAdapter&>())) {
570 return !(*this == other);
571 }
572
573 /**
574 * @brief Checks if the iterator has reached the end.
575 * @return True if at end, false otherwise
576 */
577 [[nodiscard]] constexpr bool IsAtEnd() const
578 noexcept(noexcept(std::declval<const Iter&>() ==
579 std::declval<const Iter&>())) {
580 return current_ == end_;
581 }
582
583 [[nodiscard]] constexpr FilterAdapter begin() const
584 noexcept(std::is_nothrow_copy_constructible_v<FilterAdapter>) {
585 return *this;
586 }
587
588 [[nodiscard]] constexpr FilterAdapter end() const
589 noexcept(std::is_nothrow_constructible_v<FilterAdapter, const Iter&,
590 const Iter&, const Pred&>) {
591 return {end_, end_, predicate_};
592 }
593
594private:
595 constexpr void AdvanceToValid() noexcept(
596 std::is_nothrow_invocable_v<Pred, std::iter_value_t<Iter>> &&
597 noexcept(*std::declval<Iter&>()) && noexcept(++std::declval<Iter&>()) &&
598 noexcept(std::declval<Iter&>() != std::declval<Iter&>()));
599
600 Iter begin_; ///< Start of the iterator range
601 Iter current_; ///< Current position in the iteration
602 Iter end_; ///< End of the iterator range
603 Pred predicate_; ///< Predicate function for filtering
604};
605
606template <ExternalRange R, typename Pred>
607FilterAdapter(R&, Pred) -> FilterAdapter<std::ranges::iterator_t<R>, Pred>;
608
609template <ExternalRange R, typename Pred>
610FilterAdapter(const R&, Pred)
611 -> FilterAdapter<std::ranges::iterator_t<const R>, Pred>;
612
613template <typename Iter, typename Pred>
614 requires FilterAdapterRequirements<Iter, Pred>
615constexpr auto FilterAdapter<Iter, Pred>::operator++() noexcept(
616 noexcept(++std::declval<Iter&>()) && noexcept(AdvanceToValid()))
617 -> FilterAdapter& {
618 ++current_;
619 AdvanceToValid();
620 return *this;
621}
622
623template <typename Iter, typename Pred>
625constexpr auto FilterAdapter<Iter, Pred>::operator++(int) noexcept(
626 std::is_nothrow_copy_constructible_v<FilterAdapter> &&
627 noexcept(++std::declval<FilterAdapter&>())) -> FilterAdapter {
628 auto temp = *this;
629 ++(*this);
630 return temp;
631}
632
633template <typename Iter, typename Pred>
635constexpr void FilterAdapter<Iter, Pred>::AdvanceToValid() noexcept(
636 std::is_nothrow_invocable_v<Pred, std::iter_value_t<Iter>> &&
637 noexcept(*std::declval<Iter&>()) && noexcept(++std::declval<Iter&>()) &&
638 noexcept(std::declval<Iter&>() != std::declval<Iter&>())) {
639 while (current_ != end_) {
640 auto value = *current_;
641 bool matches = false;
642 if constexpr (std::invocable<Pred, decltype(value)>) {
643 matches = predicate_(value);
644 } else {
645 matches = std::apply(predicate_, value);
646 }
647 if (matches) {
648 break;
649 }
650 ++current_;
651 }
652}
653
654/**
655 * @brief Iterator adapter that transforms each element using a function.
656 * @details This adapter applies a transformation function to each element
657 * during iteration. The transformation is lazy - it happens when the element is
658 * accessed, not when the adapter is created.
659 *
660 * The transformation function can accept either the full value or individual
661 * tuple components if the value is a tuple type.
662 *
663 * @note This adapter maintains forward iterator semantics.
664 * The adapter is constexpr-compatible for compile-time evaluation.
665 * @tparam Iter Underlying iterator type that satisfies IteratorLike concept
666 * @tparam Func Transformation function type
667 *
668 * @code
669 * auto positions = query.Map([](const Transform& t) {
670 * return t.position;
671 * });
672 * @endcode
673 */
674template <typename Iter, typename Func>
675 requires MapAdapterRequirements<Iter, Func>
676class MapAdapter final : public FunctionalAdapterBase<MapAdapter<Iter, Func>> {
677private:
678 template <typename T>
679 struct DeduceValueType;
680
681 template <typename... Args>
682 struct DeduceValueType<std::tuple<Args...>> {
683 using Type = decltype(std::apply(std::declval<Func>(),
684 std::declval<std::tuple<Args...>>()));
685 };
686
687 template <typename T>
688 requires(!requires { typename std::tuple_size<T>::type; })
689 struct DeduceValueType<T> {
690 using Type = std::invoke_result_t<Func, T>;
691 };
692
693public:
694 using iterator_concept = std::input_iterator_tag;
695 using iterator_category = std::input_iterator_tag;
696 using value_type = typename DeduceValueType<std::iter_value_t<Iter>>::Type;
698 using pointer = void;
699 using difference_type = std::iter_difference_t<Iter>;
700
701 /**
702 * @brief Constructs a map adapter with the given iterator range and transform
703 * function.
704 * @param begin Start of the iterator range
705 * @param end End of the iterator range
706 * @param transform Function to transform elements
707 */
708 constexpr MapAdapter(Iter begin, Iter end, Func transform) noexcept(
709 std::is_nothrow_move_constructible_v<Iter> &&
710 std::is_nothrow_move_constructible_v<Func>)
711 : begin_(std::move(begin)),
712 current_(begin_),
713 end_(std::move(end)),
714 transform_(std::move(transform)) {}
715
716 /**
717 * @brief Constructs a map adapter from a range and transform function.
718 * @tparam R The type of the input range
719 * @param range The input range to adapt
720 * @param transform Function to transform elements
721 */
722 template <ExternalRange R>
724 constexpr MapAdapter(R& range, Func transform) noexcept(
725 noexcept(MapAdapter(std::ranges::begin(range), std::ranges::end(range),
726 std::move(transform))))
727 : MapAdapter(std::ranges::begin(range), std::ranges::end(range),
728 std::move(transform)) {}
729
730 /**
731 * @brief Constructs a map adapter from a const range and transform function.
732 * @tparam R The type of the input range
733 * @param range The input range to adapt
734 * @param transform Function to transform elements
735 */
736 template <ExternalRange R>
738 constexpr MapAdapter(const R& range, Func transform) noexcept(
739 noexcept(MapAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
740 std::move(transform))))
741 : MapAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
742 std::move(transform)) {}
743
744 constexpr MapAdapter(const MapAdapter&) noexcept(
745 std::is_nothrow_copy_constructible_v<Iter> &&
746 std::is_nothrow_copy_constructible_v<Func>) = default;
747 constexpr MapAdapter(MapAdapter&&) noexcept(
748 std::is_nothrow_move_constructible_v<Iter> &&
749 std::is_nothrow_move_constructible_v<Func>) = default;
750 constexpr ~MapAdapter() noexcept(std::is_nothrow_destructible_v<Iter> &&
751 std::is_nothrow_destructible_v<Func>) =
752 default;
753
754 constexpr MapAdapter& operator=(const MapAdapter&) noexcept(
755 std::is_nothrow_copy_assignable_v<Iter> &&
756 std::is_nothrow_copy_assignable_v<Func>) = default;
757 constexpr MapAdapter& operator=(MapAdapter&&) noexcept(
758 std::is_nothrow_move_assignable_v<Iter> &&
759 std::is_nothrow_move_assignable_v<Func>) = default;
760
761 constexpr MapAdapter& operator++() noexcept(
762 noexcept(++std::declval<Iter&>()));
763 [[nodiscard]] constexpr MapAdapter operator++(int) noexcept(
764 std::is_nothrow_copy_constructible_v<MapAdapter> &&
765 noexcept(++std::declval<MapAdapter&>()));
766
767 [[nodiscard]] constexpr reference operator*() const
768 noexcept(std::is_nothrow_invocable_v<Func, std::iter_value_t<Iter>> &&
769 noexcept(*std::declval<const Iter&>()));
770
771 pointer operator->() const = delete;
772
773 [[nodiscard]] constexpr bool operator==(const MapAdapter& other) const
774 noexcept(noexcept(std::declval<const Iter&>() ==
775 std::declval<const Iter&>())) {
776 return current_ == other.current_;
777 }
778 [[nodiscard]] constexpr bool operator!=(const MapAdapter& other) const
779 noexcept(noexcept(std::declval<const MapAdapter&>() ==
780 std::declval<const MapAdapter&>())) {
781 return !(*this == other);
782 }
783
784 [[nodiscard]] constexpr MapAdapter begin() const
785 noexcept(std::is_nothrow_copy_constructible_v<MapAdapter>) {
786 return *this;
787 }
788
789 [[nodiscard]] constexpr MapAdapter end() const
790 noexcept(std::is_nothrow_constructible_v<MapAdapter, const Iter&,
791 const Iter&, const Func&>) {
792 return {end_, end_, transform_};
793 }
794
795private:
796 Iter begin_; ///< Start of the iterator range
797 Iter current_; ///< Current position in the iteration
798 Iter end_; ///< End of the iterator range
799 Func transform_; ///< Transformation function
800};
801
802template <ExternalRange R, typename Func>
804
805template <ExternalRange R, typename Func>
806MapAdapter(const R&, Func)
808
809template <typename Iter, typename Func>
811constexpr auto MapAdapter<Iter, Func>::operator++() noexcept(
812 noexcept(++std::declval<Iter&>())) -> MapAdapter& {
813 ++current_;
814 return *this;
815}
816
817template <typename Iter, typename Func>
819constexpr auto MapAdapter<Iter, Func>::operator++(int) noexcept(
820 std::is_nothrow_copy_constructible_v<MapAdapter> &&
821 noexcept(++std::declval<MapAdapter&>())) -> MapAdapter {
822 auto temp = *this;
823 ++(*this);
824 return temp;
825}
826
827template <typename Iter, typename Func>
830 noexcept(std::is_nothrow_invocable_v<Func, std::iter_value_t<Iter>> &&
831 noexcept(*std::declval<const Iter&>())) -> reference {
832 auto value = *current_;
833 if constexpr (std::invocable<Func, decltype(value)>) {
834 return transform_(value);
835 } else {
836 return std::apply(transform_, value);
837 }
838}
839
840/**
841 * @brief Iterator adapter that yields only the first N elements.
842 * @details This adapter limits the number of elements yielded by the underlying
843 * iterator to at most the specified count. Once the count is reached or the
844 * underlying iterator reaches its end, iteration stops.
845 * @note This adapter maintains forward iterator semantics.
846 * @note The adapter is constexpr-compatible for compile-time evaluation.
847 * @tparam Iter Underlying iterator type that satisfies IteratorLike concept
848 *
849 * @code
850 * // Get only the first 5 entities
851 * auto first_five = query.Take(5);
852 * @endcode
853 */
854template <typename Iter>
856class TakeAdapter final : public FunctionalAdapterBase<TakeAdapter<Iter>> {
857public:
860 using value_type = std::iter_value_t<Iter>;
862 using pointer = void;
863 using difference_type = std::iter_difference_t<Iter>;
864
865 /**
866 * @brief Constructs a take adapter with the given iterator range and count.
867 * @param begin Start of the iterator range
868 * @param end End of the iterator range
869 * @param count Maximum number of elements to yield
870 */
871 constexpr TakeAdapter(Iter begin, Iter end, size_t count) noexcept(
872 std::is_nothrow_move_constructible_v<Iter> &&
873 std::is_nothrow_copy_constructible_v<Iter>)
874 : begin_(std::move(begin)),
875 current_(begin_),
876 end_(std::move(end)),
877 initial_count_(count),
878 remaining_(count) {}
879
880 /**
881 * @brief Constructs a take adapter from a range and count.
882 * @tparam R The type of the input range
883 * @param range The input range to adapt
884 * @param count The number of elements to take
885 */
886 template <ExternalRange R>
888 constexpr TakeAdapter(R& range, size_t count) noexcept(noexcept(
889 TakeAdapter(std::ranges::begin(range), std::ranges::end(range), count)))
890 : TakeAdapter(std::ranges::begin(range), std::ranges::end(range), count) {
891 }
892
893 /**
894 * @brief Constructs a map adapter from a const range and count.
895 * @tparam R The type of the input range
896 * @param range The input range to adapt
897 * @param count The number of elements to take
898 */
899 template <ExternalRange R>
901 constexpr TakeAdapter(const R& range, size_t count) noexcept(noexcept(
902 TakeAdapter(std::ranges::cbegin(range), std::ranges::cend(range), count)))
903 : TakeAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
904 count) {}
905
906 constexpr TakeAdapter(const TakeAdapter&) noexcept(
907 std::is_nothrow_copy_constructible_v<Iter>) = default;
908 constexpr TakeAdapter(TakeAdapter&&) noexcept(
909 std::is_nothrow_move_constructible_v<Iter>) = default;
910 constexpr ~TakeAdapter() noexcept(std::is_nothrow_destructible_v<Iter>) =
911 default;
912
913 constexpr TakeAdapter& operator=(const TakeAdapter&) noexcept(
914 std::is_nothrow_copy_assignable_v<Iter>) = default;
915 constexpr TakeAdapter& operator=(TakeAdapter&&) noexcept(
916 std::is_nothrow_move_assignable_v<Iter>) = default;
917
918 constexpr TakeAdapter& operator++() noexcept(
919 noexcept(++std::declval<Iter&>()));
920 [[nodiscard]] constexpr TakeAdapter operator++(int) noexcept(
921 std::is_nothrow_copy_constructible_v<TakeAdapter> &&
922 noexcept(++std::declval<TakeAdapter&>()));
923
924 [[nodiscard]] constexpr reference operator*() const
925 noexcept(noexcept(*std::declval<const Iter&>())) {
926 return *current_;
927 }
928
929 pointer operator->() const = delete;
930
931 [[nodiscard]] constexpr bool operator==(const TakeAdapter& other) const
932 noexcept(noexcept(std::declval<const Iter&>() ==
933 std::declval<const Iter&>()));
934
935 [[nodiscard]] constexpr bool operator!=(const TakeAdapter& other) const
936 noexcept(noexcept(std::declval<const TakeAdapter&>() ==
937 std::declval<const TakeAdapter&>())) {
938 return !(*this == other);
939 }
940
941 /**
942 * @brief Checks if the iterator has reached the end.
943 * @return True if at end, false otherwise
944 */
945 [[nodiscard]] constexpr bool IsAtEnd() const
946 noexcept(noexcept(std::declval<const Iter&>() ==
947 std::declval<const Iter&>())) {
948 return remaining_ == 0 || current_ == end_;
949 }
950
951 [[nodiscard]] constexpr TakeAdapter begin() const
952 noexcept(std::is_nothrow_copy_constructible_v<TakeAdapter>) {
953 return *this;
954 }
955
956 [[nodiscard]] constexpr TakeAdapter end() const
957 noexcept(std::is_nothrow_copy_constructible_v<TakeAdapter>);
958
959private:
960 Iter begin_; ///< Start of the iterator range
961 Iter current_; ///< Current position in the iteration
962 Iter end_; ///< End of the iterator range
963 size_t initial_count_ = 0; ///< Initial count limit
964 size_t remaining_ = 0; ///< Remaining elements to yield
965};
966
967template <ExternalRange R>
968TakeAdapter(R&, size_t) -> TakeAdapter<std::ranges::iterator_t<R>>;
969
970template <ExternalRange R>
971TakeAdapter(const R&, size_t) -> TakeAdapter<std::ranges::iterator_t<const R>>;
972
973template <typename Iter>
974 requires TakeAdapterRequirements<Iter>
975constexpr auto TakeAdapter<Iter>::operator++() noexcept(
976 noexcept(++std::declval<Iter&>())) -> TakeAdapter& {
977 if (remaining_ > 0 && current_ != end_) {
978 ++current_;
979 --remaining_;
980 }
981 return *this;
982}
983
984template <typename Iter>
986constexpr auto TakeAdapter<Iter>::operator++(int) noexcept(
987 std::is_nothrow_copy_constructible_v<TakeAdapter> &&
988 noexcept(++std::declval<TakeAdapter&>())) -> TakeAdapter {
989 auto temp = *this;
990 ++(*this);
991 return temp;
992}
993
994template <typename Iter>
996constexpr bool TakeAdapter<Iter>::operator==(const TakeAdapter& other) const
997 noexcept(noexcept(std::declval<const Iter&>() ==
998 std::declval<const Iter&>())) {
999 // Both are at end if either has no remaining elements or both iterators are
1000 // at end
1001 const bool this_at_end = (remaining_ == 0) || (current_ == end_);
1002 const bool other_at_end =
1003 (other.remaining_ == 0) || (other.current_ == other.end_);
1004
1005 if (this_at_end && other_at_end) {
1006 return true;
1007 }
1008
1009 return (current_ == other.current_) && (remaining_ == other.remaining_);
1010}
1011
1012template <typename Iter>
1014constexpr auto TakeAdapter<Iter>::end() const
1015 noexcept(std::is_nothrow_copy_constructible_v<TakeAdapter>) -> TakeAdapter {
1016 auto end_iter = *this;
1017 end_iter.remaining_ = 0;
1018 return end_iter;
1019}
1020
1021/**
1022 * @brief Iterator adapter that skips the first N elements.
1023 * @details This adapter skips over the first N elements of the underlying
1024 * iterator and yields all remaining elements. If the iterator has fewer than N
1025 * elements, the result will be empty.
1026 * @note This adapter maintains forward iterator semantics.
1027 * @note The adapter is constexpr-compatible for compile-time evaluation.
1028 * @tparam Iter Underlying iterator type that satisfies IteratorLike concept
1029 *
1030 * @code
1031 * // Skip the first 10 entities
1032 * auto remaining = query.Skip(10);
1033 * @endcode
1034 */
1035template <typename Iter>
1037class SkipAdapter final : public FunctionalAdapterBase<SkipAdapter<Iter>> {
1038public:
1041 using value_type = std::iter_value_t<Iter>;
1043 using pointer = void;
1044 using difference_type = std::iter_difference_t<Iter>;
1045
1046 /**
1047 * @brief Constructs a skip adapter with the given iterator range and count.
1048 * @param begin Start of the iterator range
1049 * @param end End of the iterator range
1050 * @param count Number of elements to skip
1051 */
1052 constexpr SkipAdapter(Iter begin, Iter end, size_t count) noexcept(
1053 std::is_nothrow_move_constructible_v<Iter> &&
1054 noexcept(++std::declval<Iter&>()));
1055
1056 /**
1057 * @brief Constructs a skip adapter from a range and count.
1058 * @tparam R The type of the input range
1059 * @param range The input range to adapt
1060 * @param count Number of elements to skip
1061 */
1062 template <ExternalRange R>
1064 constexpr SkipAdapter(R& range, size_t count) noexcept(noexcept(
1065 SkipAdapter(std::ranges::begin(range), std::ranges::end(range), count)))
1066 : SkipAdapter(std::ranges::begin(range), std::ranges::end(range), count) {
1067 }
1068
1069 /**
1070 * @brief Constructs a skip adapter from a const range and count.
1071 * @tparam R The type of the input range
1072 * @param range The input range to adapt
1073 * @param count Number of elements to skip
1074 */
1075 template <ExternalRange R>
1077 constexpr SkipAdapter(const R& range, size_t count) noexcept(noexcept(
1078 SkipAdapter(std::ranges::cbegin(range), std::ranges::cend(range), count)))
1079 : SkipAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
1080 count) {}
1081
1082 constexpr SkipAdapter(const SkipAdapter&) noexcept(
1083 std::is_nothrow_copy_constructible_v<Iter>) = default;
1084 constexpr SkipAdapter(SkipAdapter&&) noexcept(
1085 std::is_nothrow_move_constructible_v<Iter>) = default;
1086 constexpr ~SkipAdapter() noexcept(std::is_nothrow_destructible_v<Iter>) =
1087 default;
1088
1089 constexpr SkipAdapter& operator=(const SkipAdapter&) noexcept(
1090 std::is_nothrow_copy_assignable_v<Iter>) = default;
1091 constexpr SkipAdapter& operator=(SkipAdapter&&) noexcept(
1092 std::is_nothrow_move_assignable_v<Iter>) = default;
1093
1094 constexpr SkipAdapter& operator++() noexcept(
1095 noexcept(++std::declval<Iter&>()));
1096 [[nodiscard]] constexpr SkipAdapter operator++(int) noexcept(
1097 std::is_nothrow_copy_constructible_v<SkipAdapter> &&
1098 noexcept(++std::declval<SkipAdapter&>()));
1099
1100 [[nodiscard]] constexpr reference operator*() const
1101 noexcept(noexcept(*std::declval<const Iter&>())) {
1102 return *current_;
1103 }
1104
1105 pointer operator->() const = delete;
1106
1107 [[nodiscard]] constexpr bool operator==(const SkipAdapter& other) const
1108 noexcept(noexcept(std::declval<const Iter&>() ==
1109 std::declval<const Iter&>())) {
1110 return current_ == other.current_;
1111 }
1112
1113 [[nodiscard]] constexpr bool operator!=(const SkipAdapter& other) const
1114 noexcept(noexcept(std::declval<const SkipAdapter&>() ==
1115 std::declval<const SkipAdapter&>())) {
1116 return !(*this == other);
1117 }
1118
1119 [[nodiscard]] constexpr SkipAdapter begin() const
1120 noexcept(std::is_nothrow_copy_constructible_v<SkipAdapter>) {
1121 return *this;
1122 }
1123
1124 [[nodiscard]] constexpr SkipAdapter end() const
1125 noexcept(std::is_nothrow_constructible_v<SkipAdapter, const Iter&,
1126 const Iter&, size_t>) {
1127 return {end_, end_, 0};
1128 }
1129
1130private:
1131 Iter current_; ///< Current position in the iteration
1132 Iter end_; ///< End of the iterator range
1133};
1134
1135template <ExternalRange R>
1137
1138template <ExternalRange R>
1140
1141template <typename Iter>
1144 Iter begin, Iter end,
1145 size_t count) noexcept(std::is_nothrow_move_constructible_v<Iter> &&
1146 noexcept(++std::declval<Iter&>()))
1147 : current_(std::move(begin)), end_(std::move(end)) {
1148 for (size_t idx = 0; idx < count && current_ != end_; ++idx) {
1149 ++current_;
1150 }
1151}
1152
1153template <typename Iter>
1155constexpr auto SkipAdapter<Iter>::operator++() noexcept(
1156 noexcept(++std::declval<Iter&>())) -> SkipAdapter& {
1157 ++current_;
1158 return *this;
1159}
1160
1161template <typename Iter>
1163constexpr auto SkipAdapter<Iter>::operator++(int) noexcept(
1164 std::is_nothrow_copy_constructible_v<SkipAdapter> &&
1165 noexcept(++std::declval<SkipAdapter&>())) -> SkipAdapter {
1166 auto temp = *this;
1167 ++(*this);
1168 return temp;
1169}
1170
1171/**
1172 * @brief Iterator adapter that takes elements while a predicate returns true.
1173 * @details This adapter yields elements as long as the predicate returns true.
1174 * Once the predicate returns false, no more elements are yielded.
1175 * @tparam Iter Underlying iterator type
1176 * @tparam Pred Predicate function type
1177 *
1178 * @code
1179 * // Take entities while their health is above zero
1180 * auto alive_entities = query.TakeWhile([](const Health& h) { return h.points >
1181 * 0; });
1182 * @endcode
1183 */
1184template <typename Iter, typename Pred>
1187 : public FunctionalAdapterBase<TakeWhileAdapter<Iter, Pred>> {
1188public:
1191 using value_type = std::iter_value_t<Iter>;
1193 using pointer = void;
1194 using difference_type = std::iter_difference_t<Iter>;
1195
1196 /**
1197 * @brief Constructs a take-while adapter with the given iterator range and
1198 * predicate.
1199 * @param begin Start of the iterator range
1200 * @param end End of the iterator range
1201 * @param predicate Function to test elements
1202 */
1203 constexpr TakeWhileAdapter(Iter begin, Iter end, Pred predicate) noexcept(
1204 std::is_nothrow_move_constructible_v<Iter> &&
1205 std::is_nothrow_copy_constructible_v<Iter> &&
1206 std::is_nothrow_move_constructible_v<Pred> && noexcept(CheckPredicate()))
1207 : begin_(std::move(begin)),
1208 current_(begin_),
1209 end_(std::move(end)),
1210 predicate_(std::move(predicate)) {
1211 CheckPredicate();
1212 }
1213
1214 /**
1215 * @brief Constructs a take-while adapter from a range and predicate.
1216 * @tparam R The type of the input range
1217 * @param range The input range to adapt
1218 * @param predicate Function to test elements
1219 */
1220 template <ExternalRange R>
1222 constexpr TakeWhileAdapter(R& range, Pred predicate) noexcept(
1223 noexcept(TakeWhileAdapter(std::ranges::begin(range),
1224 std::ranges::end(range), std::move(predicate))))
1225 : TakeWhileAdapter(std::ranges::begin(range), std::ranges::end(range),
1226 std::move(predicate)) {}
1227
1228 /**
1229 * @brief Constructs a take-while adapter from a const range and predicate.
1230 * @tparam R The type of the input range
1231 * @param range The input range to adapt
1232 * @param predicate Function to test elements
1233 */
1234 template <ExternalRange R>
1236 Pred>
1237 constexpr TakeWhileAdapter(const R& range, Pred predicate) noexcept(noexcept(
1238 TakeWhileAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
1239 std::move(predicate))))
1240 : TakeWhileAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
1241 std::move(predicate)) {}
1242
1243 constexpr TakeWhileAdapter(const TakeWhileAdapter&) noexcept(
1244 std::is_nothrow_copy_constructible_v<Iter> &&
1245 std::is_nothrow_copy_constructible_v<Pred>) = default;
1246 constexpr TakeWhileAdapter(TakeWhileAdapter&&) noexcept(
1247 std::is_nothrow_move_constructible_v<Iter> &&
1248 std::is_nothrow_move_constructible_v<Pred>) = default;
1249 constexpr ~TakeWhileAdapter() noexcept(std::is_nothrow_destructible_v<Iter> &&
1250 std::is_nothrow_destructible_v<Pred>) =
1251 default;
1252
1253 constexpr TakeWhileAdapter& operator=(const TakeWhileAdapter&) noexcept(
1254 std::is_nothrow_copy_assignable_v<Iter> &&
1255 std::is_nothrow_copy_assignable_v<Pred>) = default;
1256 constexpr TakeWhileAdapter& operator=(TakeWhileAdapter&&) noexcept(
1257 std::is_nothrow_move_assignable_v<Iter> &&
1258 std::is_nothrow_move_assignable_v<Pred>) = default;
1259
1260 constexpr TakeWhileAdapter& operator++() noexcept(
1261 noexcept(++std::declval<Iter&>()) &&
1262 noexcept(std::declval<Iter&>() != std::declval<Iter&>()) &&
1263 noexcept(CheckPredicate()));
1264 [[nodiscard]] constexpr TakeWhileAdapter operator++(int) noexcept(
1265 std::is_nothrow_copy_constructible_v<TakeWhileAdapter> &&
1266 noexcept(++std::declval<TakeWhileAdapter&>()));
1267
1268 [[nodiscard]] constexpr reference operator*() const
1269 noexcept(noexcept(*std::declval<const Iter&>())) {
1270 return *current_;
1271 }
1272
1273 pointer operator->() const = delete;
1274
1275 [[nodiscard]] constexpr bool operator==(const TakeWhileAdapter& other) const
1276 noexcept(noexcept(std::declval<const Iter&>() ==
1277 std::declval<const Iter&>())) {
1278 return (stopped_ && other.stopped_) ||
1279 (current_ == other.current_ && stopped_ == other.stopped_);
1280 }
1281
1282 [[nodiscard]] constexpr bool operator!=(const TakeWhileAdapter& other) const
1283 noexcept(noexcept(std::declval<const TakeWhileAdapter&>() ==
1284 std::declval<const TakeWhileAdapter&>())) {
1285 return !(*this == other);
1286 }
1287
1288 /**
1289 * @brief Checks if the iterator has reached the end.
1290 * @return True if at end, false otherwise
1291 */
1292 [[nodiscard]] constexpr bool IsAtEnd() const
1293 noexcept(noexcept(std::declval<const Iter&>() ==
1294 std::declval<const Iter&>())) {
1295 return stopped_ || current_ == end_;
1296 }
1297
1298 [[nodiscard]] constexpr TakeWhileAdapter begin() const
1299 noexcept(std::is_nothrow_copy_constructible_v<TakeWhileAdapter>) {
1300 return *this;
1301 }
1302
1303 [[nodiscard]] constexpr TakeWhileAdapter end() const
1304 noexcept(std::is_nothrow_copy_constructible_v<TakeWhileAdapter>);
1305
1306private:
1307 constexpr void CheckPredicate() noexcept(
1308 std::is_nothrow_invocable_v<Pred, std::iter_value_t<Iter>> &&
1309 noexcept(std::declval<Iter&>() == std::declval<Iter&>()) &&
1310 noexcept(*std::declval<Iter&>()));
1311
1312 Iter begin_; ///< Start of the iterator range
1313 Iter current_; ///< Current position in the iteration
1314 Iter end_; ///< End of the iterator range
1315 Pred predicate_; ///< Predicate function
1316 bool stopped_ = false; ///< Whether predicate has failed
1317};
1318
1319template <ExternalRange R, typename Pred>
1321 -> TakeWhileAdapter<std::ranges::iterator_t<R>, Pred>;
1322
1323template <ExternalRange R, typename Pred>
1324TakeWhileAdapter(const R&, Pred)
1325 -> TakeWhileAdapter<std::ranges::iterator_t<const R>, Pred>;
1326
1327template <typename Iter, typename Pred>
1328 requires TakeWhileAdapterRequirements<Iter, Pred>
1329constexpr auto TakeWhileAdapter<Iter, Pred>::operator++() noexcept(
1330 noexcept(++std::declval<Iter&>()) &&
1331 noexcept(std::declval<Iter&>() != std::declval<Iter&>()) &&
1332 noexcept(CheckPredicate())) -> TakeWhileAdapter& {
1333 if (!stopped_ && current_ != end_) {
1334 ++current_;
1335 CheckPredicate();
1336 }
1337 return *this;
1338}
1339
1340template <typename Iter, typename Pred>
1342constexpr auto TakeWhileAdapter<Iter, Pred>::operator++(int) noexcept(
1343 std::is_nothrow_copy_constructible_v<TakeWhileAdapter> &&
1344 noexcept(++std::declval<TakeWhileAdapter&>())) -> TakeWhileAdapter {
1345 auto temp = *this;
1346 ++(*this);
1347 return temp;
1348}
1349
1350template <typename Iter, typename Pred>
1353 noexcept(std::is_nothrow_copy_constructible_v<TakeWhileAdapter>)
1354 -> TakeWhileAdapter {
1355 auto end_iter = *this;
1356 end_iter.stopped_ = true;
1357 return end_iter;
1358}
1359
1360template <typename Iter, typename Pred>
1362constexpr void TakeWhileAdapter<Iter, Pred>::CheckPredicate() noexcept(
1363 std::is_nothrow_invocable_v<Pred, std::iter_value_t<Iter>> &&
1364 noexcept(std::declval<Iter&>() == std::declval<Iter&>()) &&
1365 noexcept(*std::declval<Iter&>())) {
1366 if (stopped_ || current_ == end_) {
1367 stopped_ = true;
1368 return;
1369 }
1370
1371 auto value = *current_;
1372 bool matches = false;
1373 if constexpr (std::invocable<Pred, decltype(value)>) {
1374 matches = predicate_(value);
1375 } else {
1376 matches = std::apply(predicate_, value);
1377 }
1378 if (!matches) {
1379 stopped_ = true;
1380 }
1381}
1382
1383/**
1384 * @brief Iterator adapter that skips elements while a predicate returns true.
1385 * @tparam Iter Underlying iterator type
1386 * @tparam Pred Predicate function type
1387 *
1388 * @code
1389 * // Skip entities while their health is zero
1390 * auto non_zero_health = query.SkipWhile([](const Health& h) { return h.points
1391 * == 0; });
1392 * @endcode
1393 */
1394template <typename Iter, typename Pred>
1395 requires SkipWhileAdapterRequirements<Iter, Pred>
1397 : public FunctionalAdapterBase<SkipWhileAdapter<Iter, Pred>> {
1398public:
1401 using value_type = std::iter_value_t<Iter>;
1403 using pointer = void;
1404 using difference_type = std::iter_difference_t<Iter>;
1405
1406 /**
1407 * @brief Constructs a skip-while adapter with the given iterator range and
1408 * predicate.
1409 * @param begin Start of the iterator range
1410 * @param end End of the iterator range
1411 * @param predicate Function to test elements
1412 */
1413 constexpr SkipWhileAdapter(Iter begin, Iter end, Pred predicate) noexcept(
1414 std::is_nothrow_move_constructible_v<Iter> &&
1415 std::is_nothrow_move_constructible_v<Pred> &&
1416 noexcept(AdvancePastSkipped()))
1417 : current_(std::move(begin)),
1418 end_(std::move(end)),
1419 predicate_(std::move(predicate)) {
1420 AdvancePastSkipped();
1421 }
1422
1423 /**
1424 * @brief Constructs a skip-while adapter from a range and predicate.
1425 * @tparam R The type of the input range
1426 * @param range The input range to adapt
1427 * @param predicate Function to test elements
1428 */
1429 template <ExternalRange R>
1431 constexpr SkipWhileAdapter(R& range, Pred predicate) noexcept(
1432 noexcept(SkipWhileAdapter(std::ranges::begin(range),
1433 std::ranges::end(range), std::move(predicate))))
1434 : SkipWhileAdapter(std::ranges::begin(range), std::ranges::end(range),
1435 std::move(predicate)) {}
1436
1437 /**
1438 * @brief Constructs a skip-while adapter from a const range and predicate.
1439 * @tparam R The type of the input range
1440 * @param range The input range to adapt
1441 * @param predicate Function to test elements
1442 */
1443 template <ExternalRange R>
1445 Pred>
1446 constexpr SkipWhileAdapter(const R& range, Pred predicate) noexcept(noexcept(
1447 SkipWhileAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
1448 std::move(predicate))))
1449 : SkipWhileAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
1450 std::move(predicate)) {}
1451
1452 constexpr SkipWhileAdapter(const SkipWhileAdapter&) noexcept(
1453 std::is_nothrow_copy_constructible_v<Iter> &&
1454 std::is_nothrow_copy_constructible_v<Pred>) = default;
1455 constexpr SkipWhileAdapter(SkipWhileAdapter&&) noexcept(
1456 std::is_nothrow_move_constructible_v<Iter> &&
1457 std::is_nothrow_move_constructible_v<Pred>) = default;
1458 constexpr ~SkipWhileAdapter() noexcept(std::is_nothrow_destructible_v<Iter> &&
1459 std::is_nothrow_destructible_v<Pred>) =
1460 default;
1461
1462 constexpr SkipWhileAdapter& operator=(const SkipWhileAdapter&) noexcept(
1463 std::is_nothrow_copy_assignable_v<Iter> &&
1464 std::is_nothrow_copy_assignable_v<Pred>) = default;
1465 constexpr SkipWhileAdapter& operator=(SkipWhileAdapter&&) noexcept(
1466 std::is_nothrow_move_assignable_v<Iter> &&
1467 std::is_nothrow_move_assignable_v<Pred>) = default;
1468
1469 constexpr SkipWhileAdapter& operator++() noexcept(
1470 noexcept(++std::declval<Iter&>()));
1471 [[nodiscard]] constexpr SkipWhileAdapter operator++(int) noexcept(
1472 std::is_nothrow_copy_constructible_v<SkipWhileAdapter> &&
1473 noexcept(++std::declval<SkipWhileAdapter&>()));
1474
1475 [[nodiscard]] constexpr reference operator*() const
1476 noexcept(noexcept(*std::declval<const Iter&>())) {
1477 return *current_;
1478 }
1479
1480 pointer operator->() const = delete;
1481
1482 [[nodiscard]] constexpr bool operator==(const SkipWhileAdapter& other) const
1483 noexcept(noexcept(std::declval<const Iter&>() ==
1484 std::declval<const Iter&>())) {
1485 return current_ == other.current_;
1486 }
1487
1488 [[nodiscard]] constexpr bool operator!=(const SkipWhileAdapter& other) const
1489 noexcept(noexcept(std::declval<const SkipWhileAdapter&>() ==
1490 std::declval<const SkipWhileAdapter&>())) {
1491 return !(*this == other);
1492 }
1493
1494 [[nodiscard]] constexpr SkipWhileAdapter begin() const
1495 noexcept(std::is_nothrow_copy_constructible_v<SkipWhileAdapter>) {
1496 return *this;
1497 }
1498
1499 [[nodiscard]] constexpr SkipWhileAdapter end() const
1500 noexcept(std::is_nothrow_constructible_v<SkipWhileAdapter, const Iter&,
1501 const Iter&, const Pred&>) {
1502 return {end_, end_, predicate_};
1503 }
1504
1505private:
1506 constexpr void AdvancePastSkipped() noexcept(
1507 std::is_nothrow_invocable_v<Pred, std::iter_value_t<Iter>> &&
1508 noexcept(std::declval<Iter&>() != std::declval<Iter&>()) &&
1509 noexcept(*std::declval<Iter&>()) && noexcept(++std::declval<Iter&>()));
1510
1511 Iter current_; ///< Current position in the iteration
1512 Iter end_; ///< End of the iterator range
1513 Pred predicate_; ///< Predicate function
1514};
1515
1516template <ExternalRange R, typename Pred>
1518 -> SkipWhileAdapter<std::ranges::iterator_t<R>, Pred>;
1519
1520template <ExternalRange R, typename Pred>
1521SkipWhileAdapter(const R&, Pred)
1522 -> SkipWhileAdapter<std::ranges::iterator_t<const R>, Pred>;
1523
1524template <typename Iter, typename Pred>
1525 requires SkipWhileAdapterRequirements<Iter, Pred>
1526constexpr auto SkipWhileAdapter<Iter, Pred>::operator++() noexcept(
1527 noexcept(++std::declval<Iter&>())) -> SkipWhileAdapter& {
1528 ++current_;
1529 return *this;
1530}
1531
1532template <typename Iter, typename Pred>
1534constexpr auto SkipWhileAdapter<Iter, Pred>::operator++(int) noexcept(
1535 std::is_nothrow_copy_constructible_v<SkipWhileAdapter> &&
1536 noexcept(++std::declval<SkipWhileAdapter&>())) -> SkipWhileAdapter {
1537 auto temp = *this;
1538 ++(*this);
1539 return temp;
1540}
1541
1542template <typename Iter, typename Pred>
1544constexpr void SkipWhileAdapter<Iter, Pred>::AdvancePastSkipped() noexcept(
1545 std::is_nothrow_invocable_v<Pred, std::iter_value_t<Iter>> &&
1546 noexcept(std::declval<Iter&>() != std::declval<Iter&>()) &&
1547 noexcept(*std::declval<Iter&>()) && noexcept(++std::declval<Iter&>())) {
1548 while (current_ != end_) {
1549 auto value = *current_;
1550 bool matches = false;
1551 if constexpr (std::invocable<Pred, decltype(value)>) {
1552 matches = predicate_(value);
1553 } else {
1554 matches = std::apply(predicate_, value);
1555 }
1556 if (!matches) {
1557 break;
1558 }
1559 ++current_;
1560 }
1561}
1562
1563/**
1564 * @brief Iterator adapter that adds index information to each element.
1565 * @details This adapter yields tuples of (index, value) where index starts at 0
1566 * and increments for each element.
1567 * @tparam Iter Underlying iterator type
1568 *
1569 * @code
1570 * // Enumerate entities with their indices
1571 * auto enumerated = query.Enumerate();
1572 * for (const auto& [index, _] : enumerated) {
1573 * // Use index and components
1574 * }
1575 * @endcode
1576 */
1577template <typename Iter>
1578 requires EnumerateAdapterRequirements<Iter>
1580 : public FunctionalAdapterBase<EnumerateAdapter<Iter>> {
1581private:
1582 template <typename T>
1583 struct MakeEnumeratedValue {
1584 using type = std::tuple<size_t, T>;
1585 };
1586
1587 template <typename... Args>
1588 struct MakeEnumeratedValue<std::tuple<Args...>> {
1589 using type = std::tuple<size_t, Args...>;
1590 };
1591
1592public:
1596 typename MakeEnumeratedValue<std::iter_value_t<Iter>>::type;
1598 using pointer = void;
1599 using difference_type = std::iter_difference_t<Iter>;
1600
1601 /**
1602 * @brief Constructs an enumerate adapter with the given iterator range.
1603 * @param begin Start of the iterator range
1604 * @param end End of the iterator range
1605 */
1606 constexpr EnumerateAdapter(Iter begin, Iter end) noexcept(
1607 std::is_nothrow_move_constructible_v<Iter> &&
1608 std::is_nothrow_copy_constructible_v<Iter>)
1609 : begin_(std::move(begin)), current_(begin_), end_(std::move(end)) {}
1610
1611 /**
1612 * @brief Constructs a enumerate adapter from a range.
1613 * @tparam R The type of the input range
1614 * @param range The input range to adapt
1615 */
1616 template <ExternalRange R>
1618 explicit constexpr EnumerateAdapter(R& range) noexcept(noexcept(
1619 EnumerateAdapter(std::ranges::begin(range), std::ranges::end(range))))
1620 : EnumerateAdapter(std::ranges::begin(range), std::ranges::end(range)) {}
1621
1622 /**
1623 * @brief Constructs a enumerate adapter from a const range.
1624 * @tparam R The type of the input range
1625 * @param range The input range to adapt
1626 */
1627 template <ExternalRange R>
1629 explicit constexpr EnumerateAdapter(const R& range) noexcept(noexcept(
1630 EnumerateAdapter(std::ranges::cbegin(range), std::ranges::cend(range))))
1631 : EnumerateAdapter(std::ranges::cbegin(range), std::ranges::cend(range)) {
1632 }
1633
1634 constexpr EnumerateAdapter(const EnumerateAdapter&) = default;
1635 constexpr EnumerateAdapter(EnumerateAdapter&&) = default;
1636 constexpr ~EnumerateAdapter() = default;
1637
1638 constexpr EnumerateAdapter& operator=(const EnumerateAdapter&) = default;
1640
1641 constexpr EnumerateAdapter& operator++() noexcept(
1642 noexcept(++std::declval<Iter&>()));
1643 [[nodiscard]] constexpr EnumerateAdapter operator++(int) noexcept(
1644 std::is_nothrow_copy_constructible_v<EnumerateAdapter> &&
1645 noexcept(++std::declval<EnumerateAdapter&>()));
1646
1647 [[nodiscard]] constexpr reference operator*() const;
1648
1649 pointer operator->() const = delete;
1650
1651 [[nodiscard]] constexpr bool operator==(const EnumerateAdapter& other) const
1652 noexcept(noexcept(std::declval<const Iter&>() ==
1653 std::declval<const Iter&>())) {
1654 return current_ == other.current_;
1655 }
1656
1657 [[nodiscard]] constexpr bool operator!=(const EnumerateAdapter& other) const
1658 noexcept(noexcept(std::declval<const EnumerateAdapter&>() ==
1659 std::declval<const EnumerateAdapter&>())) {
1660 return !(*this == other);
1661 }
1662
1663 [[nodiscard]] constexpr EnumerateAdapter begin() const
1664 noexcept(std::is_nothrow_copy_constructible_v<EnumerateAdapter>) {
1665 return *this;
1666 }
1667
1668 [[nodiscard]] constexpr EnumerateAdapter end() const
1669 noexcept(std::is_nothrow_constructible_v<EnumerateAdapter, const Iter&,
1670 const Iter&>) {
1671 return {end_, end_};
1672 }
1673
1674private:
1675 Iter begin_; ///< Start of the iterator range
1676 Iter current_; ///< Current position in the iteration
1677 Iter end_; ///< End of the iterator range
1678 size_t index_ = 0; ///< Current index in the enumeration
1679};
1680
1681template <ExternalRange R>
1683
1684template <ExternalRange R>
1687
1688template <typename Iter>
1690constexpr auto EnumerateAdapter<Iter>::operator++() noexcept(
1691 noexcept(++std::declval<Iter&>())) -> EnumerateAdapter& {
1692 ++current_;
1693 ++index_;
1694 return *this;
1695}
1696
1697template <typename Iter>
1699constexpr auto EnumerateAdapter<Iter>::operator++(int) noexcept(
1700 std::is_nothrow_copy_constructible_v<EnumerateAdapter> &&
1701 noexcept(++std::declval<EnumerateAdapter&>())) -> EnumerateAdapter {
1702 auto temp = *this;
1703 ++(*this);
1704 return temp;
1705}
1706
1707template <typename Iter>
1710 auto value = *current_;
1711#if defined(_MSC_VER)
1712#pragma warning(push)
1713#pragma warning(disable : 4702)
1714#endif
1715 if constexpr (details::TupleLike<decltype(value)>) {
1716 return std::tuple_cat(std::tuple{index_}, value);
1717 } else {
1718 return std::tuple{index_, value};
1719 }
1720#if defined(_MSC_VER)
1721#pragma warning(pop)
1722#endif
1723}
1724
1725/**
1726 * @brief Iterator adapter that applies a function to each element for
1727 * observation.
1728 * @details This adapter allows observing elements without modifying them.
1729 * The inspector function is called for side effects only.
1730 * @tparam Iter Underlying iterator type
1731 * @tparam Func Inspector function type
1732 *
1733 * @code
1734 * // Inspect entities to log their IDs
1735 * auto inspected = query.Inspect([](const Entity& e) { helios::log::Info("{}",
1736 * e.id);
1737 * });
1738 * @endcode
1739 */
1740template <typename Iter, typename Func>
1743 : public FunctionalAdapterBase<InspectAdapter<Iter, Func>> {
1744public:
1747 using value_type = std::iter_value_t<Iter>;
1749 using pointer = void;
1750 using difference_type = std::iter_difference_t<Iter>;
1751
1752 /**
1753 * @brief Constructs an inspect adapter with the given iterator range and
1754 * inspector function.
1755 * @param begin Start of the iterator range
1756 * @param end End of the iterator range
1757 * @param inspector Function to call for each element
1758 */
1759 constexpr InspectAdapter(Iter begin, Iter end, Func inspector) noexcept(
1760 std::is_nothrow_move_constructible_v<Iter> &&
1761 std::is_nothrow_copy_constructible_v<Iter> &&
1762 std::is_nothrow_move_constructible_v<Func>)
1763 : begin_(std::move(begin)),
1764 current_(begin_),
1765 end_(std::move(end)),
1766 inspector_(std::move(inspector)) {}
1767
1768 /**
1769 * @brief Constructs an inspect adapter with the given range and inspector
1770 * function.
1771 * @tparam R The type of the input range
1772 * @param range The input range to adapt
1773 * @param inspector Function to call for each element
1774 */
1775 template <ExternalRange R>
1777 constexpr InspectAdapter(R& range, Func inspector) noexcept(
1778 noexcept(InspectAdapter(std::ranges::begin(range),
1779 std::ranges::end(range), std::move(inspector))))
1780 : InspectAdapter(std::ranges::begin(range), std::ranges::end(range),
1781 std::move(inspector)) {}
1782
1783 /**
1784 * @brief Constructs an inspect adapter with the given const range and
1785 * inspector function.
1786 * @tparam R The type of the input range
1787 * @param range The input range to adapt
1788 * @param inspector Function to call for each element
1789 */
1790 template <ExternalRange R>
1792 constexpr InspectAdapter(const R& range, Func inspector) noexcept(
1793 noexcept(InspectAdapter(std::ranges::cbegin(range),
1794 std::ranges::cend(range), std::move(inspector))))
1795 : InspectAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
1796 std::move(inspector)) {}
1797
1798 constexpr InspectAdapter(const InspectAdapter&) noexcept(
1799 std::is_nothrow_copy_constructible_v<Iter> &&
1800 std::is_nothrow_copy_constructible_v<Func>) = default;
1801 constexpr InspectAdapter(InspectAdapter&&) noexcept(
1802 std::is_nothrow_move_constructible_v<Iter> &&
1803 std::is_nothrow_move_constructible_v<Func>) = default;
1804 constexpr ~InspectAdapter() noexcept(std::is_nothrow_destructible_v<Iter> &&
1805 std::is_nothrow_destructible_v<Func>) =
1806 default;
1807
1808 constexpr InspectAdapter& operator=(const InspectAdapter&) noexcept(
1809 std::is_nothrow_copy_assignable_v<Iter> &&
1810 std::is_nothrow_copy_assignable_v<Func>) = default;
1811 constexpr InspectAdapter& operator=(InspectAdapter&&) noexcept(
1812 std::is_nothrow_move_assignable_v<Iter> &&
1813 std::is_nothrow_move_assignable_v<Func>) = default;
1814
1815 constexpr InspectAdapter& operator++() noexcept(
1816 noexcept(++std::declval<Iter&>()));
1817 [[nodiscard]] constexpr InspectAdapter operator++(int) noexcept(
1818 std::is_nothrow_copy_constructible_v<InspectAdapter> &&
1819 noexcept(++std::declval<InspectAdapter&>()));
1820
1821 [[nodiscard]] constexpr reference operator*() const
1822 noexcept(std::is_nothrow_invocable_v<Func, std::iter_value_t<Iter>> &&
1823 noexcept(*std::declval<const Iter&>()));
1824
1825 pointer operator->() const = delete;
1826
1827 [[nodiscard]] constexpr bool operator==(const InspectAdapter& other) const
1828 noexcept(noexcept(std::declval<const Iter&>() ==
1829 std::declval<const Iter&>())) {
1830 return current_ == other.current_;
1831 }
1832
1833 [[nodiscard]] constexpr bool operator!=(const InspectAdapter& other) const
1834 noexcept(noexcept(std::declval<const InspectAdapter&>() ==
1835 std::declval<const InspectAdapter&>())) {
1836 return !(*this == other);
1837 }
1838
1839 [[nodiscard]] constexpr InspectAdapter begin() const
1840 noexcept(std::is_nothrow_copy_constructible_v<InspectAdapter>) {
1841 return *this;
1842 }
1843
1844 [[nodiscard]] constexpr InspectAdapter end() const
1845 noexcept(std::is_nothrow_constructible_v<InspectAdapter, const Iter&,
1846 const Iter&, const Func&>) {
1847 return {end_, end_, inspector_};
1848 }
1849
1850private:
1851 Iter begin_; ///< Start of the iterator range
1852 Iter current_; ///< Current position in the iteration
1853 Iter end_; ///< End of the iterator range
1854 Func inspector_; ///< Inspector function
1855};
1856
1857template <ExternalRange R, typename Func>
1859
1860template <ExternalRange R, typename Func>
1861InspectAdapter(const R&, Func)
1863
1864template <typename Iter, typename Func>
1867 noexcept(++std::declval<Iter&>())) -> InspectAdapter& {
1868 ++current_;
1869 return *this;
1870}
1871
1872template <typename Iter, typename Func>
1874constexpr auto InspectAdapter<Iter, Func>::operator++(int) noexcept(
1875 std::is_nothrow_copy_constructible_v<InspectAdapter> &&
1876 noexcept(++std::declval<InspectAdapter&>())) -> InspectAdapter {
1877 auto temp = *this;
1878 ++(*this);
1879 return temp;
1880}
1881
1882template <typename Iter, typename Func>
1885 noexcept(std::is_nothrow_invocable_v<Func, std::iter_value_t<Iter>> &&
1886 noexcept(*std::declval<const Iter&>())) -> reference {
1887 auto value = *current_;
1888 if constexpr (std::invocable<Func, decltype(value)>) {
1889 inspector_(value);
1890 } else {
1891 std::apply(inspector_, value);
1892 }
1893 return value;
1894}
1895
1896/**
1897 * @brief Iterator adapter that steps through elements by a specified stride.
1898 * @details This adapter yields every step-th element from the underlying
1899 * iterator.
1900 * @tparam Iter Underlying iterator type
1901 *
1902 * @code
1903 * // Step through entities by 3
1904 * auto stepped = query.StepBy(3);
1905 * @endcode
1906 */
1907template <typename Iter>
1909class StepByAdapter final : public FunctionalAdapterBase<StepByAdapter<Iter>> {
1910public:
1913 using value_type = std::iter_value_t<Iter>;
1915 using pointer = void;
1916 using difference_type = std::iter_difference_t<Iter>;
1917
1918 /**
1919 * @brief Constructs a step-by adapter with the given iterator range and step
1920 * size.
1921 * @param begin Start of the iterator range
1922 * @param end End of the iterator range
1923 * @param step Number of elements to step by
1924 */
1925 constexpr StepByAdapter(Iter begin, Iter end, size_t step) noexcept(
1926 std::is_nothrow_move_constructible_v<Iter> &&
1927 std::is_nothrow_copy_constructible_v<Iter>)
1928 : begin_(std::move(begin)),
1929 current_(begin_),
1930 end_(std::move(end)),
1931 step_(step > 0 ? step : 1) {}
1932
1933 /**
1934 * @brief Constructs a step-by adapter with the given range and step size.
1935 * @tparam R The type of the input range
1936 * @param range The input range to adapt
1937 * @param step Number of elements to step by
1938 */
1939 template <ExternalRange R>
1941 constexpr StepByAdapter(R& range, size_t step) noexcept(noexcept(
1942 StepByAdapter(std::ranges::begin(range), std::ranges::end(range), step)))
1943 : StepByAdapter(std::ranges::begin(range), std::ranges::end(range),
1944 step) {}
1945
1946 /**
1947 * @brief Constructs a step-by adapter with the given const range and step
1948 * size.
1949 * @tparam R The type of the input range
1950 * @param range The input range to adapt
1951 * @param step Number of elements to step by
1952 */
1953 template <ExternalRange R>
1955 constexpr StepByAdapter(const R& range, size_t step) noexcept(
1956 noexcept(StepByAdapter(std::ranges::cbegin(range),
1957 std::ranges::cend(range), step)))
1958 : StepByAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
1959 step) {}
1960
1961 constexpr StepByAdapter(const StepByAdapter&) = default;
1962 constexpr StepByAdapter(StepByAdapter&&) = default;
1963 constexpr ~StepByAdapter() = default;
1964
1965 constexpr StepByAdapter& operator=(const StepByAdapter&) = default;
1966 constexpr StepByAdapter& operator=(StepByAdapter&&) = default;
1967
1968 constexpr StepByAdapter& operator++() noexcept(
1969 noexcept(++std::declval<Iter&>()) &&
1970 noexcept(std::declval<Iter&>() != std::declval<Iter&>()));
1971 [[nodiscard]] constexpr StepByAdapter operator++(int) noexcept(
1972 std::is_copy_constructible_v<StepByAdapter> &&
1973 noexcept(++std::declval<StepByAdapter&>()));
1974
1975 [[nodiscard]] constexpr reference operator*() const
1976 noexcept(noexcept(*std::declval<const Iter&>())) {
1977 return *current_;
1978 }
1979
1980 pointer operator->() const = delete;
1981
1982 [[nodiscard]] constexpr bool operator==(const StepByAdapter& other) const
1983 noexcept(noexcept(std::declval<const Iter&>() ==
1984 std::declval<const Iter&>())) {
1985 return current_ == other.current_;
1986 }
1987
1988 [[nodiscard]] constexpr bool operator!=(const StepByAdapter& other) const
1989 noexcept(noexcept(std::declval<const StepByAdapter&>() ==
1990 std::declval<const StepByAdapter&>())) {
1991 return !(*this == other);
1992 }
1993
1994 [[nodiscard]] constexpr StepByAdapter begin() const
1995 noexcept(std::is_nothrow_copy_constructible_v<StepByAdapter>) {
1996 return *this;
1997 }
1998
1999 [[nodiscard]] constexpr StepByAdapter end() const
2000 noexcept(std::is_nothrow_constructible_v<StepByAdapter, const Iter&,
2001 const Iter&, size_t>) {
2002 return {end_, end_, step_};
2003 }
2004
2005private:
2006 Iter begin_; ///< Start of the iterator range
2007 Iter current_; ///< Current position in the iteration
2008 Iter end_; ///< End of the iterator range
2009 size_t step_ = 0; ///< Step size between elements
2010};
2011
2012template <ExternalRange R>
2014
2015template <ExternalRange R>
2016StepByAdapter(const R&, size_t)
2018
2019template <typename Iter>
2021constexpr auto StepByAdapter<Iter>::operator++() noexcept(
2022 noexcept(++std::declval<Iter&>()) &&
2023 noexcept(std::declval<Iter&>() != std::declval<Iter&>()))
2024 -> StepByAdapter& {
2025 for (size_t i = 0; i < step_ && current_ != end_; ++i) {
2026 ++current_;
2027 }
2028 return *this;
2029}
2030
2031template <typename Iter>
2033constexpr auto StepByAdapter<Iter>::operator++(int) noexcept(
2034 std::is_copy_constructible_v<StepByAdapter> &&
2035 noexcept(++std::declval<StepByAdapter&>())) -> StepByAdapter {
2036 auto temp = *this;
2037 ++(*this);
2038 return temp;
2039}
2040
2041/**
2042 * @brief Iterator adapter that chains two sequences together.
2043 * @details This adapter yields all elements from the first iterator, then all
2044 * elements from the second iterator.
2045 * @tparam Iter1 First iterator type
2046 * @tparam Iter2 Second iterator type
2047 *
2048 * @code
2049 * // Chain two `Health` queries together
2050 * auto chained = query1.Chain(query2);
2051 * chained.ForEach([](const Health& h) {
2052 * // Process health components from both queries
2053 * });
2054 * @endcode
2055 */
2056template <typename Iter1, typename Iter2>
2058class ChainAdapter final
2059 : public FunctionalAdapterBase<ChainAdapter<Iter1, Iter2>> {
2060public:
2062 std::conditional_t<details::IterYieldsReference<Iter1> &&
2064 std::forward_iterator_tag, std::input_iterator_tag>;
2066 std::conditional_t<details::IterYieldsReference<Iter1> &&
2068 std::forward_iterator_tag, std::input_iterator_tag>;
2069
2070 using value_type = std::iter_value_t<Iter1>;
2072 using pointer = void;
2073 using difference_type = std::common_type_t<std::iter_difference_t<Iter1>,
2074 std::iter_difference_t<Iter2>>;
2075
2076 /**
2077 * @brief Constructs a chain adapter with two iterator ranges.
2078 * @param first_begin Start of the first iterator range
2079 * @param first_end End of the first iterator range
2080 * @param second_begin Start of the second iterator range
2081 * @param second_end End of the second iterator range
2082 */
2083 constexpr ChainAdapter(
2084 Iter1 first_begin, Iter1 first_end, Iter2 second_begin,
2085 Iter2 second_end) noexcept(std::is_nothrow_move_constructible_v<Iter1> &&
2086 std::is_nothrow_move_constructible_v<Iter2> &&
2087 noexcept(std::declval<Iter1&>() !=
2088 std::declval<Iter1&>()))
2089 : first_current_(std::move(first_begin)),
2090 first_end_(std::move(first_end)),
2091 second_current_(std::move(second_begin)),
2092 second_end_(std::move(second_end)),
2093 in_first_(first_current_ != first_end_) {}
2094
2095 /**
2096 * @brief Constructs a chain adapter with the given ranges.
2097 * @tparam R1 The type of the first range
2098 * @tparam R2 The type of the second range
2099 * @param first_range The first range to adapt
2100 * @param second_range The second range to adapt
2101 */
2102 template <ExternalRange R1, ExternalRange R2>
2104 std::ranges::iterator_t<R2>>
2105 constexpr ChainAdapter(R1& first_range, R2& second_range) noexcept(
2106 noexcept(ChainAdapter(std::ranges::begin(first_range),
2107 std::ranges::end(first_range),
2108 std::ranges::begin(second_range),
2109 std::ranges::end(second_range))))
2110 : ChainAdapter(
2111 std::ranges::begin(first_range), std::ranges::end(first_range),
2112 std::ranges::begin(second_range), std::ranges::end(second_range)) {}
2113
2114 /**
2115 * @brief Constructs a chain adapter with the given const ranges.
2116 * @tparam R1 The type of the first range
2117 * @tparam R2 The type of the second range
2118 * @param first_range The first range to adapt
2119 * @param second_range The second range to adapt
2120 */
2121 template <ExternalRange R1, ExternalRange R2>
2123 std::ranges::iterator_t<const R2>>
2124 constexpr ChainAdapter(
2125 const R1& first_range,
2126 const R2&
2127 second_range) noexcept(noexcept(ChainAdapter(std::ranges::
2128 cbegin(first_range),
2129 std::ranges::cend(
2130 first_range),
2131 std::ranges::cbegin(
2132 second_range),
2133 std::ranges::cend(
2134 second_range))))
2135 : ChainAdapter(std::ranges::cbegin(first_range),
2136 std::ranges::cend(first_range),
2137 std::ranges::cbegin(second_range),
2138 std::ranges::cend(second_range)) {}
2139
2140 constexpr ChainAdapter(const ChainAdapter&) noexcept(
2141 std::is_nothrow_copy_constructible_v<Iter1> &&
2142 std::is_nothrow_copy_constructible_v<Iter2>) = default;
2143 constexpr ChainAdapter(ChainAdapter&&) noexcept(
2144 std::is_nothrow_move_constructible_v<Iter1> &&
2145 std::is_nothrow_move_constructible_v<Iter2>) = default;
2146 constexpr ~ChainAdapter() noexcept(std::is_nothrow_destructible_v<Iter1> &&
2147 std::is_nothrow_destructible_v<Iter2>) =
2148 default;
2149
2150 constexpr ChainAdapter& operator=(const ChainAdapter&) noexcept(
2151 std::is_nothrow_copy_assignable_v<Iter1> &&
2152 std::is_nothrow_copy_assignable_v<Iter2>) = default;
2153 constexpr ChainAdapter& operator=(ChainAdapter&&) noexcept(
2154 std::is_nothrow_move_assignable_v<Iter1> &&
2155 std::is_nothrow_move_assignable_v<Iter2>) = default;
2156
2157 constexpr ChainAdapter& operator++() noexcept(
2158 noexcept(++std::declval<Iter1&>()) &&
2159 noexcept(++std::declval<Iter2&>()) &&
2160 noexcept(std::declval<Iter1&>() == std::declval<Iter1&>()) &&
2161 noexcept(std::declval<Iter2&>() != std::declval<Iter2&>()));
2162 [[nodiscard]] constexpr ChainAdapter operator++(int) noexcept(
2163 std::is_nothrow_copy_constructible_v<ChainAdapter> &&
2164 noexcept(++std::declval<ChainAdapter&>()));
2165
2166 [[nodiscard]] constexpr reference operator*() const
2167 noexcept(noexcept(*std::declval<const Iter1&>()) &&
2168 noexcept(*std::declval<const Iter2&>())) {
2169 return in_first_ ? *first_current_ : *second_current_;
2170 }
2171
2172 pointer operator->() const = delete;
2173
2174 [[nodiscard]] constexpr bool operator==(const ChainAdapter& other) const
2175 noexcept(noexcept(std::declval<const Iter1&>() ==
2176 std::declval<const Iter1&>()) &&
2177 noexcept(std::declval<const Iter2&>() ==
2178 std::declval<const Iter2&>()));
2179
2180 [[nodiscard]] constexpr bool operator!=(const ChainAdapter& other) const
2181 noexcept(noexcept(std::declval<const ChainAdapter&>() ==
2182 std::declval<const ChainAdapter&>())) {
2183 return !(*this == other);
2184 }
2185
2186 [[nodiscard]] constexpr ChainAdapter begin() const
2187 noexcept(std::is_nothrow_copy_constructible_v<ChainAdapter>) {
2188 return *this;
2189 }
2190
2191 [[nodiscard]] constexpr ChainAdapter end() const
2192 noexcept(std::is_nothrow_copy_constructible_v<ChainAdapter> &&
2193 std::is_nothrow_copy_constructible_v<Iter1> &&
2194 std::is_nothrow_copy_constructible_v<Iter2>);
2195
2196private:
2197 Iter1 first_current_; ///< Current position in first iterator
2198 Iter1 first_end_; ///< End of first iterator range
2199 Iter2 second_current_; ///< Current position in second iterator
2200 Iter2 second_end_; ///< End of second iterator range
2201 bool in_first_ = true; ///< Whether iterating through first range
2202};
2203
2204template <ExternalRange R1, ExternalRange R2>
2205 requires ChainAdapterRequirements<std::ranges::iterator_t<R1>,
2206 std::ranges::iterator_t<R2>>
2208 -> ChainAdapter<std::ranges::iterator_t<R1>, std::ranges::iterator_t<R2>>;
2209
2210template <ExternalRange R1, ExternalRange R2>
2211 requires ChainAdapterRequirements<std::ranges::iterator_t<const R1>,
2212 std::ranges::iterator_t<const R2>>
2213ChainAdapter(const R1&, const R2&)
2214 -> ChainAdapter<std::ranges::iterator_t<const R1>,
2215 std::ranges::iterator_t<const R2>>;
2216
2217template <typename Iter1, typename Iter2>
2218 requires ChainAdapterRequirements<Iter1, Iter2>
2219constexpr auto ChainAdapter<Iter1, Iter2>::operator++() noexcept(
2220 noexcept(++std::declval<Iter1&>()) && noexcept(++std::declval<Iter2&>()) &&
2221 noexcept(std::declval<Iter1&>() == std::declval<Iter1&>()) &&
2222 noexcept(std::declval<Iter2&>() != std::declval<Iter2&>()))
2223 -> ChainAdapter& {
2224 if (in_first_) {
2225 ++first_current_;
2226 if (first_current_ == first_end_) {
2227 in_first_ = false;
2228 }
2229 } else {
2230 if (second_current_ != second_end_) {
2231 ++second_current_;
2232 }
2233 }
2234 return *this;
2235}
2236
2237template <typename Iter1, typename Iter2>
2239constexpr auto ChainAdapter<Iter1, Iter2>::operator++(int) noexcept(
2240 std::is_nothrow_copy_constructible_v<ChainAdapter> &&
2241 noexcept(++std::declval<ChainAdapter&>())) -> ChainAdapter {
2242 auto temp = *this;
2243 ++(*this);
2244 return temp;
2245}
2246
2247template <typename Iter1, typename Iter2>
2250 const noexcept(noexcept(std::declval<const Iter1&>() ==
2251 std::declval<const Iter1&>()) &&
2252 noexcept(std::declval<const Iter2&>() ==
2253 std::declval<const Iter2&>())) {
2254 // Check if both are at end (in second range and at second_end_)
2255 const bool this_at_end = !in_first_ && (second_current_ == second_end_);
2256 const bool other_at_end =
2257 !other.in_first_ && (other.second_current_ == other.second_end_);
2258
2259 if (this_at_end && other_at_end) {
2260 return true;
2261 }
2262
2263 // Otherwise, must be in same range at same position
2264 if (in_first_ != other.in_first_) {
2265 return false;
2266 }
2267
2268 return in_first_ ? (first_current_ == other.first_current_)
2269 : (second_current_ == other.second_current_);
2270}
2271
2272template <typename Iter1, typename Iter2>
2275 noexcept(std::is_nothrow_copy_constructible_v<ChainAdapter> &&
2276 std::is_nothrow_copy_constructible_v<Iter1> &&
2277 std::is_nothrow_copy_constructible_v<Iter2>) -> ChainAdapter {
2278 auto end_iter = *this;
2279 end_iter.first_current_ = first_end_;
2280 end_iter.second_current_ = second_end_;
2281 end_iter.in_first_ = false;
2282 return end_iter;
2283}
2284
2285/**
2286 * @brief Adapter that iterates through elements in reverse order.
2287 * @details Requires a bidirectional iterator. Uses operator-- to traverse
2288 * backwards.
2289 * @tparam Iter Type of the underlying iterator
2290 *
2291 * @code
2292 * // Reverse iterate through entities
2293 * auto reversed = query.Reverse();
2294 * @endcode
2295 */
2296template <typename Iter>
2299 : public FunctionalAdapterBase<ReverseAdapter<Iter>> {
2300public:
2302 std::conditional_t<details::IterYieldsReference<Iter>,
2303 std::bidirectional_iterator_tag,
2304 std::input_iterator_tag>;
2306 std::conditional_t<details::IterYieldsReference<Iter>,
2307 std::bidirectional_iterator_tag,
2308 std::input_iterator_tag>;
2309 using value_type = std::iter_value_t<Iter>;
2311 using pointer = void;
2312 using difference_type = std::iter_difference_t<Iter>;
2313
2314 /**
2315 * @brief Constructs a reverse adapter.
2316 * @param begin Iterator to the beginning of the range
2317 * @param end Iterator to the end of the range
2318 */
2319 constexpr ReverseAdapter(Iter begin, Iter end) noexcept(
2320 std::is_nothrow_move_constructible_v<Iter> &&
2321 std::is_nothrow_copy_constructible_v<Iter> &&
2322 noexcept(std::declval<Iter&>() == std::declval<Iter&>()) &&
2323 noexcept(--std::declval<Iter&>()));
2324
2325 /**
2326 * @brief Constructs a reverse adapter with the given range.
2327 * @tparam R The type of the range
2328 * @param range The range to adapt
2329 */
2330 template <ExternalRange R>
2332 std::ranges::bidirectional_range<R>
2333 explicit constexpr ReverseAdapter(R& range) noexcept(noexcept(
2334 ReverseAdapter(std::ranges::begin(range), std::ranges::end(range))))
2335 : ReverseAdapter(std::ranges::begin(range), std::ranges::end(range)) {}
2336
2337 /**
2338 * @brief Constructs a reverse adapter with the given const range.
2339 * @tparam R The type of the range
2340 * @param range The range to adapt
2341 */
2342 template <ExternalRange R>
2344 std::ranges::bidirectional_range<R>
2345 explicit constexpr ReverseAdapter(const R& range) noexcept(noexcept(
2346 ReverseAdapter(std::ranges::cbegin(range), std::ranges::cend(range))))
2347 : ReverseAdapter(std::ranges::cbegin(range), std::ranges::cend(range)) {}
2348
2349 constexpr ReverseAdapter(const ReverseAdapter&) noexcept(
2350 std::is_nothrow_copy_constructible_v<Iter>) = default;
2351 constexpr ReverseAdapter(ReverseAdapter&&) noexcept(
2352 std::is_nothrow_move_constructible_v<Iter>) = default;
2353 constexpr ~ReverseAdapter() noexcept(std::is_nothrow_destructible_v<Iter>) =
2354 default;
2355
2356 constexpr ReverseAdapter& operator=(const ReverseAdapter&) noexcept(
2357 std::is_nothrow_copy_assignable_v<Iter>) = default;
2358 constexpr ReverseAdapter& operator=(ReverseAdapter&&) noexcept(
2359 std::is_nothrow_move_assignable_v<Iter>) = default;
2360
2361 constexpr ReverseAdapter& operator++() noexcept(
2362 noexcept(std::declval<Iter&>() == std::declval<Iter&>()) &&
2363 noexcept(--std::declval<Iter&>()));
2364 [[nodiscard]] constexpr ReverseAdapter operator++(int) noexcept(
2365 std::is_nothrow_copy_constructible_v<ReverseAdapter> &&
2366 noexcept(++std::declval<ReverseAdapter&>()));
2367
2368 constexpr ReverseAdapter& operator--() noexcept(
2369 std::is_nothrow_copy_constructible_v<Iter> &&
2370 noexcept(++std::declval<Iter&>()) &&
2371 noexcept(std::declval<Iter&>() != std::declval<Iter&>()));
2372 [[nodiscard]] constexpr ReverseAdapter operator--(int) noexcept(
2373 std::is_nothrow_copy_constructible_v<ReverseAdapter> &&
2374 noexcept(--std::declval<ReverseAdapter&>()));
2375
2376 [[nodiscard]] constexpr reference operator*() const
2377 noexcept(noexcept(*std::declval<const Iter&>())) {
2378 return *current_;
2379 }
2380
2381 pointer operator->() const = delete;
2382
2383 constexpr bool operator==(const ReverseAdapter& other) const
2384 noexcept(noexcept(std::declval<const Iter&>() ==
2385 std::declval<const Iter&>()));
2386 constexpr bool operator!=(const ReverseAdapter& other) const
2387 noexcept(noexcept(std::declval<ReverseAdapter>() ==
2388 std::declval<ReverseAdapter>())) {
2389 return !(*this == other);
2390 }
2391
2392 constexpr ReverseAdapter begin() const
2393 noexcept(std::is_nothrow_copy_constructible_v<ReverseAdapter>) {
2394 return *this;
2395 }
2396
2397 constexpr ReverseAdapter end() const
2398 noexcept(std::is_nothrow_copy_constructible_v<ReverseAdapter>);
2399
2400private:
2401 Iter begin_;
2402 Iter current_;
2403 Iter end_;
2404 bool done_ = false;
2405};
2406
2407template <ExternalRange R>
2408ReverseAdapter(R&) -> ReverseAdapter<std::ranges::iterator_t<R>>;
2409
2410template <ExternalRange R>
2411ReverseAdapter(const R&) -> ReverseAdapter<std::ranges::iterator_t<const R>>;
2412
2413template <typename Iter>
2414 requires ReverseAdapterRequirements<Iter>
2415constexpr ReverseAdapter<Iter>::ReverseAdapter(Iter begin, Iter end) noexcept(
2416 std::is_nothrow_move_constructible_v<Iter> &&
2417 std::is_nothrow_copy_constructible_v<Iter> &&
2418 noexcept(std::declval<Iter&>() == std::declval<Iter&>()) &&
2419 noexcept(--std::declval<Iter&>()))
2420 : begin_(std::move(begin)),
2421 current_(std::move(end)),
2422 end_(current_),
2423 done_(begin_ == end_) {
2424 if (current_ != begin_) {
2425 --current_;
2426 }
2427}
2428
2429template <typename Iter>
2431constexpr auto ReverseAdapter<Iter>::operator++() noexcept(
2432 noexcept(std::declval<Iter&>() == std::declval<Iter&>()) &&
2433 noexcept(--std::declval<Iter&>())) -> ReverseAdapter& {
2434 if (!done_) {
2435 if (current_ == begin_) {
2436 done_ = true;
2437 } else {
2438 --current_;
2439 }
2440 }
2441 return *this;
2442}
2443
2444template <typename Iter>
2446constexpr auto ReverseAdapter<Iter>::operator++(int) noexcept(
2447 std::is_nothrow_copy_constructible_v<ReverseAdapter> &&
2448 noexcept(++std::declval<ReverseAdapter&>())) -> ReverseAdapter {
2449 auto temp = *this;
2450 ++(*this);
2451 return temp;
2452}
2453
2454template <typename Iter>
2456constexpr auto ReverseAdapter<Iter>::operator--() noexcept(
2457 std::is_nothrow_copy_constructible_v<Iter> &&
2458 noexcept(++std::declval<Iter&>()) &&
2459 noexcept(std::declval<Iter&>() != std::declval<Iter&>()))
2460 -> ReverseAdapter& {
2461 if (done_) {
2462 done_ = false;
2463 current_ = begin_;
2464 } else {
2465 if (current_ != end_) {
2466 ++current_;
2467 }
2468 }
2469 return *this;
2470}
2471
2472template <typename Iter>
2474constexpr auto ReverseAdapter<Iter>::operator--(int) noexcept(
2475 std::is_nothrow_copy_constructible_v<ReverseAdapter> &&
2476 noexcept(--std::declval<ReverseAdapter&>())) -> ReverseAdapter {
2477 auto temp = *this;
2478 --(*this);
2479 return temp;
2480}
2481
2482template <typename Iter>
2485 const noexcept(noexcept(std::declval<const Iter&>() ==
2486 std::declval<const Iter&>())) {
2487 if (done_ && other.done_) {
2488 return true;
2489 }
2490 return done_ == other.done_ && current_ == other.current_;
2491}
2492
2493template <typename Iter>
2495constexpr auto ReverseAdapter<Iter>::end() const
2496 noexcept(std::is_nothrow_copy_constructible_v<ReverseAdapter>)
2497 -> ReverseAdapter {
2498 auto result = *this;
2499 result.done_ = true;
2500 return result;
2501}
2502
2503/**
2504 * @brief Adapter that flattens nested ranges into a single sequence.
2505 * @details Iterates through an outer range of ranges, yielding elements from
2506 * inner ranges.
2507 * @tparam Iter Type of the outer iterator
2508 *
2509 * @code
2510 * // Join queries of `Position` and `Velocity` components
2511 * auto joined = query1.Join(query2);
2512 * joined.ForEach([](const Position& pos, const Velocity& vel) {
2513 */
2514template <typename Iter>
2516class JoinAdapter final : public FunctionalAdapterBase<JoinAdapter<Iter>> {
2517public:
2518 using iterator_concept = details::adapter_iterator_concept_t<Iter>;
2519 using iterator_category = details::adapter_iterator_category_t<Iter>;
2520 using outer_value_type = std::iter_value_t<Iter>;
2521 using inner_iterator_type = std::ranges::iterator_t<outer_value_type>;
2522 using value_type = std::iter_value_t<inner_iterator_type>;
2523 using difference_type = ptrdiff_t;
2524 using pointer = value_type*;
2525 using reference = value_type;
2526
2527 /**
2528 * @brief Constructs a join adapter.
2529 * @param begin Iterator to the beginning of the outer range
2530 * @param end Iterator to the end of the outer range
2531 */
2532 constexpr JoinAdapter(Iter begin, Iter end) noexcept(
2533 std::is_nothrow_move_constructible_v<Iter> &&
2534 std::is_nothrow_copy_constructible_v<Iter> &&
2535 std::is_nothrow_move_constructible_v<inner_iterator_type> &&
2536 noexcept(std::declval<Iter&>() != std::declval<Iter&>()) &&
2537 noexcept(*std::declval<inner_iterator_type&>()) &&
2538 noexcept(AdvanceToValid()));
2539
2540 /**
2541 * @brief Constructs a join adapter from a range.
2542 * @tparam R The type of the range
2543 * @param range The range to adapt
2544 */
2545 template <ExternalRange R>
2547 explicit constexpr JoinAdapter(R& range) noexcept(
2548 noexcept(JoinAdapter(std::ranges::begin(range), std::ranges::end(range))))
2549 : JoinAdapter(std::ranges::begin(range), std::ranges::end(range)) {}
2550
2551 /**
2552 * @brief Constructs a join adapter from a range.
2553 * @tparam R The type of the range
2554 * @param range The range to adapt
2555 */
2556 template <ExternalRange R>
2557 requires JoinAdapterRequirements<std::ranges::iterator_t<const R>>
2558 explicit constexpr JoinAdapter(const R& range) noexcept(noexcept(
2559 JoinAdapter(std::ranges::cbegin(range), std::ranges::cend(range))))
2560 : JoinAdapter(std::ranges::cbegin(range), std::ranges::cend(range)) {}
2561
2562 constexpr JoinAdapter(const JoinAdapter&) noexcept(
2563 std::is_nothrow_copy_constructible_v<Iter> &&
2564 std::is_nothrow_copy_constructible_v<inner_iterator_type>) = default;
2565
2566 constexpr JoinAdapter(JoinAdapter&&) noexcept(
2567 std::is_nothrow_move_constructible_v<Iter> &&
2568 std::is_nothrow_move_constructible_v<inner_iterator_type>) = default;
2569 constexpr ~JoinAdapter() noexcept(
2570 std::is_nothrow_destructible_v<Iter> &&
2571 std::is_nothrow_destructible_v<inner_iterator_type>) = default;
2572
2573 constexpr JoinAdapter& operator=(const JoinAdapter&) noexcept(
2574 std::is_nothrow_copy_assignable_v<Iter> &&
2575 std::is_nothrow_copy_assignable_v<inner_iterator_type>) = default;
2576 constexpr JoinAdapter& operator=(JoinAdapter&&) noexcept(
2577 std::is_nothrow_move_assignable_v<Iter> &&
2578 std::is_nothrow_move_assignable_v<inner_iterator_type>) = default;
2579
2580 constexpr JoinAdapter& operator++() noexcept(
2581 noexcept(std::declval<inner_iterator_type&>() !=
2582 std::declval<inner_iterator_type&>()) &&
2583 noexcept(++std::declval<inner_iterator_type&>()) &&
2584 noexcept(AdvanceToValid()));
2585 [[nodiscard]] constexpr JoinAdapter operator++(int) noexcept(
2586 std::is_nothrow_copy_constructible_v<JoinAdapter> &&
2587 noexcept(++std::declval<JoinAdapter&>()));
2588
2589 [[nodiscard]] constexpr reference operator*() const
2590 noexcept(noexcept(*std::declval<const inner_iterator_type&>())) {
2591 return *inner_current_;
2592 }
2593
2594 pointer operator->() const = delete;
2595
2596 [[nodiscard]] constexpr bool operator==(const JoinAdapter& other) const
2597 noexcept(noexcept(std::declval<const Iter&>() ==
2598 std::declval<const Iter&>()) &&
2599 noexcept(std::declval<const inner_iterator_type&>() ==
2600 std::declval<const inner_iterator_type&>()));
2601 [[nodiscard]] constexpr bool operator!=(const JoinAdapter& other) const
2602 noexcept(noexcept(std::declval<JoinAdapter>() ==
2603 std::declval<JoinAdapter>())) {
2604 return !(*this == other);
2605 }
2606
2607 [[nodiscard]] constexpr JoinAdapter begin() const noexcept(
2608 std::is_nothrow_constructible_v<JoinAdapter, const Iter&, const Iter&>) {
2609 return {outer_begin_, outer_end_};
2610 }
2611
2612 [[nodiscard]] constexpr JoinAdapter end() const
2613 noexcept(std::is_nothrow_copy_constructible_v<JoinAdapter> &&
2614 std::is_nothrow_copy_assignable_v<Iter>);
2615
2616private:
2617 constexpr void AdvanceToValid() noexcept(
2618 noexcept(std::declval<Iter&>() == std::declval<Iter&>()) &&
2619 noexcept(std::declval<inner_iterator_type&>() ==
2620 std::declval<inner_iterator_type&>()) &&
2621 noexcept(++std::declval<Iter&>()) && noexcept(*std::declval<Iter&>()) &&
2622 std::is_nothrow_move_assignable_v<inner_iterator_type>);
2623
2624 Iter outer_begin_;
2625 Iter outer_current_;
2626 Iter outer_end_;
2627 inner_iterator_type inner_current_;
2628 inner_iterator_type inner_end_;
2629};
2630
2631template <ExternalRange R>
2632 requires JoinAdapterRequirements<std::ranges::iterator_t<R>>
2633JoinAdapter(R&) -> JoinAdapter<std::ranges::iterator_t<R>>;
2634
2635template <ExternalRange R>
2636 requires JoinAdapterRequirements<std::ranges::iterator_t<const R>>
2637JoinAdapter(const R&) -> JoinAdapter<std::ranges::iterator_t<const R>>;
2638
2639template <typename Iter>
2640 requires JoinAdapterRequirements<Iter>
2641constexpr JoinAdapter<Iter>::JoinAdapter(Iter begin, Iter end) noexcept(
2642 std::is_nothrow_move_constructible_v<Iter> &&
2643 std::is_nothrow_copy_constructible_v<Iter> &&
2644 std::is_nothrow_move_constructible_v<inner_iterator_type> &&
2645 noexcept(std::declval<Iter&>() != std::declval<Iter&>()) &&
2646 noexcept(*std::declval<inner_iterator_type&>()) &&
2647 noexcept(AdvanceToValid()))
2648 : outer_begin_(std::move(begin)),
2649 outer_current_(outer_begin_),
2650 outer_end_(std::move(end)) {
2651 if (outer_current_ != outer_end_) {
2652 auto& inner_range = *outer_current_;
2653 inner_current_ = std::ranges::begin(inner_range);
2654 inner_end_ = std::ranges::end(inner_range);
2655 }
2656 AdvanceToValid();
2657}
2658
2659template <typename Iter>
2660 requires JoinAdapterRequirements<Iter>
2661constexpr auto JoinAdapter<Iter>::operator++() noexcept(
2662 noexcept(std::declval<inner_iterator_type&>() !=
2663 std::declval<inner_iterator_type&>()) &&
2664 noexcept(++std::declval<inner_iterator_type&>()) &&
2665 noexcept(AdvanceToValid())) -> JoinAdapter& {
2666 if (inner_current_ != inner_end_) {
2667 ++inner_current_;
2668 }
2669 AdvanceToValid();
2670 return *this;
2671}
2672
2673template <typename Iter>
2674 requires JoinAdapterRequirements<Iter>
2675constexpr auto JoinAdapter<Iter>::operator++(int) noexcept(
2676 std::is_nothrow_copy_constructible_v<JoinAdapter> &&
2677 noexcept(++std::declval<JoinAdapter&>())) -> JoinAdapter {
2678 auto temp = *this;
2679 ++(*this);
2680 return temp;
2681}
2682
2683template <typename Iter>
2684 requires JoinAdapterRequirements<Iter>
2685constexpr bool JoinAdapter<Iter>::operator==(const JoinAdapter& other) const
2686 noexcept(noexcept(std::declval<const Iter&>() ==
2687 std::declval<const Iter&>()) &&
2688 noexcept(std::declval<const inner_iterator_type&>() ==
2689 std::declval<const inner_iterator_type&>())) {
2690 if (outer_current_ != other.outer_current_) {
2691 return false;
2692 }
2693 if (outer_current_ == outer_end_) {
2694 return true;
2695 }
2696 return inner_current_ == other.inner_current_;
2697}
2698
2699template <typename Iter>
2700 requires JoinAdapterRequirements<Iter>
2701constexpr auto JoinAdapter<Iter>::end() const
2702 noexcept(std::is_nothrow_copy_constructible_v<JoinAdapter> &&
2703 std::is_nothrow_copy_assignable_v<Iter>) -> JoinAdapter {
2704 auto result = *this;
2705 result.outer_current_ = result.outer_end_;
2706 return result;
2707}
2708
2709template <typename Iter>
2710 requires JoinAdapterRequirements<Iter>
2711constexpr void JoinAdapter<Iter>::AdvanceToValid() noexcept(
2712 noexcept(std::declval<Iter&>() == std::declval<Iter&>()) &&
2713 noexcept(std::declval<inner_iterator_type&>() ==
2714 std::declval<inner_iterator_type&>()) &&
2715 noexcept(++std::declval<Iter&>()) && noexcept(*std::declval<Iter&>()) &&
2716 std::is_nothrow_move_assignable_v<inner_iterator_type>) {
2717 while (outer_current_ != outer_end_) {
2718 if (inner_current_ == inner_end_) {
2719 ++outer_current_;
2720 if (outer_current_ == outer_end_) {
2721 return;
2722 }
2723 auto& inner_range = *outer_current_;
2724 inner_current_ = std::ranges::begin(inner_range);
2725 inner_end_ = std::ranges::end(inner_range);
2726 }
2727
2728 if (inner_current_ != inner_end_) {
2729 return;
2730 }
2731 }
2732}
2733
2734/**
2735 * @brief A non-owning view over a sliding window of elements.
2736 * @details Provides a lightweight, non-allocating view over a contiguous window
2737 * of elements.
2738 * @tparam Iter Type of the underlying iterator
2739 *
2740 * @code
2741 * std::vector<int> data = {1, 2, 3, 4, 5};
2742 * auto slide = SlideAdapterFromRange(data, 3);
2743 * for (const auto& window : slide) {
2744 * // window is a SlideView, iterate over it
2745 * for (int val : window) {
2746 * std::cout << val << " ";
2747 * }
2748 * std::cout << "\n";
2749 * }
2750 * // Output:
2751 * // 1 2 3
2752 * // 2 3 4
2753 * // 3 4 5
2754 * @endcode
2755 */
2756template <IteratorLike Iter>
2758public:
2759 using iterator = Iter;
2760 using const_iterator = Iter;
2761 using value_type = std::iter_value_t<Iter>;
2762 using reference = decltype(*std::declval<Iter>());
2763 using size_type = size_t;
2764
2765 /**
2766 * @brief Constructs a SlideView.
2767 * @param begin Iterator to the beginning of the window
2768 * @param size Size of the window
2769 */
2770 constexpr SlideView(Iter begin, size_t size) noexcept(
2771 std::is_nothrow_copy_constructible_v<Iter>)
2772 : begin_(begin), size_(size) {}
2773
2774 constexpr SlideView(const SlideView&) noexcept(
2775 std::is_nothrow_copy_constructible_v<Iter>) = default;
2776 constexpr SlideView(SlideView&&) noexcept(
2777 std::is_nothrow_move_constructible_v<Iter>) = default;
2778 constexpr ~SlideView() noexcept = default;
2779
2780 constexpr SlideView& operator=(const SlideView&) noexcept(
2781 std::is_nothrow_copy_assignable_v<Iter>) = default;
2782 constexpr SlideView& operator=(SlideView&&) noexcept(
2783 std::is_nothrow_move_assignable_v<Iter>) = default;
2784
2785 /**
2786 * @brief Collects the window elements into a vector.
2787 * @details Use when you need ownership of the elements.
2788 * @return Vector containing copies of the window elements
2789 */
2790 [[nodiscard]] constexpr auto Collect() const -> std::vector<value_type>
2791 requires std::copy_constructible<value_type>;
2792
2793 /**
2794 * @brief Collects the window elements into a vector with a custom allocator.
2795 * @details Use when you need ownership of the elements with a specific
2796 * allocator.
2797 * @tparam Allocator Allocator type
2798 * @param allocator Allocator to use for the vector
2799 * @return Vector containing copies of the window elements
2800 */
2801 template <typename Allocator>
2802 requires(!std::derived_from<std::remove_pointer_t<Allocator>,
2803 std::pmr::memory_resource>)
2804 [[nodiscard]] constexpr auto CollectWith(Allocator allocator = {}) const
2805 -> std::vector<value_type, Allocator>
2806 requires std::copy_constructible<value_type>;
2807
2808 /**
2809 * @brief Collects the window elements into a vector with a specific memory
2810 * resource.
2811 * @details Use when you need ownership of the elements with a specific
2812 * memory resource.
2813 * @param resource Memory resource to use for the vector
2814 * @return Vector containing copies of the window elements
2815 */
2816 [[nodiscard]] constexpr auto CollectWith(
2817 std::pmr::memory_resource* resource) const -> std::pmr::vector<value_type>
2818 requires std::copy_constructible<value_type>;
2819
2820 auto CollectWith(std::nullptr_t) const
2821 -> std::pmr::vector<value_type> = delete;
2822
2823 /**
2824 * @brief Accesses an element by index.
2825 * @warning Index must be less than size
2826 * @param index Index of the element
2827 * @return Reference to the element
2828 */
2829 [[nodiscard]] constexpr reference operator[](size_t index) const
2830 noexcept(noexcept(*std::declval<Iter>()) &&
2831 noexcept(++std::declval<Iter&>()));
2832
2833 /**
2834 * @brief Compares two SlideViews for equality.
2835 * @param other SlideView to compare with
2836 * @return True if both views have the same elements
2837 */
2838 [[nodiscard]] constexpr bool operator==(const SlideView& other) const;
2839
2840 /**
2841 * @brief Compares SlideView with a vector for equality.
2842 * @tparam R
2843 * @param vec Vector to compare with
2844 * @return True if the view has the same elements as the vector
2845 */
2846 template <std::ranges::sized_range R>
2847 [[nodiscard]] constexpr bool operator==(const R& range) const;
2848
2849 /**
2850 * @brief Checks if the window is empty.
2851 * @return True if the window has no elements
2852 */
2853 [[nodiscard]] constexpr bool Empty() const noexcept { return size_ == 0; }
2854
2855 /**
2856 * @brief Returns the size of the window.
2857 * @return Number of elements in the window
2858 */
2859 [[nodiscard]] constexpr size_t Size() const noexcept { return size_; }
2860
2861 /**
2862 * @brief Returns an iterator to the beginning.
2863 * @return Iterator to the first element
2864 */
2865 [[nodiscard]] constexpr Iter begin() const
2866 noexcept(std::is_nothrow_copy_constructible_v<Iter>) {
2867 return begin_;
2868 }
2869
2870 /**
2871 * @brief Returns an iterator to the end.
2872 * @return Iterator past the last element
2873 */
2874 [[nodiscard]] constexpr Iter end() const
2875 noexcept(std::is_nothrow_copy_constructible_v<Iter> &&
2876 noexcept(++std::declval<Iter&>()));
2877
2878private:
2879 Iter begin_;
2880 size_t size_ = 0;
2881};
2882
2883template <IteratorLike Iter>
2884constexpr auto SlideView<Iter>::Collect() const -> std::vector<value_type>
2885 requires std::copy_constructible<value_type>
2886{
2887 std::vector<value_type> result;
2888 result.reserve(size_);
2889 auto it = begin_;
2890 for (size_t i = 0; i < size_; ++i) {
2891 result.push_back(*it++);
2892 }
2893 return result;
2894}
2895
2896template <IteratorLike Iter>
2897template <typename Allocator>
2898 requires(!std::derived_from<std::remove_pointer_t<Allocator>,
2899 std::pmr::memory_resource>)
2900constexpr auto SlideView<Iter>::CollectWith(Allocator allocator) const
2901 -> std::vector<value_type, Allocator>
2902 requires std::copy_constructible<value_type>
2903{
2904 std::vector<value_type, Allocator> result{std::move(allocator)};
2905 result.reserve(size_);
2906 auto it = begin_;
2907 for (size_t i = 0; i < size_; ++i) {
2908 result.push_back(*it++);
2909 }
2910 return result;
2911}
2912
2913template <IteratorLike Iter>
2914constexpr auto SlideView<Iter>::CollectWith(
2915 std::pmr::memory_resource* resource) const -> std::pmr::vector<value_type>
2916 requires std::copy_constructible<value_type>
2917{
2918 std::pmr::vector<value_type> result{resource};
2919 result.reserve(size_);
2920 auto it = begin_;
2921 for (size_t i = 0; i < size_; ++i) {
2922 result.push_back(*it++);
2923 }
2924 return result;
2925}
2926
2927template <IteratorLike Iter>
2928constexpr auto SlideView<Iter>::operator[](size_t index) const
2929 noexcept(noexcept(*std::declval<Iter>()) &&
2930 noexcept(++std::declval<Iter&>())) -> reference {
2931 auto it = begin_;
2932 for (size_t i = 0; i < index; ++i) {
2933 ++it;
2934 }
2935 return *it;
2936}
2937
2938template <IteratorLike Iter>
2939constexpr bool SlideView<Iter>::operator==(const SlideView& other) const {
2940 if (size_ != other.size_) {
2941 return false;
2942 }
2943
2944 auto it1 = begin_;
2945 auto it2 = other.begin_;
2946 for (size_t i = 0; i < size_; ++i) {
2947 if (*it1++ != *it2++) {
2948 return false;
2949 }
2950 }
2951 return true;
2952}
2953
2954template <IteratorLike Iter>
2955template <std::ranges::sized_range R>
2956constexpr bool SlideView<Iter>::operator==(const R& range) const {
2957 if (size_ != std::ranges::size(range)) {
2958 return false;
2959 }
2960
2961 return std::equal(std::ranges::cbegin(range), std::ranges::cend(range),
2962 begin_);
2963}
2964
2965template <IteratorLike Iter>
2966constexpr Iter SlideView<Iter>::end() const
2967 noexcept(std::is_nothrow_copy_constructible_v<Iter> &&
2968 noexcept(++std::declval<Iter&>())) {
2969 auto it = begin_;
2970 for (size_t i = 0; i < size_; ++i) {
2971 ++it;
2972 }
2973 return it;
2974}
2975
2976/**
2977 * @brief Adapter that yields sliding windows of elements.
2978 * @details Creates overlapping windows of a fixed size, moving one element at a
2979 * time. Each window is returned as a SlideView, which is a non-allocating view
2980 * over the elements.
2981 * @tparam Iter Type of the underlying iterator
2982 *
2983 * @code
2984 * std::vector<int> data = {1, 2, 3, 4, 5};
2985 * auto slide = SlideAdapterFromRange(data, 3);
2986 *
2987 * // Iterate over windows
2988 * for (const auto& window : slide) {
2989 * // Each window is a SlideView
2990 * for (int val : window) {
2991 * std::cout << val << " ";
2992 * }
2993 * std::cout << "\n";
2994 * }
2995 *
2996 * // Collect windows if needed
2997 * auto windows = slide.Collect(); // Vector of SlideViews
2998 *
2999 * // Convert a window to vector if ownership needed
3000 * auto first_window = (*slide.begin()).Collect(); // std::vector<int>
3001 * @endcode
3002 */
3003template <typename Iter>
3004 requires SlideAdapterRequirements<Iter>
3005class SlideAdapter final : public FunctionalAdapterBase<SlideAdapter<Iter>> {
3006public:
3007 using iterator_concept = std::forward_iterator_tag;
3008 using iterator_category = std::input_iterator_tag;
3011 using pointer = void;
3012 using difference_type = std::iter_difference_t<Iter>;
3013
3014 /**
3015 * @brief Constructs a slide adapter.
3016 * @warning window_size must be greater than 0
3017 * @param begin Iterator to the beginning of the range
3018 * @param end Iterator to the end of the range
3019 * @param window_size Size of the sliding window
3020 */
3021 constexpr SlideAdapter(Iter begin, Iter end, size_t window_size) noexcept(
3022 std::is_nothrow_move_constructible_v<Iter> &&
3023 std::is_nothrow_copy_constructible_v<Iter> &&
3024 noexcept(std::declval<Iter&>() != std::declval<Iter&>()) &&
3025 noexcept(++std::declval<Iter&>()));
3026
3027 /**
3028 * @brief Constructs a slide adapter from a range.
3029 * @tparam R The type of the range
3030 * @param range The range to adapt
3031 * @param window_size The size of the sliding window
3032 */
3033 template <ExternalRange R>
3035 explicit constexpr SlideAdapter(R& range, size_t window_size) noexcept(
3036 noexcept(SlideAdapter(std::ranges::begin(range), std::ranges::end(range),
3037 window_size)))
3038 : SlideAdapter(std::ranges::begin(range), std::ranges::end(range),
3039 window_size) {}
3040
3041 /**
3042 * @brief Constructs a slide adapter from a const range.
3043 * @tparam R The type of the range
3044 * @param range The range to adapt
3045 * @param window_size The size of the sliding window
3046 */
3047 template <ExternalRange R>
3049 explicit constexpr SlideAdapter(const R& range, size_t window_size) noexcept(
3050 noexcept(SlideAdapter(std::ranges::cbegin(range),
3051 std::ranges::cend(range), window_size)))
3052 : SlideAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
3053 window_size) {}
3054
3055 constexpr SlideAdapter(const SlideAdapter&) noexcept(
3056 std::is_nothrow_copy_constructible_v<Iter>) = default;
3057 constexpr SlideAdapter(SlideAdapter&&) noexcept(
3058 std::is_nothrow_move_constructible_v<Iter>) = default;
3059 constexpr ~SlideAdapter() noexcept(std::is_nothrow_destructible_v<Iter>) =
3060 default;
3061
3062 constexpr SlideAdapter& operator=(const SlideAdapter&) noexcept(
3063 std::is_nothrow_copy_assignable_v<Iter>) = default;
3064 constexpr SlideAdapter& operator=(SlideAdapter&&) noexcept(
3065 std::is_nothrow_move_assignable_v<Iter>) = default;
3066
3067 constexpr SlideAdapter& operator++() noexcept(
3068 noexcept(std::declval<Iter&>() != std::declval<Iter&>()) &&
3069 noexcept(++std::declval<Iter&>()));
3070 [[nodiscard]] constexpr SlideAdapter operator++(int) noexcept(
3071 std::is_nothrow_copy_constructible_v<SlideAdapter> &&
3072 noexcept(++std::declval<SlideAdapter&>()));
3073
3074 /**
3075 * @brief Dereferences the iterator to get the current window.
3076 * @return SlideView representing the current window
3077 */
3078 [[nodiscard]] constexpr reference operator*() const
3079 noexcept(std::is_nothrow_constructible_v<SlideView<Iter>, Iter, size_t>) {
3080 return {current_, window_size_};
3081 }
3082
3083 pointer operator->() const = delete;
3084
3085 [[nodiscard]] constexpr bool operator==(const SlideAdapter& other) const
3086 noexcept(noexcept(std::declval<const Iter&>() ==
3087 std::declval<const Iter&>())) {
3088 return current_ == other.current_;
3089 }
3090
3091 [[nodiscard]] constexpr bool operator!=(const SlideAdapter& other) const
3092 noexcept(noexcept(std::declval<const Iter&>() !=
3093 std::declval<const Iter&>())) {
3094 return current_ != other.current_;
3095 }
3096
3097 [[nodiscard]] constexpr SlideAdapter begin() const
3098 noexcept(std::is_nothrow_constructible_v<SlideAdapter, const Iter&,
3099 const Iter&, size_t>) {
3100 return {begin_, end_, window_size_};
3101 }
3102
3103 [[nodiscard]] constexpr SlideAdapter end() const
3104 noexcept(std::is_nothrow_copy_constructible_v<SlideAdapter> &&
3105 std::is_nothrow_copy_assignable_v<Iter> &&
3106 noexcept(std::declval<Iter&>() != std::declval<const Iter&>()) &&
3107 noexcept(++std::declval<Iter&>()));
3108
3109 /**
3110 * @brief Returns the window size.
3111 * @return Size of the sliding window
3112 */
3113 [[nodiscard]] constexpr size_t WindowSize() const noexcept {
3114 return window_size_;
3115 }
3116
3117private:
3118 Iter begin_;
3119 Iter current_;
3120 Iter end_;
3121 size_t window_size_ = 0;
3122};
3123
3124template <ExternalRange R>
3125 requires SlideAdapterRequirements<std::ranges::iterator_t<R>>
3127
3128template <ExternalRange R>
3130SlideAdapter(const R&, size_t)
3132
3133template <typename Iter>
3136 Iter begin, Iter end,
3137 size_t window_size) noexcept(std::is_nothrow_move_constructible_v<Iter> &&
3138 std::is_nothrow_copy_constructible_v<Iter> &&
3139 noexcept(std::declval<Iter&>() !=
3140 std::declval<Iter&>()) &&
3141 noexcept(++std::declval<Iter&>()))
3142 : begin_(std::move(begin)),
3143 current_(begin_),
3144 end_(std::move(end)),
3145 window_size_(window_size) {
3146 // If we can't form a complete window, start at end
3147 Iter iter = begin_;
3148 size_t count = 0;
3149 while (iter != end_) {
3150 ++iter;
3151 ++count;
3152 }
3153 if (count < window_size_) {
3154 current_ = end_;
3155 }
3156}
3157
3158template <typename Iter>
3160constexpr auto SlideAdapter<Iter>::operator++() noexcept(
3161 noexcept(std::declval<Iter&>() != std::declval<Iter&>()) &&
3162 noexcept(++std::declval<Iter&>())) -> SlideAdapter& {
3163 if (current_ != end_) {
3164 ++current_;
3165 }
3166 return *this;
3167}
3168
3169template <typename Iter>
3171constexpr auto SlideAdapter<Iter>::operator++(int) noexcept(
3172 std::is_nothrow_copy_constructible_v<SlideAdapter> &&
3173 noexcept(++std::declval<SlideAdapter&>())) -> SlideAdapter {
3174 auto temp = *this;
3175 ++(*this);
3176 return temp;
3177}
3178
3179template <typename Iter>
3181constexpr auto SlideAdapter<Iter>::end() const
3182 noexcept(std::is_nothrow_copy_constructible_v<SlideAdapter> &&
3183 std::is_nothrow_copy_assignable_v<Iter> &&
3184 noexcept(std::declval<Iter&>() != std::declval<const Iter&>()) &&
3185 noexcept(++std::declval<Iter&>())) -> SlideAdapter {
3186 auto result = *this;
3187
3188 // Calculate end position: when we can't form a complete window
3189 Iter iter = begin_;
3190 size_t count = 0;
3191 while (iter != end_) {
3192 ++iter;
3193 ++count;
3194 }
3195
3196 // Position where we can't form more windows
3197 if (count >= window_size_) {
3198 result.current_ = begin_;
3199 for (size_t i = 0; i < count - window_size_ + 1; ++i) {
3200 ++result.current_;
3201 }
3202 } else {
3203 result.current_ = end_;
3204 }
3205
3206 return result;
3207}
3208
3209/**
3210 * @brief Adapter that yields every Nth element from the range.
3211 * @details Similar to StepBy but with different semantics - takes stride, not
3212 * step.
3213 * @tparam Iter Type of the underlying iterator
3214 *
3215 * @code
3216 * std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
3217 * auto strided = StrideAdapterFromRange(data, 3);
3218 * // Yields: 1, 4, 7
3219 * for (int val : strided) {
3220 * std::cout << val << " ";
3221 * }
3222 * @endcode
3223 */
3224template <typename Iter>
3226class StrideAdapter final : public FunctionalAdapterBase<StrideAdapter<Iter>> {
3227public:
3230 using value_type = std::iter_value_t<Iter>;
3232 using pointer = void;
3233 using difference_type = std::iter_difference_t<Iter>;
3234
3235 /**
3236 * @brief Constructs a stride adapter.
3237 * @param begin Iterator to the beginning of the range
3238 * @param end Iterator to the end of the range
3239 * @param stride Number of elements to skip between yields (1 = every element,
3240 * 2 = every other, etc.)
3241 * @warning stride must be greater than 0
3242 */
3243 constexpr StrideAdapter(Iter begin, Iter end, size_t stride) noexcept(
3244 std::is_nothrow_move_constructible_v<Iter> &&
3245 std::is_nothrow_copy_constructible_v<Iter>)
3246 : begin_(std::move(begin)),
3247 current_(begin_),
3248 end_(std::move(end)),
3249 stride_(stride) {}
3250
3251 /**
3252 * @brief Constructs a stride adapter from a range and a stride.
3253 * @tparam R The type of the range
3254 * @param range The range to adapt
3255 * @param stride Number of elements to skip between yields (1 = every element,
3256 * 2 = every other, etc.)
3257 * @warning stride must be greater than 0
3258 */
3259 template <ExternalRange R>
3261 constexpr StrideAdapter(R& range, size_t stride) noexcept(
3262 noexcept(StrideAdapter(std::ranges::begin(range), std::ranges::end(range),
3263 stride)))
3264 : StrideAdapter(std::ranges::begin(range), std::ranges::end(range),
3265 stride) {}
3266
3267 /**
3268 * @brief Constructs a stride adapter from a const range and a stride.
3269 * @tparam R The type of the range
3270 * @param range The range to adapt
3271 * @param stride Number of elements to skip between yields (1 = every element,
3272 * 2 = every other, etc.)
3273 * @warning stride must be greater than 0
3274 */
3275 template <ExternalRange R>
3277 constexpr StrideAdapter(const R& range, size_t stride) noexcept(
3278 noexcept(StrideAdapter(std::ranges::cbegin(range),
3279 std::ranges::cend(range), stride)))
3280 : StrideAdapter(std::ranges::cbegin(range), std::ranges::cend(range),
3281 stride) {}
3282
3283 constexpr StrideAdapter(const StrideAdapter&) noexcept(
3284 std::is_nothrow_copy_constructible_v<Iter>) = default;
3285 constexpr StrideAdapter(StrideAdapter&&) noexcept(
3286 std::is_nothrow_move_constructible_v<Iter>) = default;
3287 constexpr ~StrideAdapter() noexcept(std::is_nothrow_destructible_v<Iter>) =
3288 default;
3289
3290 constexpr StrideAdapter& operator=(const StrideAdapter&) noexcept(
3291 std::is_nothrow_copy_assignable_v<Iter>) = default;
3292 constexpr StrideAdapter& operator=(StrideAdapter&&) noexcept(
3293 std::is_nothrow_move_assignable_v<Iter>) = default;
3294
3295 constexpr StrideAdapter& operator++() noexcept(
3296 noexcept(std::declval<Iter&>() != std::declval<Iter&>()) &&
3297 noexcept(++std::declval<Iter&>()));
3298 [[nodiscard]] constexpr StrideAdapter operator++(int) noexcept(
3299 std::is_nothrow_copy_constructible_v<StrideAdapter> &&
3300 noexcept(++std::declval<StrideAdapter&>()));
3301
3302 [[nodiscard]] constexpr reference operator*() const
3303 noexcept(noexcept(*std::declval<const Iter&>())) {
3304 return *current_;
3305 }
3306
3307 pointer operator->() const = delete;
3308
3309 [[nodiscard]] constexpr bool operator==(const StrideAdapter& other) const
3310 noexcept(noexcept(std::declval<const Iter&>() ==
3311 std::declval<const Iter&>())) {
3312 return current_ == other.current_;
3313 }
3314
3315 [[nodiscard]] constexpr bool operator!=(const StrideAdapter& other) const
3316 noexcept(noexcept(std::declval<const Iter&>() !=
3317 std::declval<const Iter&>())) {
3318 return current_ != other.current_;
3319 }
3320
3321 [[nodiscard]] constexpr StrideAdapter begin() const
3322 noexcept(std::is_nothrow_constructible_v<StrideAdapter, const Iter&,
3323 const Iter&, size_t>) {
3324 return {begin_, end_, stride_};
3325 }
3326
3327 [[nodiscard]] constexpr StrideAdapter end() const
3328 noexcept(std::is_nothrow_copy_constructible_v<StrideAdapter> &&
3329 std::is_nothrow_copy_assignable_v<Iter>);
3330
3331private:
3332 Iter begin_;
3333 Iter current_;
3334 Iter end_;
3335 size_t stride_ = 0;
3336};
3337
3338template <ExternalRange R>
3339 requires StrideAdapterRequirements<std::ranges::iterator_t<R>>
3340StrideAdapter(R&, size_t) -> StrideAdapter<std::ranges::iterator_t<R>>;
3341
3342template <ExternalRange R>
3343 requires StrideAdapterRequirements<std::ranges::iterator_t<const R>>
3344StrideAdapter(const R&, size_t)
3345 -> StrideAdapter<std::ranges::iterator_t<const R>>;
3346
3347template <typename Iter>
3348 requires StrideAdapterRequirements<Iter>
3349constexpr auto StrideAdapter<Iter>::operator++() noexcept(
3350 noexcept(std::declval<Iter&>() != std::declval<Iter&>()) &&
3351 noexcept(++std::declval<Iter&>())) -> StrideAdapter& {
3352 for (size_t i = 0; i < stride_ && current_ != end_; ++i) {
3353 ++current_;
3354 }
3355 return *this;
3356}
3357
3358template <typename Iter>
3360constexpr auto StrideAdapter<Iter>::operator++(int) noexcept(
3361 std::is_nothrow_copy_constructible_v<StrideAdapter> &&
3362 noexcept(++std::declval<StrideAdapter&>())) -> StrideAdapter {
3363 auto temp = *this;
3364 ++(*this);
3365 return temp;
3366}
3367
3368template <typename Iter>
3370constexpr auto StrideAdapter<Iter>::end() const
3371 noexcept(std::is_nothrow_copy_constructible_v<StrideAdapter> &&
3372 std::is_nothrow_copy_assignable_v<Iter>) -> StrideAdapter {
3373 auto result = *this;
3374 result.current_ = end_;
3375 return result;
3376}
3377
3378/**
3379 * @brief Adapter that combines two ranges into pairs.
3380 * @details Iterates both ranges in parallel, yielding tuples of corresponding
3381 * elements. Stops when either range is exhausted.
3382 * @tparam Iter1 Type of the first iterator
3383 * @tparam Iter2 Type of the second iterator
3384 *
3385 * @code
3386 * std::vector<int> ids = {1, 2, 3};
3387 * std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
3388 * auto zipped = ZipAdapterFromRange(ids, names);
3389 * for (const auto& [id, name] : zipped) {
3390 * std::cout << id << ": " << name << "\n";
3391 * }
3392 * // Output:
3393 * // 1: Alice
3394 * // 2: Bob
3395 * // 3: Charlie
3396 * @endcode
3397 */
3398template <typename Iter1, typename Iter2>
3400class ZipAdapter final
3401 : public FunctionalAdapterBase<ZipAdapter<Iter1, Iter2>> {
3402public:
3403 using iterator_concept = std::input_iterator_tag;
3404 using iterator_category = std::input_iterator_tag;
3406 std::tuple<std::iter_value_t<Iter1>, std::iter_value_t<Iter2>>;
3408 using pointer = void;
3409 using difference_type = ptrdiff_t;
3410
3411 /**
3412 * @brief Constructs a zip adapter.
3413 * @param begin1 Iterator to the beginning of the first range
3414 * @param end1 Iterator to the end of the first range
3415 * @param begin2 Iterator to the beginning of the second range
3416 * @param end2 Iterator to the end of the second range
3417 */
3418 constexpr ZipAdapter(
3419 Iter1 begin1, Iter1 end1, Iter2 begin2,
3420 Iter2 end2) noexcept(std::is_nothrow_move_constructible_v<Iter1> &&
3421 std::is_nothrow_copy_constructible_v<Iter1> &&
3422 std::is_nothrow_move_constructible_v<Iter2> &&
3423 std::is_nothrow_copy_constructible_v<Iter2>)
3424 : first_begin_(std::move(begin1)),
3425 first_current_(first_begin_),
3426 first_end_(std::move(end1)),
3427 second_begin_(std::move(begin2)),
3428 second_current_(second_begin_),
3429 second_end_(std::move(end2)) {}
3430
3431 template <ExternalRange R1, ExternalRange R2>
3433 std::ranges::iterator_t<R2>>
3434 constexpr ZipAdapter(R1& first_range, R2& second_range) noexcept(noexcept(
3435 ZipAdapter(std::ranges::begin(first_range), std::ranges::end(first_range),
3436 std::ranges::begin(second_range),
3437 std::ranges::end(second_range))))
3438 : ZipAdapter(
3439 std::ranges::begin(first_range), std::ranges::end(first_range),
3440 std::ranges::begin(second_range), std::ranges::end(second_range)) {}
3441
3442 template <ExternalRange R1, ExternalRange R2>
3444 std::ranges::iterator_t<const R2>>
3445 constexpr ZipAdapter(const R1& first_range, const R2& second_range) noexcept(
3446 noexcept(ZipAdapter(std::ranges::cbegin(first_range),
3447 std::ranges::cend(first_range),
3448 std::ranges::cbegin(second_range),
3449 std::ranges::cend(second_range))))
3450 : ZipAdapter(std::ranges::cbegin(first_range),
3451 std::ranges::cend(first_range),
3452 std::ranges::cbegin(second_range),
3453 std::ranges::cend(second_range)) {}
3454
3455 constexpr ZipAdapter(const ZipAdapter&) noexcept(
3456 std::is_nothrow_copy_constructible_v<Iter1> &&
3457 std::is_nothrow_copy_constructible_v<Iter2>) = default;
3458 constexpr ZipAdapter(ZipAdapter&&) noexcept(
3459 std::is_nothrow_move_constructible_v<Iter1> &&
3460 std::is_nothrow_move_constructible_v<Iter2>) = default;
3461 constexpr ~ZipAdapter() noexcept(std::is_nothrow_destructible_v<Iter1> &&
3462 std::is_nothrow_destructible_v<Iter2>) =
3463 default;
3464
3465 constexpr ZipAdapter& operator=(const ZipAdapter&) noexcept(
3466 std::is_nothrow_copy_assignable_v<Iter1> &&
3467 std::is_nothrow_copy_assignable_v<Iter2>) = default;
3468 constexpr ZipAdapter& operator=(ZipAdapter&&) noexcept(
3469 std::is_nothrow_move_assignable_v<Iter1> &&
3470 std::is_nothrow_move_assignable_v<Iter2>) = default;
3471
3472 constexpr ZipAdapter& operator++() noexcept(
3473 noexcept(std::declval<Iter1&>() != std::declval<Iter1&>()) &&
3474 noexcept(++std::declval<Iter1&>()) &&
3475 noexcept(std::declval<Iter2&>() != std::declval<Iter2&>()) &&
3476 noexcept(++std::declval<Iter2&>()));
3477
3478 [[nodiscard]] constexpr ZipAdapter operator++(int) noexcept(
3479 std::is_nothrow_copy_constructible_v<ZipAdapter> &&
3480 noexcept(++std::declval<ZipAdapter&>()));
3481
3482 [[nodiscard]] constexpr reference operator*() const {
3483 return std::make_tuple(*first_current_, *second_current_);
3484 }
3485
3486 pointer operator->() const = delete;
3487
3488 [[nodiscard]] constexpr bool operator==(const ZipAdapter& other) const
3489 noexcept(noexcept(std::declval<const Iter1&>() ==
3490 std::declval<const Iter1&>()) &&
3491 noexcept(std::declval<const Iter2&>() ==
3492 std::declval<const Iter2&>())) {
3493 return first_current_ == other.first_current_ ||
3494 second_current_ == other.second_current_;
3495 }
3496
3497 [[nodiscard]] constexpr bool operator!=(const ZipAdapter& other) const
3498 noexcept(noexcept(std::declval<const ZipAdapter&>() ==
3499 std::declval<const ZipAdapter&>())) {
3500 return !(*this == other);
3501 }
3502
3503 [[nodiscard]] constexpr bool IsAtEnd() const noexcept(
3504 noexcept(std::declval<const Iter1&>() == std::declval<const Iter1&>()) &&
3505 noexcept(std::declval<const Iter2&>() == std::declval<const Iter2&>())) {
3506 return first_current_ == first_end_ || second_current_ == second_end_;
3507 }
3508
3509 [[nodiscard]] constexpr ZipAdapter begin() const
3510 noexcept(std::is_nothrow_copy_constructible_v<ZipAdapter>) {
3511 return *this;
3512 }
3513
3514 [[nodiscard]] constexpr ZipAdapter end() const
3515 noexcept(std::is_nothrow_copy_constructible_v<ZipAdapter>);
3516
3517private:
3518 Iter1 first_begin_;
3519 Iter1 first_current_;
3520 Iter1 first_end_;
3521 Iter2 second_begin_;
3522 Iter2 second_current_;
3523 Iter2 second_end_;
3524};
3525
3526template <ExternalRange R1, ExternalRange R2>
3527 requires ZipAdapterRequirements<std::ranges::iterator_t<R1>,
3528 std::ranges::iterator_t<R2>>
3529ZipAdapter(R1&, R2&)
3530 -> ZipAdapter<std::ranges::iterator_t<R1>, std::ranges::iterator_t<R2>>;
3531
3532template <ExternalRange R1, ExternalRange R2>
3533 requires ZipAdapterRequirements<std::ranges::iterator_t<const R1>,
3534 std::ranges::iterator_t<const R2>>
3535ZipAdapter(const R1&, const R2&)
3536 -> ZipAdapter<std::ranges::iterator_t<const R1>,
3537 std::ranges::iterator_t<const R2>>;
3538
3539template <typename Iter1, typename Iter2>
3540 requires ZipAdapterRequirements<Iter1, Iter2>
3541constexpr auto ZipAdapter<Iter1, Iter2>::operator++() noexcept(
3542 noexcept(std::declval<Iter1&>() != std::declval<Iter1&>()) &&
3543 noexcept(++std::declval<Iter1&>()) &&
3544 noexcept(std::declval<Iter2&>() != std::declval<Iter2&>()) &&
3545 noexcept(++std::declval<Iter2&>())) -> ZipAdapter& {
3546 if (first_current_ != first_end_) {
3547 ++first_current_;
3548 }
3549 if (second_current_ != second_end_) {
3550 ++second_current_;
3551 }
3552 return *this;
3553}
3554
3555template <typename Iter1, typename Iter2>
3557constexpr auto ZipAdapter<Iter1, Iter2>::operator++(int) noexcept(
3558 std::is_nothrow_copy_constructible_v<ZipAdapter> &&
3559 noexcept(++std::declval<ZipAdapter&>())) -> ZipAdapter {
3560 auto temp = *this;
3561 ++(*this);
3562 return temp;
3563}
3564
3565template <typename Iter1, typename Iter2>
3567constexpr auto ZipAdapter<Iter1, Iter2>::end() const
3568 noexcept(std::is_nothrow_copy_constructible_v<ZipAdapter>) -> ZipAdapter {
3569 auto result = *this;
3570 result.first_current_ = result.first_end_;
3571 result.second_current_ = result.second_end_;
3572 return result;
3573}
3574
3575/**
3576 * @brief CRTP base class providing common adapter operations.
3577 * @details Provides chaining methods (Filter, Map, Take, Skip, etc.) that can
3578 * be used by any derived adapter class. Uses CRTP pattern to return the correct
3579 * derived type.
3580 * @tparam Derived The derived adapter class
3581 */
3582template <typename Derived>
3584public:
3585 /**
3586 * @brief Chains another filter operation on top of this iterator.
3587 * @tparam Pred Predicate type
3588 * @param predicate Function to filter elements
3589 * @return FilterAdapter that applies this adapter then filters
3590 */
3591 template <typename Pred>
3592 [[nodiscard]] constexpr auto Filter(Pred predicate) const
3593 noexcept(noexcept(FilterAdapter<Derived, Pred>(GetDerived().begin(),
3594 GetDerived().end(),
3595 std::move(predicate)))) {
3597 GetDerived().begin(), GetDerived().end(), std::move(predicate));
3598 }
3599
3600 /**
3601 * @brief Transforms each element using the given function.
3602 * @tparam Func Transformation function type
3603 * @param transform Function to apply to each element
3604 * @return MapAdapter that transforms adapted results
3605 */
3606 template <typename Func>
3607 [[nodiscard]] constexpr auto Map(Func transform) const
3608 noexcept(noexcept(MapAdapter<Derived, Func>(GetDerived().begin(),
3609 GetDerived().end(),
3610 std::move(transform)))) {
3611 return MapAdapter<Derived, Func>(GetDerived().begin(), GetDerived().end(),
3612 std::move(transform));
3613 }
3614
3615 /**
3616 * @brief Limits the number of elements to at most count.
3617 * @param count Maximum number of elements to yield
3618 * @return TakeAdapter that limits adapted results
3619 */
3620 [[nodiscard]] constexpr auto Take(size_t count) const
3621 noexcept(noexcept(TakeAdapter<Derived>(GetDerived().begin(),
3622 GetDerived().end(), count))) {
3623 return TakeAdapter<Derived>(GetDerived().begin(), GetDerived().end(),
3624 count);
3625 }
3626
3627 /**
3628 * @brief Skips the first count elements.
3629 * @param count Number of elements to skip
3630 * @return SkipAdapter that skips adapted results
3631 */
3632 [[nodiscard]] constexpr auto Skip(size_t count) const
3633 noexcept(noexcept(SkipAdapter<Derived>(GetDerived().begin(),
3634 GetDerived().end(), count))) {
3635 return SkipAdapter<Derived>(GetDerived().begin(), GetDerived().end(),
3636 count);
3637 }
3638
3639 /**
3640 * @brief Takes elements while a predicate is true.
3641 * @tparam Pred Predicate type for take-while operation
3642 * @param predicate Predicate to determine when to stop taking
3643 * @return TakeWhileAdapter that conditionally takes elements
3644 */
3645 template <typename Pred>
3646 [[nodiscard]] constexpr auto TakeWhile(Pred predicate) const
3647 noexcept(noexcept(TakeWhileAdapter<Derived, Pred>(
3648 GetDerived().begin(), GetDerived().end(), std::move(predicate)))) {
3650 GetDerived().begin(), GetDerived().end(), std::move(predicate));
3651 }
3652
3653 /**
3654 * @brief Skips elements while a predicate is true.
3655 * @tparam Pred Predicate type for skip-while operation
3656 * @param predicate Predicate to determine when to stop skipping
3657 * @return SkipWhileAdapter that conditionally skips elements
3658 */
3659 template <typename Pred>
3660 [[nodiscard]] constexpr auto SkipWhile(Pred predicate) const
3661 noexcept(noexcept(SkipWhileAdapter<Derived, Pred>(
3662 GetDerived().begin(), GetDerived().end(), std::move(predicate)))) {
3664 GetDerived().begin(), GetDerived().end(), std::move(predicate));
3665 }
3666
3667 /**
3668 * @brief Adds an index to each element.
3669 * @return EnumerateAdapter that pairs indices with values
3670 */
3671 [[nodiscard]] constexpr auto Enumerate() const
3672 noexcept(noexcept(EnumerateAdapter<Derived>(GetDerived().begin(),
3673 GetDerived().end()))) {
3674 return EnumerateAdapter<Derived>(GetDerived().begin(), GetDerived().end());
3675 }
3676
3677 /**
3678 * @brief Observes each element without modifying it.
3679 * @tparam Func Inspector function type
3680 * @param inspector Function to call on each element
3681 * @return InspectAdapter for side effects
3682 */
3683 template <typename Func>
3684 [[nodiscard]] constexpr auto Inspect(Func inspector) const
3685 noexcept(noexcept(InspectAdapter<Derived, Func>(GetDerived().begin(),
3686 GetDerived().end(),
3687 std::move(inspector)))) {
3689 GetDerived().begin(), GetDerived().end(), std::move(inspector));
3690 }
3691
3692 /**
3693 * @brief Takes every Nth element.
3694 * @param step Step size between elements
3695 * @return StepByAdapter that skips elements
3696 */
3697 [[nodiscard]] constexpr auto StepBy(size_t step) const
3698 noexcept(noexcept(StepByAdapter<Derived>(GetDerived().begin(),
3699 GetDerived().end(), step))) {
3700 return StepByAdapter<Derived>(GetDerived().begin(), GetDerived().end(),
3701 step);
3702 }
3703
3704 /**
3705 * @brief Chains another range after this one.
3706 * @tparam OtherIter Iterator type of the other adapter
3707 * @param begin Begin iterator of the other adapter
3708 * @param end End iterator of the other adapter
3709 * @return ChainAdapter that yields elements from both ranges
3710 */
3711 template <typename OtherIter>
3712 [[nodiscard]] constexpr auto Chain(OtherIter begin, OtherIter end) const
3713 noexcept(noexcept(ChainAdapter<Derived, OtherIter>(GetDerived().begin(),
3714 GetDerived().end(),
3715 std::move(begin),
3716 std::move(end)))) {
3718 GetDerived().end(),
3719 std::move(begin), std::move(end));
3720 }
3721
3722 /**
3723 * @brief Chains another range after this one.
3724 * @tparam R Range type of the other adapter
3725 * @param range The other range to chain
3726 * @return ChainAdapter that yields elements from both ranges
3727 */
3728 template <typename R>
3729 [[nodiscard]] constexpr auto Chain(R& range) const
3731 GetDerived().begin(), GetDerived().end(), range))) {
3733 GetDerived().begin(), GetDerived().end(), range);
3734 }
3735
3736 /**
3737 * @brief Chains another range after this one.
3738 * @tparam R Range type of the other adapter
3739 * @param range The other range to chain
3740 * @return ChainAdapter that yields elements from both ranges
3741 */
3742 template <typename R>
3743 [[nodiscard]] constexpr auto Chain(const R& range) const
3745 GetDerived().begin(), GetDerived().end(), range))) {
3747 GetDerived().begin(), GetDerived().end(), range);
3748 }
3749
3750 /**
3751 * @brief Reverses the order of elements.
3752 * @note Requires bidirectional iterator support
3753 * @return ReverseAdapter that yields elements in reverse order
3754 */
3755 [[nodiscard]] constexpr auto Reverse() const
3756 noexcept(noexcept(ReverseAdapter<Derived>(GetDerived().begin(),
3757 GetDerived().end()))) {
3758 return ReverseAdapter<Derived>(GetDerived().begin(), GetDerived().end());
3759 }
3760
3761 /**
3762 * @brief Creates sliding windows over elements.
3763 * @param window_size Size of the sliding window
3764 * @return SlideAdapter that yields windows of elements
3765 * @warning window_size must be greater than 0
3766 */
3767 [[nodiscard]] constexpr auto Slide(size_t window_size) const
3768 noexcept(noexcept(SlideAdapter<Derived>(GetDerived().begin(),
3769 GetDerived().end(),
3770 window_size))) {
3771 return SlideAdapter<Derived>(GetDerived().begin(), GetDerived().end(),
3772 window_size);
3773 }
3774
3775 /**
3776 * @brief Takes every Nth element with stride.
3777 * @param stride Number of elements to skip between yields
3778 * @return StrideAdapter that yields every Nth element
3779 * @warning stride must be greater than 0
3780 */
3781 [[nodiscard]] constexpr auto Stride(size_t stride) const
3782 noexcept(noexcept(StrideAdapter<Derived>(GetDerived().begin(),
3783 GetDerived().end(), stride))) {
3784 return StrideAdapter<Derived>(GetDerived().begin(), GetDerived().end(),
3785 stride);
3786 }
3787
3788 /**
3789 * @brief Zips another range with this one.
3790 * @tparam OtherIter Iterator type to zip with
3791 * @param begin Begin iterator for the other range
3792 * @param end End iterator for the other range
3793 * @return ZipAdapter that yields tuples of corresponding elements
3794 */
3795 template <typename OtherIter>
3796 [[nodiscard]] constexpr auto Zip(OtherIter begin, OtherIter end) const
3797 noexcept(noexcept(ZipAdapter<Derived, OtherIter>(GetDerived().begin(),
3798 GetDerived().end(),
3799 std::move(begin),
3800 std::move(end)))) {
3802 GetDerived().end(), std::move(begin),
3803 std::move((end)));
3804 }
3805
3806 /**
3807 * @brief Zips another range with this one.
3808 * @tparam R Other range
3809 * @param range The other range to zip with
3810 * @return ZipAdapter that
3811 */
3812 template <typename R>
3813 [[nodiscard]] constexpr auto Zip(R& range) const
3815 GetDerived().begin(), GetDerived().end(), range))) {
3817 GetDerived().begin(), GetDerived().end(), range);
3818 }
3819
3820 /**
3821 * @brief Zips another range with this one.
3822 * @tparam R Other range
3823 * @param range The other range to zip with
3824 * @return ZipAdapter that
3825 */
3826 template <typename R>
3827 [[nodiscard]] constexpr auto Zip(const R& range) const
3829 GetDerived().begin(), GetDerived().end(), range))) {
3831 GetDerived().begin(), GetDerived().end(), range);
3832 }
3833
3834 /**
3835 * @brief Terminal operation: applies an action to each element.
3836 * @tparam Action Function type that processes each element
3837 * @param action Function to apply to each element
3838 */
3839 template <typename Action>
3840 constexpr void ForEach(const Action& action) const;
3841
3842 /**
3843 * @brief Terminal operation: reduces elements to a single value using a
3844 * folder function.
3845 * @tparam T Accumulator type
3846 * @tparam Folder Function type that combines accumulator with each element
3847 * @param init Initial accumulator value
3848 * @param folder Function to fold elements
3849 * @return Final accumulated value
3850 */
3851 template <typename T, typename Folder>
3852 [[nodiscard]] constexpr T Fold(T init, const Folder& folder) const;
3853
3854 /**
3855 * @brief Terminal operation: finds the first element satisfying a predicate.
3856 * @tparam Pred Predicate type
3857 * @param predicate Function to test elements
3858 * @return Optional or pointer to the element with maximum key, based on the
3859 * iterator type
3860 */
3861 template <typename Pred>
3862 [[nodiscard]] constexpr auto Find(const Pred& predicate) const;
3863
3864 /**
3865 * @brief Terminal operation: counts elements satisfying a predicate.
3866 * @tparam Pred Predicate type
3867 * @param predicate Function to test elements
3868 * @return Number of elements that satisfy the predicate
3869 */
3870 template <typename Pred>
3871 [[nodiscard]] constexpr size_t CountIf(const Pred& predicate) const;
3872
3873 /**
3874 * @brief Terminal operation: partitions elements into two groups based on a
3875 * predicate.
3876 * @tparam Pred Predicate type
3877 * @param predicate Function to test elements
3878 * @return Pair of vectors: first contains elements satisfying predicate,
3879 * second contains the rest
3880 */
3881 template <typename Pred>
3882 [[nodiscard]] constexpr auto Partition(const Pred& predicate) const;
3883
3884 /**
3885 * @brief Terminal operation: partitions elements with a custom allocator.
3886 * @tparam Pred Predicate type
3887 * @tparam Allocator Allocator for result vectors
3888 * @param predicate Function to test elements
3889 * @param allocator Allocator to use for both result vectors
3890 * @return Pair of vectors using the provided allocator
3891 */
3892 template <typename Pred, typename Allocator>
3893 requires std::same_as<typename Allocator::value_type,
3894 std::iter_value_t<Derived>>
3895 [[nodiscard]] constexpr auto PartitionWith(const Pred& predicate,
3896 Allocator allocator) const;
3897
3898 /**
3899 * @brief Terminal operation: finds the element with the maximum value
3900 * according to a key function.
3901 * @tparam KeyFunc Key extraction function type
3902 * @param key_func Function to extract comparison key from each element
3903 * @return Optional or pointer to the element with maximum key, based on the
3904 * iterator type
3905 */
3906 template <typename KeyFunc>
3907 [[nodiscard]] constexpr auto MaxBy(const KeyFunc& key_func) const;
3908
3909 /**
3910 * @brief Terminal operation: finds the element with the minimum value
3911 * according to a key function.
3912 * @tparam KeyFunc Key extraction function type
3913 * @param key_func Function to extract comparison key from each element
3914 * @return Optional or pointer to the element with maximum key, based on the
3915 * iterator type
3916 */
3917 template <typename KeyFunc>
3918 [[nodiscard]] constexpr auto MinBy(const KeyFunc& key_func) const;
3919
3920 /**
3921 * @brief Terminal operation: groups elements by a key function.
3922 * @tparam KeyFunc Key extraction function type
3923 * @param key_func Function to extract grouping key from each element
3924 * @return Map from keys to vectors of elements with that key
3925 */
3926 template <typename KeyFunc>
3927 [[nodiscard]] constexpr auto GroupBy(const KeyFunc& key_func) const;
3928
3929 /**
3930 * @brief Terminal operation: groups elements by key with custom allocators.
3931 * @tparam KeyFunc Key extraction function type
3932 * @tparam MapAllocator Allocator for unordered_map nodes
3933 * @tparam ValueAllocator Allocator for grouped vectors
3934 * @param key_func Function to extract grouping key from each element
3935 * @param map_allocator Allocator for map storage
3936 * @param value_allocator Allocator for each grouped vector
3937 * @return Map from keys to vectors using provided allocators
3938 */
3939 template <typename KeyFunc, typename MapAllocator, typename ValueAllocator>
3940 [[nodiscard]] constexpr auto GroupByWith(
3941 const KeyFunc& key_func, MapAllocator map_allocator,
3942 ValueAllocator value_allocator) const;
3943
3944 /**
3945 * @brief Terminal operation: collects all elements into a vector.
3946 * @return Vector containing all elements
3947 */
3948 [[nodiscard]] constexpr auto Collect() const;
3949
3950 /**
3951 * @brief Terminal operation: collects all elements into a vector with a
3952 * custom allocator.
3953 * @tparam Allocator Allocator type
3954 * @param allocator Allocator to use for the vector
3955 * @return Vector containing all elements
3956 */
3957 template <typename Allocator>
3958 [[nodiscard]] constexpr auto CollectWith(Allocator allocator = {}) const;
3959
3960 /**
3961 * @brief Terminal operation: collects all elements into a vector with a
3962 * specific memory resource.
3963 * @param resource Memory resource to use for the vector
3964 * @return Vector containing all elements
3965 */
3966 [[nodiscard]] constexpr auto CollectWith(
3967 std::pmr::memory_resource* resource) const;
3968
3969 auto CollectWith(std::nullptr_t) const = delete;
3970
3971 /**
3972 * @brief Terminal operation: writes all elements into an output iterator.
3973 * @details Consumes the adapter and writes each element to the provided
3974 * output iterator. This is more efficient than `Collect()` when you already
3975 * have a destination container.
3976 * @tparam OutIt Output iterator type
3977 * @param out Output iterator to write elements into
3978 *
3979 * @code
3980 * std::vector<int> results;
3981 * query.Filter([](int x) { return x > 5;
3982 * }).Into(std::back_inserter(results));
3983 * @endcode
3984 */
3985 template <typename OutIt>
3986 constexpr void Into(OutIt out) const;
3987
3988 /**
3989 * @brief Terminal operation: checks if any element satisfies a predicate.
3990 * @tparam Pred Predicate type
3991 * @param predicate Function to test elements
3992 * @return True if any element satisfies the predicate, false otherwise
3993 */
3994 template <typename Pred>
3995 [[nodiscard]] constexpr bool Any(const Pred& predicate) const;
3996
3997 /**
3998 * @brief Terminal operation: checks if all elements satisfy a predicate.
3999 * @tparam Pred Predicate type
4000 * @param predicate Function to test elements
4001 * @return True if all elements satisfy the predicate, false otherwise
4002 */
4003 template <typename Pred>
4004 [[nodiscard]] constexpr bool All(const Pred& predicate) const;
4005
4006 /**
4007 * @brief Terminal operation: checks if no elements satisfy a predicate.
4008 * @tparam Pred Predicate type
4009 * @param predicate Function to test elements
4010 * @return True if no elements satisfy the predicate, false otherwise
4011 */
4012 template <typename Pred>
4013 [[nodiscard]] constexpr bool None(const Pred& predicate) const {
4014 return !Any(predicate);
4015 }
4016
4017protected:
4018 /**
4019 * @brief Gets reference to derived class instance.
4020 * @return Reference to derived class
4021 */
4022 [[nodiscard]] constexpr Derived& GetDerived() noexcept {
4023 return static_cast<Derived&>(*this);
4024 }
4025
4026 /**
4027 * @brief Gets const reference to derived class instance.
4028 * @return Const reference to derived class
4029 */
4030 [[nodiscard]] constexpr const Derived& GetDerived() const noexcept {
4031 return static_cast<const Derived&>(*this);
4032 }
4033};
4034
4035template <typename Derived>
4036template <typename Action>
4038 const Action& action) const {
4039 for (auto&& value : GetDerived()) {
4040 if constexpr (std::invocable<Action, decltype(value)>) {
4041 action(std::forward<decltype(value)>(value));
4042 } else {
4043 std::apply(action, std::forward<decltype(value)>(value));
4044 }
4045 }
4046}
4047
4048template <typename Derived>
4049template <typename T, typename Folder>
4051 const Folder& folder) const {
4052 for (auto&& value : GetDerived()) {
4053 if constexpr (std::invocable<Folder, T&&, decltype(value)>) {
4054 init = folder(std::move(init), std::forward<decltype(value)>(value));
4055 } else {
4056 init = std::apply(
4057 [&init, &folder](auto&&... args) {
4058 return folder(std::move(init),
4059 std::forward<decltype(args)>(args)...);
4060 },
4061 std::forward<decltype(value)>(value));
4062 }
4063 }
4064 return init;
4065}
4066
4067template <typename Derived>
4068template <typename Pred>
4070 const Pred& predicate) const {
4071 using IterType = decltype(GetDerived().begin());
4072 using Result = details::find_result_t<IterType>;
4073
4074 for (auto&& value : GetDerived()) {
4075 bool result = false;
4076 if constexpr (std::invocable<Pred, decltype(value)>) {
4077 result = predicate(value);
4078 } else {
4079 result = std::apply(predicate, value);
4080 }
4081 if (result) {
4083 return Result{&value};
4084 } else {
4085 return Result{value};
4086 }
4087 }
4088 }
4089
4091 return Result{nullptr};
4092 } else {
4093 return Result{std::nullopt};
4094 }
4095}
4096
4097template <typename Derived>
4098template <typename Pred>
4100 const Pred& predicate) const {
4101 size_t count = 0;
4102 for (const auto& value : GetDerived()) {
4103 bool result = false;
4104 if constexpr (std::invocable<Pred, decltype(value)>) {
4105 result = predicate(std::forward<decltype(value)>(value));
4106 } else {
4107 result = std::apply(predicate, std::forward<decltype(value)>(value));
4108 }
4109 if (result) {
4110 ++count;
4111 }
4112 }
4113 return count;
4114}
4115
4116template <typename Derived>
4117template <typename Pred>
4119 const Pred& predicate) const {
4120 using ValueType = std::iter_value_t<Derived>;
4121 std::vector<ValueType> matched;
4122 std::vector<ValueType> not_matched;
4123
4124 for (auto&& value : GetDerived()) {
4125 bool result = false;
4126 if constexpr (std::invocable<Pred, decltype(value)>) {
4127 result = predicate(std::forward<decltype(value)>(value));
4128 } else {
4129 result = std::apply(predicate, std::forward<decltype(value)>(value));
4130 }
4131
4132 if (result) {
4133 matched.push_back(std::forward<decltype(value)>(value));
4134 } else {
4135 not_matched.push_back(std::forward<decltype(value)>(value));
4136 }
4137 }
4138
4139 return std::pair{std::move(matched), std::move(not_matched)};
4140}
4141
4142template <typename Derived>
4143template <typename Pred, typename Allocator>
4144 requires std::same_as<typename Allocator::value_type,
4145 std::iter_value_t<Derived>>
4147 const Pred& predicate, Allocator allocator) const {
4148 using ValueType = std::iter_value_t<Derived>;
4149 std::vector<ValueType, Allocator> matched(allocator);
4150 std::vector<ValueType, Allocator> not_matched(std::move(allocator));
4151
4152 for (auto&& value : GetDerived()) {
4153 bool result = false;
4154 if constexpr (std::invocable<Pred, decltype(value)>) {
4155 result = predicate(std::forward<decltype(value)>(value));
4156 } else {
4157 result = std::apply(predicate, std::forward<decltype(value)>(value));
4158 }
4159
4160 if (result) {
4161 matched.push_back(std::forward<decltype(value)>(value));
4162 } else {
4163 not_matched.push_back(std::forward<decltype(value)>(value));
4164 }
4165 }
4166
4167 return std::pair{std::move(matched), std::move(not_matched)};
4168}
4169
4170template <typename Derived>
4171template <typename KeyFunc>
4173 const KeyFunc& key_func) const {
4174 using IterType = decltype(GetDerived().begin());
4175 using Result = details::find_result_t<IterType>;
4176
4177 auto iter = GetDerived().begin();
4178 const auto end_iter = GetDerived().end();
4179
4180 if (iter == end_iter) {
4182 return Result{nullptr};
4183 } else {
4184 return Result{std::nullopt};
4185 }
4186 }
4187
4188 auto get_key = [&key_func](auto&& val) {
4189 if constexpr (std::invocable<KeyFunc, decltype(val)>) {
4190 return key_func(std::forward<decltype(val)>(val));
4191 } else {
4192 return std::apply(key_func, std::forward<decltype(val)>(val));
4193 }
4194 };
4195
4196 Result max_element;
4198 max_element = &(*iter);
4199 } else {
4200 max_element.emplace(*iter);
4201 }
4202
4203 auto max_key = get_key(*max_element);
4204 ++iter;
4205
4206 while (iter != end_iter) {
4207 auto current_key = get_key(*iter);
4208 if (current_key > max_key) {
4209 max_key = std::move(current_key);
4211 max_element = &(*iter);
4212 } else {
4213 max_element.emplace(*iter);
4214 }
4215 }
4216 ++iter;
4217 }
4218 return max_element;
4219}
4220
4221template <typename Derived>
4222template <typename KeyFunc>
4224 const KeyFunc& key_func) const {
4225 using IterType = decltype(GetDerived().begin());
4226 using Result = details::find_result_t<IterType>;
4227
4228 auto iter = GetDerived().begin();
4229 const auto end_iter = GetDerived().end();
4230
4231 if (iter == end_iter) {
4233 return Result{nullptr};
4234 } else {
4235 return Result{std::nullopt};
4236 }
4237 }
4238
4239 auto get_key = [&key_func](auto&& val) {
4240 if constexpr (std::invocable<KeyFunc, decltype(val)>) {
4241 return key_func(std::forward<decltype(val)>(val));
4242 } else {
4243 return std::apply(key_func, std::forward<decltype(val)>(val));
4244 }
4245 };
4246
4247 Result min_element;
4249 min_element = &(*iter);
4250 } else {
4251 min_element.emplace(*iter);
4252 }
4253
4254 auto min_key = get_key(*min_element);
4255 ++iter;
4256
4257 while (iter != end_iter) {
4258 auto current_key = get_key(*iter);
4259 if (current_key < min_key) {
4260 min_key = std::move(current_key);
4262 min_element = &(*iter);
4263 } else {
4264 min_element.emplace(*iter);
4265 }
4266 }
4267 ++iter;
4268 }
4269
4270 return min_element;
4271}
4272
4273template <typename Derived>
4274template <typename KeyFunc>
4276 const KeyFunc& key_func) const {
4277 using ValueType = std::iter_value_t<Derived>;
4278 using KeyType = std::decay_t<std::invoke_result_t<KeyFunc, ValueType>>;
4279 std::unordered_map<KeyType, std::vector<ValueType>> groups;
4280
4281 for (auto&& value : GetDerived()) {
4282 KeyType key;
4283 if constexpr (std::invocable<KeyFunc, decltype(value)>) {
4284 key = key_func(std::forward<decltype(value)>(value));
4285 } else {
4286 key = std::apply(key_func, std::forward<decltype(value)>(value));
4287 }
4288 groups[std::move(key)].push_back(std::forward<decltype(value)>(value));
4289 }
4290
4291 return groups;
4292}
4293
4294template <typename Derived>
4295template <typename KeyFunc, typename MapAllocator, typename ValueAllocator>
4297 const KeyFunc& key_func, MapAllocator map_allocator,
4298 ValueAllocator value_allocator) const {
4299 using ValueType = std::iter_value_t<Derived>;
4300 using KeyType =
4301 std::decay_t<details::call_or_apply_result_t<const KeyFunc&, ValueType>>;
4302 using GroupVector = std::vector<ValueType, ValueAllocator>;
4303 using MapValueType = std::pair<const KeyType, GroupVector>;
4304 static_assert(std::same_as<typename MapAllocator::value_type, MapValueType>,
4305 "Map allocator value_type must match map value type");
4306 static_assert(std::same_as<typename ValueAllocator::value_type, ValueType>,
4307 "Value allocator value_type must match grouped element type");
4308
4309 std::unordered_map<KeyType, GroupVector, std::hash<KeyType>, std::equal_to<>,
4310 MapAllocator>
4311 groups(0, std::hash<KeyType>{}, std::equal_to<>{},
4312 std::move(map_allocator));
4313
4314 for (auto&& value : GetDerived()) {
4315 KeyType key;
4316 if constexpr (std::invocable<KeyFunc, decltype(value)>) {
4317 key = key_func(std::forward<decltype(value)>(value));
4318 } else {
4319 key = std::apply(key_func, std::forward<decltype(value)>(value));
4320 }
4321
4322 auto [iter, inserted] = groups.try_emplace(std::move(key));
4323 if (inserted) {
4324 iter->second = GroupVector(value_allocator);
4325 }
4326 iter->second.push_back(std::forward<decltype(value)>(value));
4327 }
4328
4329 return groups;
4330}
4331
4332template <typename Derived>
4334 using ValueType = std::iter_value_t<Derived>;
4335 std::vector<ValueType> result;
4336 result.reserve(static_cast<size_t>(
4337 std::distance(GetDerived().begin(), GetDerived().end())));
4338 for (auto&& value : GetDerived()) {
4339 result.push_back(std::forward<decltype(value)>(value));
4340 }
4341 return result;
4342}
4343
4344template <typename Derived>
4345template <typename Allocator>
4347 Allocator allocator) const {
4348 using ValueType = std::iter_value_t<Derived>;
4349 std::vector<ValueType, Allocator> result{std::move(allocator)};
4350 result.reserve(static_cast<size_t>(
4351 std::distance(GetDerived().begin(), GetDerived().end())));
4352 for (auto&& value : GetDerived()) {
4353 result.push_back(std::forward<decltype(value)>(value));
4354 }
4355 return result;
4356}
4357
4358template <typename Derived>
4360 std::pmr::memory_resource* resource) const {
4361 using ValueType = std::iter_value_t<Derived>;
4362 return CollectWith(std::pmr::polymorphic_allocator<ValueType>{resource});
4363}
4364
4365template <typename Derived>
4366template <typename OutIt>
4367constexpr void FunctionalAdapterBase<Derived>::Into(OutIt out) const {
4368 for (auto&& value : GetDerived()) {
4369 *out++ = std::forward<decltype(value)>(value);
4370 }
4371}
4372
4373template <typename Derived>
4374template <typename Pred>
4376 const Pred& predicate) const {
4377 for (const auto& value : GetDerived()) {
4378 bool result = false;
4379 if constexpr (std::invocable<Pred, decltype(value)>) {
4380 result = predicate(std::forward<decltype(value)>(value));
4381 } else {
4382 result = std::apply(predicate, std::forward<decltype(value)>(value));
4383 }
4384 if (result) {
4385 return true;
4386 }
4387 }
4388 return false;
4389}
4390
4391template <typename Derived>
4392template <typename Pred>
4394 const Pred& predicate) const {
4395 for (const auto& value : GetDerived()) {
4396 bool result = false;
4397 if constexpr (std::invocable<Pred, decltype(value)>) {
4398 result = predicate(std::forward<decltype(value)>(value));
4399 } else {
4400 result = std::apply(predicate, std::forward<decltype(value)>(value));
4401 }
4402 if (!result) {
4403 return false;
4404 }
4405 }
4406 return true;
4407}
4408
4409} // namespace helios::utils
Iterator adapter that chains two sequences together.
constexpr ChainAdapter end() const noexcept(std::is_nothrow_copy_constructible_v< ChainAdapter > &&std::is_nothrow_copy_constructible_v< Iter1 > &&std::is_nothrow_copy_constructible_v< Iter2 >)
std::common_type_t< std::iter_difference_t< Iter1 >, std::iter_difference_t< Iter2 > > difference_type
constexpr bool operator==(const ChainAdapter &other) const noexcept(noexcept(std::declval< const Iter1 & >()==std::declval< const Iter1 & >()) &&noexcept(std::declval< const Iter2 & >()==std::declval< const Iter2 & >()))
constexpr ChainAdapter & operator++() noexcept(noexcept(++std::declval< Iter1 & >()) &&noexcept(++std::declval< Iter2 & >()) &&noexcept(std::declval< Iter1 & >()==std::declval< Iter1 & >()) &&noexcept(std::declval< Iter2 & >() !=std::declval< Iter2 & >()))
std::conditional_t< details::IterYieldsReference< Iter1 > && details::IterYieldsReference< Iter2 >, std::forward_iterator_tag, std::input_iterator_tag > iterator_concept
constexpr ChainAdapter(const ChainAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter1 > &&std::is_nothrow_copy_constructible_v< Iter2 >)=default
constexpr bool operator!=(const ChainAdapter &other) const noexcept(noexcept(std::declval< const ChainAdapter & >()==std::declval< const ChainAdapter & >()))
constexpr ChainAdapter(Iter1 first_begin, Iter1 first_end, Iter2 second_begin, Iter2 second_end) noexcept(std::is_nothrow_move_constructible_v< Iter1 > &&std::is_nothrow_move_constructible_v< Iter2 > &&noexcept(std::declval< Iter1 & >() !=std::declval< Iter1 & >()))
Constructs a chain adapter with two iterator ranges.
std::iter_value_t< Iter1 > value_type
constexpr ChainAdapter(const R1 &first_range, const R2 &second_range) noexcept(noexcept(ChainAdapter(std::ranges::cbegin(first_range), std::ranges::cend(first_range), std::ranges::cbegin(second_range), std::ranges::cend(second_range))))
Constructs a chain adapter with the given const ranges.
pointer operator->() const =delete
constexpr ChainAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< ChainAdapter >)
constexpr ChainAdapter(ChainAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter1 > &&std::is_nothrow_move_constructible_v< Iter2 >)=default
constexpr ChainAdapter(R1 &first_range, R2 &second_range) noexcept(noexcept(ChainAdapter(std::ranges::begin(first_range), std::ranges::end(first_range), std::ranges::begin(second_range), std::ranges::end(second_range))))
Constructs a chain adapter with the given ranges.
std::conditional_t< details::IterYieldsReference< Iter1 > && details::IterYieldsReference< Iter2 >, std::forward_iterator_tag, std::input_iterator_tag > iterator_category
Iterator adapter that adds index information to each element.
constexpr EnumerateAdapter(EnumerateAdapter &&)=default
constexpr EnumerateAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< EnumerateAdapter >)
std::iter_difference_t< Iter > difference_type
constexpr EnumerateAdapter(const R &range) noexcept(noexcept(EnumerateAdapter(std::ranges::cbegin(range), std::ranges::cend(range))))
Constructs a enumerate adapter from a const range.
constexpr EnumerateAdapter & operator=(const EnumerateAdapter &)=default
typename MakeEnumeratedValue< std::iter_value_t< Iter > >::type value_type
constexpr EnumerateAdapter & operator++() noexcept(noexcept(++std::declval< Iter & >()))
constexpr EnumerateAdapter & operator=(EnumerateAdapter &&)=default
constexpr EnumerateAdapter(Iter begin, Iter end) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Iter >)
Constructs an enumerate adapter with the given iterator range.
constexpr ~EnumerateAdapter()=default
constexpr EnumerateAdapter end() const noexcept(std::is_nothrow_constructible_v< EnumerateAdapter, const Iter &, const Iter & >)
constexpr bool operator!=(const EnumerateAdapter &other) const noexcept(noexcept(std::declval< const EnumerateAdapter & >()==std::declval< const EnumerateAdapter & >()))
constexpr reference operator*() const
constexpr EnumerateAdapter(const EnumerateAdapter &)=default
constexpr EnumerateAdapter(R &range) noexcept(noexcept(EnumerateAdapter(std::ranges::begin(range), std::ranges::end(range))))
Constructs a enumerate adapter from a range.
details::adapter_iterator_category_t< Iter > iterator_category
details::adapter_iterator_concept_t< Iter > iterator_concept
Iterator adapter that filters elements based on a predicate function.
std::iter_value_t< Iter > value_type
constexpr FilterAdapter(FilterAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_move_constructible_v< Pred >)=default
details::adapter_iterator_concept_t< Iter > iterator_concept
constexpr FilterAdapter end() const noexcept(std::is_nothrow_constructible_v< FilterAdapter, const Iter &, const Iter &, const Pred & >)
std::iter_difference_t< Iter > difference_type
details::adapter_iterator_category_t< Iter > iterator_category
constexpr bool operator!=(const FilterAdapter &other) const noexcept(noexcept(std::declval< const FilterAdapter & >()==std::declval< const FilterAdapter & >()))
pointer operator->() const =delete
constexpr bool operator==(const FilterAdapter &other) const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
constexpr FilterAdapter(Iter begin, Iter end, Pred predicate) noexcept(std::is_nothrow_copy_constructible_v< Iter > &&std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_move_constructible_v< Pred > &&noexcept(AdvanceToValid()))
Constructs a filter adapter with the given iterator range and predicate.
constexpr FilterAdapter(R &range, Pred predicate) noexcept(noexcept(FilterAdapter(std::ranges::begin(range), std::ranges::end(range), std::move(predicate))))
Constructs a filter adapter from a range.
constexpr FilterAdapter & operator++() noexcept(noexcept(++std::declval< Iter & >()) &&noexcept(AdvanceToValid()))
constexpr FilterAdapter(const R &range, Pred predicate) noexcept(noexcept(FilterAdapter(std::ranges::cbegin(range), std::ranges::cend(range), std::move(predicate))))
Constructs a filter adapter from a const range.
constexpr FilterAdapter(const FilterAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Pred >)=default
constexpr bool IsAtEnd() const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
Checks if the iterator has reached the end.
constexpr FilterAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< FilterAdapter >)
CRTP base class providing common adapter operations.
constexpr auto Chain(R &range) const noexcept(noexcept(ChainAdapter< Derived, std::ranges::iterator_t< R > >(GetDerived().begin(), GetDerived().end(), range)))
Chains another range after this one.
constexpr size_t CountIf(const Pred &predicate) const
Terminal operation: counts elements satisfying a predicate.
constexpr auto CollectWith(Allocator allocator={}) const
Terminal operation: collects all elements into a vector with a custom allocator.
auto CollectWith(std::nullptr_t) const =delete
constexpr auto Enumerate() const noexcept(noexcept(EnumerateAdapter< Derived >(GetDerived().begin(), GetDerived().end())))
Adds an index to each element.
constexpr auto GroupByWith(const KeyFunc &key_func, MapAllocator map_allocator, ValueAllocator value_allocator) const
Terminal operation: groups elements by key with custom allocators.
constexpr auto Zip(R &range) const noexcept(noexcept(ZipAdapter< Derived, std::ranges::iterator_t< R > >(GetDerived().begin(), GetDerived().end(), range)))
Zips another range with this one.
constexpr bool All(const Pred &predicate) const
Terminal operation: checks if all elements satisfy a predicate.
constexpr auto PartitionWith(const Pred &predicate, Allocator allocator) const
Terminal operation: partitions elements with a custom allocator.
constexpr auto GroupBy(const KeyFunc &key_func) const
Terminal operation: groups elements by a key function.
constexpr auto Map(Func transform) const noexcept(noexcept(MapAdapter< Derived, Func >(GetDerived().begin(), GetDerived().end(), std::move(transform))))
Transforms each element using the given function.
constexpr auto MaxBy(const KeyFunc &key_func) const
Terminal operation: finds the element with the maximum value according to a key function.
constexpr auto TakeWhile(Pred predicate) const noexcept(noexcept(TakeWhileAdapter< Derived, Pred >(GetDerived().begin(), GetDerived().end(), std::move(predicate))))
Takes elements while a predicate is true.
constexpr auto Reverse() const noexcept(noexcept(ReverseAdapter< Derived >(GetDerived().begin(), GetDerived().end())))
Reverses the order of elements.
constexpr void Into(OutIt out) const
Terminal operation: writes all elements into an output iterator.
constexpr Derived & GetDerived() noexcept
Gets reference to derived class instance.
constexpr auto Stride(size_t stride) const noexcept(noexcept(StrideAdapter< Derived >(GetDerived().begin(), GetDerived().end(), stride)))
Takes every Nth element with stride.
constexpr auto Inspect(Func inspector) const noexcept(noexcept(InspectAdapter< Derived, Func >(GetDerived().begin(), GetDerived().end(), std::move(inspector))))
Observes each element without modifying it.
constexpr bool None(const Pred &predicate) const
Terminal operation: checks if no elements satisfy a predicate.
constexpr auto Find(const Pred &predicate) const
Terminal operation: finds the first element satisfying a predicate.
constexpr auto Partition(const Pred &predicate) const
Terminal operation: partitions elements into two groups based on a predicate.
constexpr auto Collect() const
Terminal operation: collects all elements into a vector.
constexpr auto Chain(const R &range) const noexcept(noexcept(ChainAdapter< Derived, std::ranges::iterator_t< const R > >(GetDerived().begin(), GetDerived().end(), range)))
Chains another range after this one.
constexpr auto Chain(OtherIter begin, OtherIter end) const noexcept(noexcept(ChainAdapter< Derived, OtherIter >(GetDerived().begin(), GetDerived().end(), std::move(begin), std::move(end))))
Chains another range after this one.
constexpr auto SkipWhile(Pred predicate) const noexcept(noexcept(SkipWhileAdapter< Derived, Pred >(GetDerived().begin(), GetDerived().end(), std::move(predicate))))
Skips elements while a predicate is true.
constexpr auto Zip(OtherIter begin, OtherIter end) const noexcept(noexcept(ZipAdapter< Derived, OtherIter >(GetDerived().begin(), GetDerived().end(), std::move(begin), std::move(end))))
Zips another range with this one.
constexpr auto MinBy(const KeyFunc &key_func) const
Terminal operation: finds the element with the minimum value according to a key function.
constexpr auto Skip(size_t count) const noexcept(noexcept(SkipAdapter< Derived >(GetDerived().begin(), GetDerived().end(), count)))
Skips the first count elements.
constexpr auto Filter(Pred predicate) const noexcept(noexcept(FilterAdapter< Derived, Pred >(GetDerived().begin(), GetDerived().end(), std::move(predicate))))
Chains another filter operation on top of this iterator.
constexpr auto Slide(size_t window_size) const noexcept(noexcept(SlideAdapter< Derived >(GetDerived().begin(), GetDerived().end(), window_size)))
Creates sliding windows over elements.
constexpr auto StepBy(size_t step) const noexcept(noexcept(StepByAdapter< Derived >(GetDerived().begin(), GetDerived().end(), step)))
Takes every Nth element.
constexpr void ForEach(const Action &action) const
Terminal operation: applies an action to each element.
constexpr T Fold(T init, const Folder &folder) const
Terminal operation: reduces elements to a single value using a folder function.
constexpr auto Zip(const R &range) const noexcept(noexcept(ZipAdapter< Derived, std::ranges::iterator_t< const R > >(GetDerived().begin(), GetDerived().end(), range)))
Zips another range with this one.
constexpr const Derived & GetDerived() const noexcept
Gets const reference to derived class instance.
constexpr bool Any(const Pred &predicate) const
Terminal operation: checks if any element satisfies a predicate.
constexpr auto Take(size_t count) const noexcept(noexcept(TakeAdapter< Derived >(GetDerived().begin(), GetDerived().end(), count)))
Limits the number of elements to at most count.
Iterator adapter that applies a function to each element for observation.
constexpr InspectAdapter(R &range, Func inspector) noexcept(noexcept(InspectAdapter(std::ranges::begin(range), std::ranges::end(range), std::move(inspector))))
Constructs an inspect adapter with the given range and inspector function.
constexpr InspectAdapter & operator++() noexcept(noexcept(++std::declval< Iter & >()))
details::adapter_iterator_category_t< Iter > iterator_category
constexpr reference operator*() const noexcept(std::is_nothrow_invocable_v< Func, std::iter_value_t< Iter > > &&noexcept(*std::declval< const Iter & >()))
constexpr InspectAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< InspectAdapter >)
constexpr InspectAdapter(InspectAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_move_constructible_v< Func >)=default
constexpr InspectAdapter end() const noexcept(std::is_nothrow_constructible_v< InspectAdapter, const Iter &, const Iter &, const Func & >)
constexpr InspectAdapter(Iter begin, Iter end, Func inspector) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Iter > &&std::is_nothrow_move_constructible_v< Func >)
Constructs an inspect adapter with the given iterator range and inspector function.
details::adapter_iterator_concept_t< Iter > iterator_concept
constexpr bool operator!=(const InspectAdapter &other) const noexcept(noexcept(std::declval< const InspectAdapter & >()==std::declval< const InspectAdapter & >()))
std::iter_difference_t< Iter > difference_type
std::iter_value_t< Iter > value_type
constexpr InspectAdapter(const InspectAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Func >)=default
constexpr InspectAdapter(const R &range, Func inspector) noexcept(noexcept(InspectAdapter(std::ranges::cbegin(range), std::ranges::cend(range), std::move(inspector))))
Constructs an inspect adapter with the given const range and inspector function.
Iterator adapter that transforms each element using a function.
constexpr MapAdapter(Iter begin, Iter end, Func transform) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_move_constructible_v< Func >)
Constructs a map adapter with the given iterator range and transform function.
constexpr reference operator*() const noexcept(std::is_nothrow_invocable_v< Func, std::iter_value_t< Iter > > &&noexcept(*std::declval< const Iter & >()))
constexpr MapAdapter(MapAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_move_constructible_v< Func >)=default
constexpr MapAdapter end() const noexcept(std::is_nothrow_constructible_v< MapAdapter, const Iter &, const Iter &, const Func & >)
constexpr MapAdapter & operator++() noexcept(noexcept(++std::declval< Iter & >()))
constexpr MapAdapter(const R &range, Func transform) noexcept(noexcept(MapAdapter(std::ranges::cbegin(range), std::ranges::cend(range), std::move(transform))))
Constructs a map adapter from a const range and transform function.
constexpr MapAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< MapAdapter >)
constexpr bool operator!=(const MapAdapter &other) const noexcept(noexcept(std::declval< const MapAdapter & >()==std::declval< const MapAdapter & >()))
typename DeduceValueType< std::iter_value_t< Iter > >::Type value_type
std::iter_difference_t< Iter > difference_type
std::input_iterator_tag iterator_concept
std::input_iterator_tag iterator_category
constexpr MapAdapter(R &range, Func transform) noexcept(noexcept(MapAdapter(std::ranges::begin(range), std::ranges::end(range), std::move(transform))))
Constructs a map adapter from a range and transform function.
constexpr MapAdapter(const MapAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Func >)=default
Adapter that iterates through elements in reverse order.
constexpr ReverseAdapter(Iter begin, Iter end) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Iter > &&noexcept(std::declval< Iter & >()==std::declval< Iter & >()) &&noexcept(--std::declval< Iter & >()))
Constructs a reverse adapter.
std::iter_value_t< Iter > value_type
std::iter_difference_t< Iter > difference_type
pointer operator->() const =delete
constexpr ReverseAdapter(const ReverseAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter >)=default
constexpr ReverseAdapter(R &range) noexcept(noexcept(ReverseAdapter(std::ranges::begin(range), std::ranges::end(range))))
Constructs a reverse adapter with the given range.
constexpr ReverseAdapter end() const noexcept(std::is_nothrow_copy_constructible_v< ReverseAdapter >)
constexpr ReverseAdapter & operator--() noexcept(std::is_nothrow_copy_constructible_v< Iter > &&noexcept(++std::declval< Iter & >()) &&noexcept(std::declval< Iter & >() !=std::declval< Iter & >()))
constexpr ReverseAdapter & operator++() noexcept(noexcept(std::declval< Iter & >()==std::declval< Iter & >()) &&noexcept(--std::declval< Iter & >()))
constexpr ReverseAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< ReverseAdapter >)
constexpr bool operator==(const ReverseAdapter &other) const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
constexpr bool operator!=(const ReverseAdapter &other) const noexcept(noexcept(std::declval< ReverseAdapter >()==std::declval< ReverseAdapter >()))
constexpr ReverseAdapter(ReverseAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter >)=default
std::conditional_t< details::IterYieldsReference< Iter >, std::bidirectional_iterator_tag, std::input_iterator_tag > iterator_category
constexpr ReverseAdapter(const R &range) noexcept(noexcept(ReverseAdapter(std::ranges::cbegin(range), std::ranges::cend(range))))
Constructs a reverse adapter with the given const range.
std::conditional_t< details::IterYieldsReference< Iter >, std::bidirectional_iterator_tag, std::input_iterator_tag > iterator_concept
Iterator adapter that skips the first N elements.
constexpr SkipAdapter & operator++() noexcept(noexcept(++std::declval< Iter & >()))
pointer operator->() const =delete
std::iter_value_t< Iter > value_type
constexpr SkipAdapter(const R &range, size_t count) noexcept(noexcept(SkipAdapter(std::ranges::cbegin(range), std::ranges::cend(range), count)))
Constructs a skip adapter from a const range and count.
constexpr SkipAdapter end() const noexcept(std::is_nothrow_constructible_v< SkipAdapter, const Iter &, const Iter &, size_t >)
constexpr SkipAdapter(Iter begin, Iter end, size_t count) noexcept(std::is_nothrow_move_constructible_v< Iter > &&noexcept(++std::declval< Iter & >()))
Constructs a skip adapter with the given iterator range and count.
constexpr SkipAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< SkipAdapter >)
details::adapter_iterator_concept_t< Iter > iterator_concept
std::iter_difference_t< Iter > difference_type
constexpr bool operator==(const SkipAdapter &other) const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
constexpr bool operator!=(const SkipAdapter &other) const noexcept(noexcept(std::declval< const SkipAdapter & >()==std::declval< const SkipAdapter & >()))
constexpr SkipAdapter(SkipAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter >)=default
constexpr SkipAdapter(R &range, size_t count) noexcept(noexcept(SkipAdapter(std::ranges::begin(range), std::ranges::end(range), count)))
Constructs a skip adapter from a range and count.
constexpr SkipAdapter(const SkipAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter >)=default
details::adapter_iterator_category_t< Iter > iterator_category
Iterator adapter that skips elements while a predicate returns true.
constexpr SkipWhileAdapter end() const noexcept(std::is_nothrow_constructible_v< SkipWhileAdapter, const Iter &, const Iter &, const Pred & >)
details::adapter_iterator_concept_t< Iter > iterator_concept
constexpr SkipWhileAdapter(const R &range, Pred predicate) noexcept(noexcept(SkipWhileAdapter(std::ranges::cbegin(range), std::ranges::cend(range), std::move(predicate))))
Constructs a skip-while adapter from a const range and predicate.
constexpr SkipWhileAdapter(SkipWhileAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_move_constructible_v< Pred >)=default
constexpr SkipWhileAdapter(Iter begin, Iter end, Pred predicate) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_move_constructible_v< Pred > &&noexcept(AdvancePastSkipped()))
Constructs a skip-while adapter with the given iterator range and predicate.
constexpr bool operator!=(const SkipWhileAdapter &other) const noexcept(noexcept(std::declval< const SkipWhileAdapter & >()==std::declval< const SkipWhileAdapter & >()))
std::iter_difference_t< Iter > difference_type
constexpr SkipWhileAdapter(R &range, Pred predicate) noexcept(noexcept(SkipWhileAdapter(std::ranges::begin(range), std::ranges::end(range), std::move(predicate))))
Constructs a skip-while adapter from a range and predicate.
pointer operator->() const =delete
constexpr SkipWhileAdapter(const SkipWhileAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Pred >)=default
constexpr bool operator==(const SkipWhileAdapter &other) const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
constexpr SkipWhileAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< SkipWhileAdapter >)
details::adapter_iterator_category_t< Iter > iterator_category
constexpr SkipWhileAdapter & operator++() noexcept(noexcept(++std::declval< Iter & >()))
Adapter that yields sliding windows of elements.
std::forward_iterator_tag iterator_concept
constexpr SlideAdapter(const R &range, size_t window_size) noexcept(noexcept(SlideAdapter(std::ranges::cbegin(range), std::ranges::cend(range), window_size)))
Constructs a slide adapter from a const range.
constexpr SlideAdapter(R &range, size_t window_size) noexcept(noexcept(SlideAdapter(std::ranges::begin(range), std::ranges::end(range), window_size)))
Constructs a slide adapter from a range.
std::iter_difference_t< Iter > difference_type
constexpr bool operator==(const SlideAdapter &other) const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
pointer operator->() const =delete
constexpr size_t WindowSize() const noexcept
Returns the window size.
constexpr SlideAdapter(SlideAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter >)=default
constexpr SlideAdapter(Iter begin, Iter end, size_t window_size) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Iter > &&noexcept(std::declval< Iter & >() !=std::declval< Iter & >()) &&noexcept(++std::declval< Iter & >()))
Constructs a slide adapter.
constexpr SlideAdapter(const SlideAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter >)=default
constexpr bool operator!=(const SlideAdapter &other) const noexcept(noexcept(std::declval< const Iter & >() !=std::declval< const Iter & >()))
constexpr SlideAdapter end() const noexcept(std::is_nothrow_copy_constructible_v< SlideAdapter > &&std::is_nothrow_copy_assignable_v< Iter > &&noexcept(std::declval< Iter & >() !=std::declval< const Iter & >()) &&noexcept(++std::declval< Iter & >()))
constexpr SlideAdapter & operator++() noexcept(noexcept(std::declval< Iter & >() !=std::declval< Iter & >()) &&noexcept(++std::declval< Iter & >()))
constexpr SlideAdapter begin() const noexcept(std::is_nothrow_constructible_v< SlideAdapter, const Iter &, const Iter &, size_t >)
std::input_iterator_tag iterator_category
Adapter that flattens nested ranges into a single sequence.
constexpr size_t Size() const noexcept
Returns the size of the window.
constexpr SlideView(const SlideView &) noexcept(std::is_nothrow_copy_constructible_v< Iter >)=default
constexpr Iter end() const noexcept(std::is_nothrow_copy_constructible_v< Iter > &&noexcept(++std::declval< Iter & >()))
Returns an iterator to the end.
constexpr auto Collect() const -> std::vector< value_type >
Collects the window elements into a vector.
constexpr bool Empty() const noexcept
Checks if the window is empty.
constexpr bool operator==(const SlideView &other) const
Compares two SlideViews for equality.
constexpr SlideView(Iter begin, size_t size) noexcept(std::is_nothrow_copy_constructible_v< Iter >)
Constructs a SlideView.
constexpr SlideView(SlideView &&) noexcept(std::is_nothrow_move_constructible_v< Iter >)=default
std::iter_value_t< Iter > value_type
constexpr auto CollectWith(Allocator allocator={}) const -> std::vector< value_type, Allocator > std constexpr auto CollectWith(std::pmr::memory_resource *resource) const -> std::pmr::vector< value_type > std auto CollectWith(std::nullptr_t) const -> std::pmr::vector< value_type >=delete
Collects the window elements into a vector with a custom allocator.
constexpr Iter begin() const noexcept(std::is_nothrow_copy_constructible_v< Iter >)
Returns an iterator to the beginning.
constexpr reference operator[](size_t index) const noexcept(noexcept(*std::declval< Iter >()) &&noexcept(++std::declval< Iter & >()))
Accesses an element by index.
constexpr bool operator==(const R &range) const
Compares SlideView with a vector for equality.
decltype(*std::declval< Iter >()) reference
Iterator adapter that steps through elements by a specified stride.
pointer operator->() const =delete
std::iter_value_t< Iter > value_type
constexpr StepByAdapter & operator=(const StepByAdapter &)=default
constexpr StepByAdapter & operator=(StepByAdapter &&)=default
constexpr StepByAdapter end() const noexcept(std::is_nothrow_constructible_v< StepByAdapter, const Iter &, const Iter &, size_t >)
constexpr StepByAdapter(StepByAdapter &&)=default
constexpr StepByAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< StepByAdapter >)
std::iter_difference_t< Iter > difference_type
details::adapter_iterator_category_t< Iter > iterator_category
constexpr StepByAdapter(Iter begin, Iter end, size_t step) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Iter >)
Constructs a step-by adapter with the given iterator range and step size.
constexpr StepByAdapter(R &range, size_t step) noexcept(noexcept(StepByAdapter(std::ranges::begin(range), std::ranges::end(range), step)))
Constructs a step-by adapter with the given range and step size.
constexpr StepByAdapter & operator++() noexcept(noexcept(++std::declval< Iter & >()) &&noexcept(std::declval< Iter & >() !=std::declval< Iter & >()))
constexpr StepByAdapter(const R &range, size_t step) noexcept(noexcept(StepByAdapter(std::ranges::cbegin(range), std::ranges::cend(range), step)))
Constructs a step-by adapter with the given const range and step size.
constexpr bool operator!=(const StepByAdapter &other) const noexcept(noexcept(std::declval< const StepByAdapter & >()==std::declval< const StepByAdapter & >()))
details::adapter_iterator_concept_t< Iter > iterator_concept
constexpr StepByAdapter(const StepByAdapter &)=default
constexpr ~StepByAdapter()=default
constexpr bool operator==(const StepByAdapter &other) const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
Adapter that yields every Nth element from the range.
constexpr StrideAdapter(Iter begin, Iter end, size_t stride) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Iter >)
Constructs a stride adapter.
constexpr bool operator==(const StrideAdapter &other) const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
constexpr StrideAdapter(const R &range, size_t stride) noexcept(noexcept(StrideAdapter(std::ranges::cbegin(range), std::ranges::cend(range), stride)))
Constructs a stride adapter from a const range and a stride.
constexpr StrideAdapter begin() const noexcept(std::is_nothrow_constructible_v< StrideAdapter, const Iter &, const Iter &, size_t >)
constexpr bool operator!=(const StrideAdapter &other) const noexcept(noexcept(std::declval< const Iter & >() !=std::declval< const Iter & >()))
std::iter_difference_t< Iter > difference_type
details::adapter_iterator_category_t< Iter > iterator_category
constexpr StrideAdapter(const StrideAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter >)=default
constexpr StrideAdapter(StrideAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter >)=default
constexpr StrideAdapter end() const noexcept(std::is_nothrow_copy_constructible_v< StrideAdapter > &&std::is_nothrow_copy_assignable_v< Iter >)
details::adapter_iterator_concept_t< Iter > iterator_concept
constexpr StrideAdapter & operator++() noexcept(noexcept(std::declval< Iter & >() !=std::declval< Iter & >()) &&noexcept(++std::declval< Iter & >()))
constexpr StrideAdapter(R &range, size_t stride) noexcept(noexcept(StrideAdapter(std::ranges::begin(range), std::ranges::end(range), stride)))
Constructs a stride adapter from a range and a stride.
pointer operator->() const =delete
std::iter_value_t< Iter > value_type
Iterator adapter that yields only the first N elements.
constexpr TakeAdapter(TakeAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter >)=default
details::adapter_iterator_category_t< Iter > iterator_category
constexpr TakeAdapter(R &range, size_t count) noexcept(noexcept(TakeAdapter(std::ranges::begin(range), std::ranges::end(range), count)))
Constructs a take adapter from a range and count.
constexpr bool operator!=(const TakeAdapter &other) const noexcept(noexcept(std::declval< const TakeAdapter & >()==std::declval< const TakeAdapter & >()))
constexpr TakeAdapter(const R &range, size_t count) noexcept(noexcept(TakeAdapter(std::ranges::cbegin(range), std::ranges::cend(range), count)))
Constructs a map adapter from a const range and count.
constexpr TakeAdapter(const TakeAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter >)=default
constexpr TakeAdapter & operator++() noexcept(noexcept(++std::declval< Iter & >()))
std::iter_difference_t< Iter > difference_type
std::iter_value_t< Iter > value_type
details::adapter_iterator_concept_t< Iter > iterator_concept
pointer operator->() const =delete
constexpr TakeAdapter end() const noexcept(std::is_nothrow_copy_constructible_v< TakeAdapter >)
constexpr TakeAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< TakeAdapter >)
constexpr TakeAdapter(Iter begin, Iter end, size_t count) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Iter >)
Constructs a take adapter with the given iterator range and count.
constexpr bool operator==(const TakeAdapter &other) const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
constexpr bool IsAtEnd() const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
Checks if the iterator has reached the end.
Iterator adapter that takes elements while a predicate returns true.
constexpr TakeWhileAdapter end() const noexcept(std::is_nothrow_copy_constructible_v< TakeWhileAdapter >)
constexpr TakeWhileAdapter(TakeWhileAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_move_constructible_v< Pred >)=default
pointer operator->() const =delete
constexpr bool operator==(const TakeWhileAdapter &other) const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
constexpr bool IsAtEnd() const noexcept(noexcept(std::declval< const Iter & >()==std::declval< const Iter & >()))
Checks if the iterator has reached the end.
constexpr TakeWhileAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< TakeWhileAdapter >)
constexpr TakeWhileAdapter(const R &range, Pred predicate) noexcept(noexcept(TakeWhileAdapter(std::ranges::cbegin(range), std::ranges::cend(range), std::move(predicate))))
Constructs a take-while adapter from a const range and predicate.
constexpr bool operator!=(const TakeWhileAdapter &other) const noexcept(noexcept(std::declval< const TakeWhileAdapter & >()==std::declval< const TakeWhileAdapter & >()))
details::adapter_iterator_concept_t< Iter > iterator_concept
details::adapter_iterator_category_t< Iter > iterator_category
std::iter_difference_t< Iter > difference_type
constexpr TakeWhileAdapter(Iter begin, Iter end, Pred predicate) noexcept(std::is_nothrow_move_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Iter > &&std::is_nothrow_move_constructible_v< Pred > &&noexcept(CheckPredicate()))
Constructs a take-while adapter with the given iterator range and predicate.
constexpr TakeWhileAdapter & operator++() noexcept(noexcept(++std::declval< Iter & >()) &&noexcept(std::declval< Iter & >() !=std::declval< Iter & >()) &&noexcept(CheckPredicate()))
constexpr TakeWhileAdapter(const TakeWhileAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter > &&std::is_nothrow_copy_constructible_v< Pred >)=default
constexpr TakeWhileAdapter(R &range, Pred predicate) noexcept(noexcept(TakeWhileAdapter(std::ranges::begin(range), std::ranges::end(range), std::move(predicate))))
Constructs a take-while adapter from a range and predicate.
Adapter that combines two ranges into pairs.
constexpr ZipAdapter(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) noexcept(std::is_nothrow_move_constructible_v< Iter1 > &&std::is_nothrow_copy_constructible_v< Iter1 > &&std::is_nothrow_move_constructible_v< Iter2 > &&std::is_nothrow_copy_constructible_v< Iter2 >)
Constructs a zip adapter.
constexpr ZipAdapter begin() const noexcept(std::is_nothrow_copy_constructible_v< ZipAdapter >)
constexpr ZipAdapter(ZipAdapter &&) noexcept(std::is_nothrow_move_constructible_v< Iter1 > &&std::is_nothrow_move_constructible_v< Iter2 >)=default
constexpr ZipAdapter(R1 &first_range, R2 &second_range) noexcept(noexcept(ZipAdapter(std::ranges::begin(first_range), std::ranges::end(first_range), std::ranges::begin(second_range), std::ranges::end(second_range))))
constexpr ZipAdapter end() const noexcept(std::is_nothrow_copy_constructible_v< ZipAdapter >)
std::tuple< std::iter_value_t< Iter1 >, std::iter_value_t< Iter2 > > value_type
pointer operator->() const =delete
constexpr ZipAdapter & operator++() noexcept(noexcept(std::declval< Iter1 & >() !=std::declval< Iter1 & >()) &&noexcept(++std::declval< Iter1 & >()) &&noexcept(std::declval< Iter2 & >() !=std::declval< Iter2 & >()) &&noexcept(++std::declval< Iter2 & >()))
constexpr bool IsAtEnd() const noexcept(noexcept(std::declval< const Iter1 & >()==std::declval< const Iter1 & >()) &&noexcept(std::declval< const Iter2 & >()==std::declval< const Iter2 & >()))
constexpr bool operator==(const ZipAdapter &other) const noexcept(noexcept(std::declval< const Iter1 & >()==std::declval< const Iter1 & >()) &&noexcept(std::declval< const Iter2 & >()==std::declval< const Iter2 & >()))
std::input_iterator_tag iterator_category
constexpr ZipAdapter(const R1 &first_range, const R2 &second_range) noexcept(noexcept(ZipAdapter(std::ranges::cbegin(first_range), std::ranges::cend(first_range), std::ranges::cbegin(second_range), std::ranges::cend(second_range))))
std::input_iterator_tag iterator_concept
constexpr bool operator!=(const ZipAdapter &other) const noexcept(noexcept(std::declval< const ZipAdapter & >()==std::declval< const ZipAdapter & >()))
constexpr ZipAdapter(const ZipAdapter &) noexcept(std::is_nothrow_copy_constructible_v< Iter1 > &&std::is_nothrow_copy_constructible_v< Iter2 >)=default
Concept for action functions that process values.
Concept for types that are adapter traits (derived from FunctionalAdapterBase).
Concept for bidirectional iterators that support decrement operations.
Concept to validate ChainAdapter requirements.
Concept to validate EnumerateAdapter requirements.
Concept for types that are external ranges (not adapters).
Concept to validate FilterAdapter requirements.
Concept for folder functions that accumulate values.
Concept to validate InspectAdapter requirements.
Concept for inspection functions that observe but don't modify values.
Concept for types that can be used as base iterators in adapters.
Concept to validate JoinAdapter requirements.
Concept to validate MapAdapter requirements.
Concept for predicate functions that can be applied to iterator values.
Concept to validate ReverseAdapter requirements.
Concept to validate SkipAdapter requirements.
Concept to validate SkipWhileAdapter requirements.
Concept to validate SlideAdapter requirements.
Concept to validate StepByAdapter requirements.
Concept to validate StrideAdapter requirements.
Concept to validate TakeAdapter requirements.
Concept to validate TakeWhileAdapter requirements.
Concept for transformation functions that can be applied to iterator values.
Concept to validate ZipAdapter requirements.
Checks if the iterator yields a genuine reference (lvalue ref), not a proxy/value.
Concept to check if a type is tuple-like (has tuple_size and get).
typename adapter_iterator_traits< Iter >::iterator_category adapter_iterator_category_t
Gets the iterator category from Iter.
consteval auto GetCallOrApplyResultType() noexcept
Helper to get the result type of either invoke or apply.
std::conditional_t< IterYieldsReference< Iter >, std::remove_reference_t< std::iter_reference_t< Iter > > *,//T */const T * std::optional< std::iter_value_t< Iter > >//owned copy > find_result_t
Selects pointer vs optional depending on whether the iterator yields a real ref.
typename folder_apply_result< Folder, Accumulator, Tuple >::type folder_apply_result_t
typename adapter_iterator_traits< Iter >::iterator_concept adapter_iterator_concept_t
Gets the iterator concept from Iter.
typename decltype(GetCallOrApplyResultType< Func, Args... >())::type call_or_apply_result_t
MapAdapter(R &, Func) -> MapAdapter< std::ranges::iterator_t< R >, Func >
constexpr bool operator==(const TypeId &lhs, TypeIndex rhs) noexcept
Compares a TypeId with a TypeIndex for equality.
STL namespace.
Traits for Iter, selecting iterator concept from Iter if it has one, else forward.
std::conditional_t< IterYieldsReference< Iter >, std::forward_iterator_tag, std::input_iterator_tag > iterator_category
std::conditional_t< IterYieldsReference< Iter >, std::forward_iterator_tag, std::input_iterator_tag > iterator_concept
Helper to get the result type of folder applied with accumulator + tuple elements.
Helper to extract tuple element types and check if a folder is invocable with accumulator + tuple ele...