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