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