From 31b87b541989bbd7da8e11c1cc0ce8f477115c8d Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 26 Mar 2026 09:56:17 +0100 Subject: [PATCH] Add C++ ranges library for C++17 (#1316) * Add ranges * Print test names at test time (#1343) * Fix conflit commit errors * Cast return value of operator* to value_type Fixed warning on VS2022 --------- Co-authored-by: John Wellbelove --- docs/ranges.md | 328 + include/etl/algorithm.h | 4037 +++++++ include/etl/alignment.h | 1 - include/etl/expected.h | 1 + include/etl/functional.h | 47 +- include/etl/invoke.h | 53 +- include/etl/iterator.h | 734 +- include/etl/memory.h | 552 + include/etl/private/ranges_mini_variant.h | 412 + include/etl/private/variant_legacy.h | 2 +- include/etl/ranges.h | 5927 ++++++++++ include/etl/type_list.h | 1 - test/CMakeLists.txt | 1 + test/test_algorithm.cpp | 11921 ++++++++++++++++++++ test/test_functional.cpp | 128 + test/test_invoke.cpp | 149 + test/test_iterator.cpp | 810 ++ test/test_memory.cpp | 1061 +- test/test_ranges.cpp | 5516 +++++++++ test/vs2022/etl.vcxproj | 3 + test/vs2022/etl.vcxproj.filters | 3 + 21 files changed, 31668 insertions(+), 19 deletions(-) create mode 100644 docs/ranges.md create mode 100644 include/etl/private/ranges_mini_variant.h create mode 100644 include/etl/ranges.h create mode 100644 test/test_ranges.cpp diff --git a/docs/ranges.md b/docs/ranges.md new file mode 100644 index 00000000..9e6f55ee --- /dev/null +++ b/docs/ranges.md @@ -0,0 +1,328 @@ +# ETL C++17 Ranges Implementation + +## Overview + +The Embedded Template Library provides a C++17-compatible implementation of ranges, inspired by the C++20 ranges library. This implementation enables range-based algorithms and views for embedded and resource-constrained environments where full C++20 support may not be available. + +## Features + +- **Ranges**: Provides range types and iterator wrappers for composing operations over sequences. +- **Views**: Includes lightweight, composable views such as `filter_view`, `transform_view`, and `subrange`. +- **Algorithms**: Supports range-based algorithms compatible with ETL containers and standard containers. +- **Compatibility**: Designed for C++17, with minimal dependencies and no reliance on C++20 features. + +## Getting Started + +Include the main header in your project: + +```cpp +#include +``` + +### Example Usage + +#### Using Ranges + +```cpp +#include +#include +#include + +... + + etl::vector data = {6, 1, 3, 3, 2}; + etl::ranges::sort(data); + etl::ranges::for_each(data, [](const int& i){etl::print(" {}", i);}); +``` + +Output: +```text + 1 2 3 3 6 +``` + +#### Using Views + +```cpp +#include +#include +#include + +... + + etl::vector data = {1, 2, 3, 4, 5}; + auto even = [](int v) { return v % 2 == 0; }; + auto filtered = etl::ranges::filter_view(data, even); + etl::ranges::for_each(filtered, [](const int& i){etl::print(" {}", i);}); +``` + +Output: +```text + 2 4 +``` + +#### Transforming Elements + +```cpp +#include +#include +#include + +... + + etl::vector data = {1, 2, 3, 4, 5}; + auto squared = etl::ranges::transform_view(data, [](int v) { return v * v; }); + etl::ranges::for_each(squared, [](const int& i){etl::print(" {}", i);}); +``` + +Output: +```text + 1 4 9 16 25 +``` + +#### Composition + +Views can be composed using the pipe (`|`) operator, allowing you to chain multiple transformations in a readable, left-to-right style: + +```cpp +#include +#include +#include + +namespace views = etl::ranges::views; + +... + + etl::vector data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + auto result = data + | views::filter([](const int& v) { return v % 2 == 0; }) + | views::transform([](const int& v) { return v * v; }); + + etl::ranges::for_each(result, [](const int& i){ etl::print(" {}", i); }); +``` + +Output: +```text + 4 16 36 64 100 +``` + +This first filters the even numbers and then squares them. Each `|` passes the result of the previous stage as input to the next view adaptor. + +## Supported Views + +All views are in the `etl::ranges` namespace. Corresponding range adaptor objects are available in `etl::ranges::views`. + +### Range Factories + +| View | `views::` adaptor | Description | +|---|---|---| +| `empty_view` | `views::empty` | A view with no elements. | +| `single_view` | `views::single` | A view containing exactly one element. | +| `iota_view` | `views::iota` | A view of sequentially increasing values. | +| `repeat_view` | `views::repeat` | A view that repeats a value a given number of times. | + +### Range Adaptors + +| View | `views::` adaptor | Description | +|---|---|---| +| `ref_view` | `views::ref` | A non-owning view that wraps a reference to a range. | +| `owning_view` | `views::owning` | A view that takes ownership of a range via move. | +| — | `views::all` | Returns the range itself (if already a view), a `ref_view`, or an `owning_view`. | +| `filter_view` | `views::filter` | Filters elements based on a predicate. | +| `transform_view` | `views::transform` | Applies a transformation to each element. | +| `as_rvalue_view` | `views::as_rvalue` | Casts each element to an rvalue reference. | +| `as_const_view` | `views::as_const` | Provides a const view of the elements. | +| `cache_latest_view` | `views::cache_latest` | Caches the most recently accessed element (avoids recomputation). | +| `reverse_view` | `views::reverse` | Reverses the order of elements. | +| `drop_view` | `views::drop` | Skips the first *n* elements. | +| `drop_while_view` | `views::drop_while` | Skips leading elements while a predicate is true. | +| `take_view` | `views::take` | Takes the first *n* elements. | +| `take_while_view` | `views::take_while` | Takes leading elements while a predicate is true. | +| `join_view` | `views::join` | Flattens a range of ranges into a single range. | +| `join_with_view` | `views::join_with` | Flattens a range of ranges, inserting a delimiter between each. | +| `split_view` | `views::split` | Splits a range into subranges around a delimiter pattern. | +| `lazy_split_view` | `views::lazy_split` | Lazily splits a range by a pattern (inner ranges discovered on iteration). | +| — | `views::counted` | Creates a view of *n* elements starting from an iterator. | +| `concat_view` | `views::concat` | Concatenates multiple ranges into a single view. | +| `zip_view` | `views::zip` | Zips multiple ranges into a view of tuples (length of shortest range). | +| `zip_transform_view` | `views::zip_transform` | Zips multiple ranges and applies a function to each tuple of elements. | +| `common_view` | `views::common` | Adapts a view so that its iterator and sentinel types are the same. | +| `enumerate_view` | `views::enumerate` | Pairs each element with its index, producing tuples of (index, value). | +| `elements_view` | `views::elements` | Extracts the *N*-th element from each tuple-like value. | +| `keys_view` | `views::keys` | Alias for `elements_view` with *N*=0 (extracts first element of pairs/tuples). | +| `values_view` | `views::values` | Alias for `elements_view` with *N*=1 (extracts second element of pairs/tuples). | +| `adjacent_view` | `views::adjacent` | Produces a view of tuples of *N* adjacent elements (sliding window of tuples). | +| — | `views::pairwise` | Alias for `views::adjacent<2>`. | +| `adjacent_transform_view` | `views::adjacent_transform` | Applies a function to each group of *N* adjacent elements. | +| — | `views::pairwise_transform` | Alias for `views::adjacent_transform<2>`. | +| `chunk_view` | `views::chunk` | Splits a range into non-overlapping chunks of a given size. | +| `slide_view` | `views::slide` | Produces overlapping subranges (sliding windows) of a given size. | +| `chunk_by_view` | `views::chunk_by` | Splits a range into subranges between adjacent elements where a predicate is false. | +| `stride_view` | `views::stride` | Yields every *N*-th element from the underlying range. | +| `cartesian_product_view` | `views::cartesian_product` | Produces the Cartesian product of multiple ranges as a view of tuples. | +| `to_input_view` | `views::to_input` | Downgrades iterator category to input iterator while preserving elements and order. | +| `subrange` | — | Represents a sub-range defined by an iterator–sentinel pair. | + +All views support range-based for-loop iteration and can be composed with the pipe (`|`) operator. + +## Supported Algorithms + +All algorithms are callable objects in the `etl::ranges` namespace. Each supports both an iterator-pair overload and a range overload (where applicable), and most accept optional projection and comparator arguments. + +### Non-modifying Sequence Operations + +| Algorithm | Description | +|---|---| +| `for_each` | Applies a function to each element in a range. | +| `for_each_n` | Applies a function to the first *n* elements. | +| `find` | Finds the first element equal to a value. | +| `find_if` | Finds the first element satisfying a predicate. | +| `find_if_not` | Finds the first element not satisfying a predicate. | +| `find_end` | Finds the last occurrence of a subsequence. | +| `find_first_of` | Finds the first element matching any in a second range. | +| `adjacent_find` | Finds the first pair of adjacent equal elements. | +| `count` | Counts elements equal to a value. | +| `count_if` | Counts elements satisfying a predicate. | +| `all_of` | Checks if all elements satisfy a predicate. | +| `any_of` | Checks if any element satisfies a predicate. | +| `none_of` | Checks if no elements satisfy a predicate. | +| `mismatch` | Finds the first position where two ranges differ. | +| `equal` | Checks if two ranges are equal. | +| `is_permutation` | Checks if one range is a permutation of another. | +| `search` | Searches for the first occurrence of a subsequence. | +| `search_n` | Searches for *n* consecutive copies of a value. | +| `starts_with` | Checks if a range starts with another range. | +| `ends_with` | Checks if a range ends with another range. | +| `lexicographical_compare` | Compares two ranges lexicographically. | + +### Fold Operations + +| Algorithm | Description | +|---|---| +| `fold_left` | Left-folds elements with a binary operation. | +| `fold_left_with_iter` | Left-folds, returning both the result and an iterator. | +| `fold_left_first` | Left-folds using the first element as the initial value. | +| `fold_left_first_with_iter` | Like `fold_left_first`, also returning an iterator. | +| `fold_right` | Right-folds elements with a binary operation. | +| `fold_right_last` | Right-folds using the last element as the initial value. | + +### Modifying Sequence Operations + +| Algorithm | Description | +|---|---| +| `copy` | Copies elements to a destination range. | +| `copy_n` | Copies *n* elements to a destination range. | +| `copy_if` | Copies elements satisfying a predicate. | +| `copy_backward` | Copies elements backwards to a destination range. | +| `move` | Moves elements to a destination range. | +| `move_backward` | Moves elements backwards to a destination range. | +| `swap_ranges` | Swaps elements between two ranges. | +| `replace` | Replaces elements equal to a value. | +| `replace_if` | Replaces elements satisfying a predicate. | +| `replace_copy` | Copies, replacing elements equal to a value. | +| `replace_copy_if` | Copies, replacing elements satisfying a predicate. | +| `remove` | Removes elements equal to a value. | +| `remove_if` | Removes elements satisfying a predicate. | +| `remove_copy` | Copies, omitting elements equal to a value. | +| `fill` | Fills a range with a value. | +| `fill_n` | Fills *n* elements with a value. | +| `generate` | Assigns each element the result of a generator function. | +| `generate_n` | Assigns *n* elements the result of a generator function. | +| `iota` | Fills a range with sequentially increasing values. | +| `unique` | Removes consecutive duplicate elements. | +| `unique_copy` | Copies, removing consecutive duplicates. | +| `transform` | Applies a transformation to each element. | +| `reverse` | Reverses the order of elements. | +| `reverse_copy` | Copies elements in reverse order. | +| `rotate` | Rotates elements in a range. | +| `rotate_copy` | Copies elements with rotation. | +| `shift_left` | Shifts elements to the left. | +| `shift_right` | Shifts elements to the right. | +| `shuffle` | Randomly reorders elements. | +| `sample` | Selects *n* random elements from a range. | + +### Sorting Operations + +| Algorithm | Description | +|---|---| +| `sort` | Sorts elements in a range. | +| `stable_sort` | Sorts elements preserving relative order of equivalent elements. | +| `partial_sort` | Partially sorts a range so that the first *n* elements are sorted. | +| `partial_sort_copy` | Copies and partially sorts elements. | +| `nth_element` | Partially sorts so that the *n*-th element is in its sorted position. | +| `is_sorted` | Checks if a range is sorted. | +| `is_sorted_until` | Finds the first unsorted element. | + +### Partitioning Operations + +| Algorithm | Description | +|---|---| +| `partition` | Partitions elements by a predicate. | +| `stable_partition` | Partitions elements, preserving relative order. | +| `is_partitioned` | Checks if a range is partitioned. | +| `partition_copy` | Copies elements into two ranges based on a predicate. | +| `partition_point` | Finds the partition point. | + +### Binary Search (on sorted ranges) + +| Algorithm | Description | +|---|---| +| `lower_bound` | Finds the first element not less than a value. | +| `upper_bound` | Finds the first element greater than a value. | +| `equal_range` | Returns the range of elements equal to a value. | +| `binary_search` | Checks if a sorted range contains a value. | + +### Set Operations (on sorted ranges) + +| Algorithm | Description | +|---|---| +| `includes` | Checks if one sorted range includes another. | +| `merge` | Merges two sorted ranges. | +| `inplace_merge` | Merges two consecutive sorted sub-ranges in place. | +| `set_union` | Computes the union of two sorted ranges. | +| `set_intersection` | Computes the intersection of two sorted ranges. | +| `set_difference` | Computes the difference of two sorted ranges. | +| `set_symmetric_difference` | Computes the symmetric difference of two sorted ranges. | + +### Heap Operations + +| Algorithm | Description | +|---|---| +| `make_heap` | Creates a heap from a range. | +| `push_heap` | Pushes an element onto a heap. | +| `pop_heap` | Pops the top element from a heap. | +| `sort_heap` | Sorts a heap into a sorted range. | +| `is_heap` | Checks if a range is a heap. | +| `is_heap_until` | Finds the first element that breaks the heap property. | + +### Min/Max Operations + +| Algorithm | Description | +|---|---| +| `min` | Returns the smaller of two values or the smallest in an initializer list. | +| `min_element` | Finds the smallest element in a range. | +| `max` | Returns the larger of two values or the largest in an initializer list. | +| `max_element` | Finds the largest element in a range. | +| `minmax` | Returns the smaller and larger of two values. | +| `minmax_element` | Finds both the smallest and largest elements in a range. | +| `clamp` | Clamps a value between a minimum and maximum. | + +### Permutation Operations + +| Algorithm | Description | +|---|---| +| `next_permutation` | Generates the next lexicographic permutation. | +| `prev_permutation` | Generates the previous lexicographic permutation. | + + +## Reference + +For reference to the STD implementation, see also: + +- Algorithms: https://en.cppreference.com/w/cpp/algorithm.html +- Ranges/Views: https://en.cppreference.com/w/cpp/ranges.html + +## Limitations + +- Not all C++20 range features are available due to limitation to C++17. Especially C++20 concepts are not used. +- Designed for ETL containers but can work with standard containers if compatible with ETL's iterator requirements. diff --git a/include/etl/algorithm.h b/include/etl/algorithm.h index ab4ae01e..ad5b165d 100644 --- a/include/etl/algorithm.h +++ b/include/etl/algorithm.h @@ -47,6 +47,8 @@ SOFTWARE. #include "gcd.h" #include "error_handler.h" #include "exception.h" +#include "ranges.h" +#include "invoke.h" #include #include @@ -4051,6 +4053,4041 @@ namespace etl nth_element(first, nth, last, compare_t()); } #endif + +#if ETL_USING_CPP17 + +namespace ranges { + template + struct in_fun_result + { + ETL_NO_UNIQUE_ADDRESS I in; + ETL_NO_UNIQUE_ADDRESS F fun; + + template + constexpr operator in_fun_result() const& + { + return {in, fun}; + } + + template + constexpr operator in_fun_result() && + { + return {etl::move(in), etl::move(fun)}; + } + }; + + template + struct in_in_result + { + ETL_NO_UNIQUE_ADDRESS I1 in1; + ETL_NO_UNIQUE_ADDRESS I2 in2; + + template + constexpr operator in_in_result() const& + { + return {in1, in2}; + } + + template + constexpr operator in_in_result() && + { + return {etl::move(in1), etl::move(in2)}; + } + }; + + template + struct in_out_result + { + ETL_NO_UNIQUE_ADDRESS I in; + ETL_NO_UNIQUE_ADDRESS O out; + + template + constexpr operator in_out_result() const& + { + return {in, out}; + } + + template + constexpr operator in_out_result() && + { + return {etl::move(in), etl::move(out)}; + } + }; + + template + struct in_in_out_result + { + ETL_NO_UNIQUE_ADDRESS I1 in1; + ETL_NO_UNIQUE_ADDRESS I2 in2; + ETL_NO_UNIQUE_ADDRESS O out; + + template + constexpr operator in_in_out_result() const& + { + return {in1, in2, out}; + } + + template + constexpr operator in_in_out_result() && + { + return {etl::move(in1), etl::move(in2), etl::move(out)}; + } + }; + + template + struct in_out_out_result + { + ETL_NO_UNIQUE_ADDRESS I in; + ETL_NO_UNIQUE_ADDRESS O1 out1; + ETL_NO_UNIQUE_ADDRESS O2 out2; + + template + constexpr operator in_out_out_result() const& + { + return {in, out1, out2}; + } + + template + constexpr operator in_out_out_result() && + { + return {etl::move(in), etl::move(out1), etl::move(out2)}; + } + }; + + template + struct in_found_result + { + ETL_NO_UNIQUE_ADDRESS I in; + bool found; + + template + constexpr operator in_found_result() const& + { + return {in, found}; + } + + template + constexpr operator in_found_result() && + { + return {etl::move(in), found}; + } + }; + + template + struct out_value_result + { + ETL_NO_UNIQUE_ADDRESS O out; + ETL_NO_UNIQUE_ADDRESS T value; + + template + constexpr operator out_value_result() const& + { + return {out, value}; + } + + template + constexpr operator out_value_result() && + { + return {etl::move(out), etl::move(value)}; + } + }; + + template + using iota_result = out_value_result; + + template + using copy_result = in_out_result; + + template + using copy_n_result = in_out_result; + + template + using copy_if_result = in_out_result; + + template + using copy_backward_result = in_out_result; + + template + using uninitialized_copy_result = in_out_result; + + template + using uninitialized_copy_n_result = in_out_result; + + template + using uninitialized_move_result = in_out_result; + + template + using uninitialized_move_n_result = in_out_result; + + template + using move_result = in_out_result; + + template + using move_backward_result = in_out_result; + + template + using mismatch_result = in_in_result; + + template + using swap_ranges_result = in_in_result; + + template + using unary_transform_result = in_out_result; + + template + using binary_transform_result = in_in_out_result; + + template + using replace_copy_result = in_out_result; + + template + using replace_copy_if_result = in_out_result; + + template + using remove_copy_result = in_out_result; + + template + using unique_copy_result = in_out_result; + + template + using rotate_copy_result = in_out_result; + + template + using partial_sort_copy_result = in_out_result; + + template + using partition_copy_result = in_out_out_result; + + template + using set_union_result = in_in_out_result; + + template + using set_intersection_result = in_in_out_result; + + template + using set_difference_result = in_out_result; + + template + using set_symmetric_difference_result = in_in_out_result; + + template + using merge_result = in_in_out_result; + + template + using next_permutation_result = in_found_result; + + template + using prev_permutation_result = in_found_result; + + template + struct min_max_result + { + T min; + T max; + + template + constexpr operator min_max_result() const& + { + return {min, max}; + } + + template + constexpr operator min_max_result() && + { + return {etl::move(min), etl::move(max)}; + } + }; + + template + using minmax_result = min_max_result; + + template + using minmax_element_result = min_max_result; + + template + using for_each_result = in_fun_result; + + struct for_each_fn + { + template>> + constexpr ranges::for_each_result + operator()(I first, S last, Fun f, Proj proj = {}) const + { + for (; first != last; ++first) + { + etl::invoke(f, etl::invoke(proj, *first)); + } + return {etl::move(first), etl::move(f)}; + } + + template>> + constexpr ranges::for_each_result, Fun> + operator()(R&& r, Fun f, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(f), etl::ref(proj)); + } + }; + + inline constexpr for_each_fn for_each; + + template + using for_each_n_result = in_fun_result; + + struct for_each_n_fn + { + template>> + constexpr for_each_n_result + operator()(I first, etl::iter_difference_t n, Fun fun, Proj proj = Proj{}) const + { + for (; n-- > 0; ++first) + { + etl::invoke(fun, etl::invoke(proj, *first)); + } + return {etl::move(first), etl::move(fun)}; + } + }; + + inline constexpr for_each_n_fn for_each_n {}; + + struct find_fn + { + template>> + constexpr I operator()(I first, S last, const T& value, Proj proj = {}) const + { + for (; first != last; ++first) + { + if (etl::invoke(proj, *first) == value) + { + return first; + } + } + return first; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, const T& value, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), value, etl::ref(proj)); + } + }; + + inline constexpr find_fn find; + + struct find_if_fn + { + template>> + constexpr I operator()(I first, S last, Pred pred, Proj proj = {}) const + { + for (; first != last; ++first) + { + if (etl::invoke(pred, etl::invoke(proj, *first))) + { + return first; + } + } + return first; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Pred pred, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr find_if_fn find_if; + + struct find_if_not_fn + { + template>> + constexpr I operator()(I first, S last, Pred pred, Proj proj = {}) const + { + for (; first != last; ++first) + { + if (!etl::invoke(pred, etl::invoke(proj, *first))) + { + return first; + } + } + return first; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Pred pred, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr find_if_not_fn find_if_not; + + struct search_fn + { + template>> + constexpr ranges::subrange + operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + for (;; ++first1) + { + I1 it1 = first1; + for (I2 it2 = first2;; ++it1, ++it2) + { + if (it2 == last2) + { + return {first1, it1}; + } + if (it1 == last1) + { + return {it1, it1}; + } + if (!etl::invoke(pred, etl::invoke(proj1, *it1), etl::invoke(proj2, *it2))) + { + break; + } + } + } + } + + template>> + constexpr ranges::borrowed_subrange_t + operator()(R1&& r1, R2&& r2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(pred), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr search_fn search {}; + + struct search_n_fn + { + template, + class Pred = ranges::equal_to, + class Proj = etl::identity, typename = etl::enable_if_t>> + constexpr ranges::subrange + operator()(I first, S last, etl::iter_difference_t count, const T& value, Pred pred = {}, + Proj proj = {}) const + { + if (count <= 0) + { + return {first, first}; + } + + for (; first != last; ++first) + { + if (etl::invoke(pred, etl::invoke(proj, *first), value)) + { + I start = first; + etl::iter_difference_t n = 1; + + for (;;) + { + if (n >= count) + { + return {start, ++first}; + } + ++first; + if (first == last) + { + return {first, first}; + } + if (!etl::invoke(pred, etl::invoke(proj, *first), value)) + { + break; + } + ++n; + } + } + } + return {first, first}; + } + + template, + class Pred = ranges::equal_to, + class Proj = etl::identity, typename = etl::enable_if_t>> + constexpr ranges::borrowed_subrange_t + operator()(R&& r, ranges::range_difference_t count, const T& value, Pred pred = {}, + Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), + count, value, + etl::move(pred), etl::move(proj)); + } + }; + + inline constexpr search_n_fn search_n {}; + + struct find_end_fn + { + template>> + constexpr ranges::subrange + operator()(I1 first1, S1 last1, + I2 first2, S2 last2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + if (first2 == last2) + { + auto last_it = ranges::next(first1, last1); + return {last_it, last_it}; + } + auto result = ranges::search(etl::move(first1), last1, first2, last2, pred, proj1, proj2); + + if (result.empty()) + { + return result; + } + + for (;;) + { + auto new_result = ranges::search(etl::next(result.begin()), last1, first2, last2, pred, proj1, proj2); + if (new_result.empty()) + { + return result; + } + else + { + result = etl::move(new_result); + } + } + } + + template>> + constexpr ranges::borrowed_subrange_t + operator()(R1&& r1, R2&& r2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(pred), + etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr find_end_fn find_end {}; + + struct find_first_of_fn + { + template>> + constexpr I1 operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + for (; first1 != last1; ++first1) + { + for (auto i = first2; i != last2; ++i) + { + if (etl::invoke(pred, etl::invoke(proj1, *first1), etl::invoke(proj2, *i))) + { + return first1; + } + } + } + return first1; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R1&& r1, R2&& r2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(pred), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr find_first_of_fn find_first_of {}; + + struct adjacent_find_fn + { + template>> + constexpr I operator()(I first, S last, Pred pred = {}, Proj proj = {}) const + { + if (first == last) + { + return first; + } + auto next_it = ranges::next(first); + for (; next_it != last; ++next_it, ++first) + { + if (etl::invoke(pred, etl::invoke(proj, *first), etl::invoke(proj, *next_it))) + { + return first; + } + } + return next_it; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Pred pred = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr adjacent_find_fn adjacent_find; + + struct count_fn + { + template, typename = etl::enable_if_t>> + constexpr etl::iter_difference_t + operator()(I first, S last, const T& value, Proj proj = {}) const + { + etl::iter_difference_t counter = 0; + for (; first != last; ++first) + { + if (etl::invoke(proj, *first) == value) + { + ++counter; + } + } + return counter; + } + + template, Proj>, typename = etl::enable_if_t>> + constexpr ranges::range_difference_t + operator()(R&& r, const T& value, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), value, etl::ref(proj)); + } + }; + + inline constexpr count_fn count; + + struct count_if_fn + { + template>> + constexpr etl::iter_difference_t + operator()(I first, S last, Pred pred, Proj proj = {}) const + { + etl::iter_difference_t counter = 0; + for (; first != last; ++first) + { + if (etl::invoke(pred, etl::invoke(proj, *first))) + { + ++counter; + } + } + return counter; + } + + template>> + constexpr ranges::range_difference_t + operator()(R&& r, Pred pred, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr count_if_fn count_if; + + struct all_of_fn + { + template>> + constexpr bool operator()(I first, S last, Pred pred, Proj proj = {}) const + { + return ranges::find_if_not(first, last, etl::ref(pred), etl::ref(proj)) == last; + } + + template>> + constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const + { + return operator()(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr all_of_fn all_of; + + struct any_of_fn + { + template>> + constexpr bool operator()(I first, S last, Pred pred, Proj proj = {}) const + { + return ranges::find_if(first, last, etl::ref(pred), etl::ref(proj)) != last; + } + + template>> + constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const + { + return operator()(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr any_of_fn any_of; + + struct none_of_fn + { + template>> + constexpr bool operator()(I first, S last, Pred pred, Proj proj = {}) const + { + return ranges::find_if(first, last, etl::ref(pred), etl::ref(proj)) == last; + } + + template>> + constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const + { + return operator()(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr none_of_fn none_of; + + struct mismatch_fn + { + template>> + constexpr ranges::mismatch_result + operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + for (; first1 != last1 && first2 != last2; ++first1, ++first2) + { + if (!etl::invoke(pred, etl::invoke(proj1, *first1), etl::invoke(proj2, *first2))) + { + break; + } + } + return {etl::move(first1), etl::move(first2)}; + } + + template>> + constexpr ranges::mismatch_result, ranges::borrowed_iterator_t> + operator()(R1&& r1, R2&& r2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(pred), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr mismatch_fn mismatch {}; + + struct equal_fn + { + template>> + constexpr bool + operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + for (; first1 != last1 && first2 != last2; ++first1, ++first2) + { + if (!etl::invoke(pred, etl::invoke(proj1, *first1), etl::invoke(proj2, *first2))) + { + return false; + } + } + return first1 == last1 && first2 == last2; + } + + template>> + constexpr bool + operator()(R1&& r1, R2&& r2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(pred), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr equal_fn equal {}; + + struct is_permutation_fn + { + template>> + constexpr bool + operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + // Skip common prefix + for (; first1 != last1 && first2 != last2; ++first1, ++first2) + { + if (!etl::invoke(pred, etl::invoke(proj1, *first1), etl::invoke(proj2, *first2))) + { + break; + } + } + + if (first1 == last1) + { + return first2 == last2; + } + + if (first2 == last2) + { + return false; + } + + // Check remaining elements + for (I1 i = first1; i != last1; ++i) + { + // Check if we already counted this element + bool already_seen = false; + for (I1 j = first1; j != i; ++j) + { + if (etl::invoke(pred, etl::invoke(proj1, *i), etl::invoke(proj1, *j))) + { + already_seen = true; + break; + } + } + + if (already_seen) + { + continue; + } + + // Count occurrences in range2 + auto count2 = etl::iter_difference_t(0); + for (I2 k = first2; k != last2; ++k) + { + if (etl::invoke(pred, etl::invoke(proj1, *i), etl::invoke(proj2, *k))) + { + ++count2; + } + } + + if (count2 == 0) + { + return false; + } + + // Count occurrences in range1 + auto count1 = etl::iter_difference_t(0); + for (I1 k = i; k != last1; ++k) + { + if (etl::invoke(pred, etl::invoke(proj1, *i), etl::invoke(proj1, *k))) + { + ++count1; + } + } + + if (count1 != count2) + { + return false; + } + } + + return true; + } + + template>> + constexpr bool + operator()(R1&& r1, R2&& r2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(pred), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr is_permutation_fn is_permutation {}; + + struct starts_with_fn + { + template>> + constexpr bool + operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + for (; first2 != last2; ++first1, ++first2) + { + if (first1 == last1 || !etl::invoke(pred, etl::invoke(proj1, *first1), etl::invoke(proj2, *first2))) + { + return false; + } + } + return true; + } + + template>> + constexpr bool + operator()(R1&& r1, R2&& r2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(pred), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr starts_with_fn starts_with {}; + + struct ends_with_fn + { + template>> + constexpr bool + operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + auto n1 = etl::distance(first1, last1); + auto n2 = etl::distance(first2, last2); + + if (n2 > n1) + { + return false; + } + + ranges::advance(first1, n1 - n2); + + return starts_with(first1, last1, first2, last2, etl::move(pred), etl::move(proj1), etl::move(proj2)); + } + + template>> + constexpr bool + operator()(R1&& r1, R2&& r2, Pred pred = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(pred), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr ends_with_fn ends_with {}; + + struct lexicographical_compare_fn + { + template>> + constexpr bool + operator()(I1 first1, S1 last1, I2 first2, S2 last2, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + for (; first1 != last1 && first2 != last2; ++first1, ++first2) + { + if (etl::invoke(comp, etl::invoke(proj1, *first1), etl::invoke(proj2, *first2))) + { + return true; + } + + if (etl::invoke(comp, etl::invoke(proj2, *first2), etl::invoke(proj1, *first1))) + { + return false; + } + } + return first1 == last1 && first2 != last2; + } + + template>> + constexpr bool + operator()(R1&& r1, R2&& r2, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(comp), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr lexicographical_compare_fn lexicographical_compare {}; + + template + struct in_value_result + { + ETL_NO_UNIQUE_ADDRESS I in; + ETL_NO_UNIQUE_ADDRESS T value; + + template + constexpr operator in_value_result() const& + { + return {in, value}; + } + + template + constexpr operator in_value_result() && + { + return {etl::move(in), etl::move(value)}; + } + }; + + template + using fold_left_with_iter_result = in_value_result; + + struct fold_left_fn + { + template>> + constexpr auto operator()(I first, S last, T init, F f) const + -> etl::decay_t>> + { + using U = etl::decay_t>>; + U accum = etl::move(init); + for (; first != last; ++first) + { + accum = etl::invoke(f, etl::move(accum), *first); + } + return accum; + } + + template>> + constexpr auto operator()(R&& r, T init, F f) const + -> etl::decay_t>> + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(init), etl::move(f)); + } + }; + + inline constexpr fold_left_fn fold_left {}; + + struct fold_left_with_iter_fn + { + template>> + constexpr auto operator()(I first, S last, T init, F f) const + -> fold_left_with_iter_result>>> + { + using U = etl::decay_t>>; + U accum = etl::move(init); + for (; first != last; ++first) + { + accum = etl::invoke(f, etl::move(accum), *first); + } + return {etl::move(first), etl::move(accum)}; + } + + template>> + constexpr auto operator()(R&& r, T init, F f) const + -> fold_left_with_iter_result, etl::decay_t>>> + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(init), etl::move(f)); + } + }; + + inline constexpr fold_left_with_iter_fn fold_left_with_iter {}; + + struct fold_left_first_fn + { + template>> + constexpr auto operator()(I first, S last, F f) const + -> etl::decay_t, etl::iter_reference_t>> + { + using U = etl::decay_t, etl::iter_reference_t>>; + if (first == last) + { + return U{}; + } + U accum = *first; + ++first; + for (; first != last; ++first) + { + accum = etl::invoke(f, etl::move(accum), *first); + } + return accum; + } + + template>> + constexpr auto operator()(R&& r, F f) const + -> etl::decay_t, etl::ranges::range_reference_t>> + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(f)); + } + }; + + inline constexpr fold_left_first_fn fold_left_first {}; + + struct fold_left_first_with_iter_fn + { + template>> + constexpr auto operator()(I first, S last, F f) const + -> fold_left_with_iter_result, etl::iter_reference_t>>> + { + using U = etl::decay_t, etl::iter_reference_t>>; + if (first == last) + { + return {etl::move(first), U{}}; + } + U accum = *first; + ++first; + for (; first != last; ++first) + { + accum = etl::invoke(f, etl::move(accum), *first); + } + return {etl::move(first), etl::move(accum)}; + } + + template>> + constexpr auto operator()(R&& r, F f) const + -> fold_left_with_iter_result, etl::decay_t, etl::ranges::range_reference_t>>> + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(f)); + } + }; + + inline constexpr fold_left_first_with_iter_fn fold_left_first_with_iter {}; + + struct fold_right_fn + { + template>> + constexpr auto operator()(I first, S last, T init, F f) const + -> etl::decay_t, T>> + { + using U = etl::decay_t, T>>; + U accum = etl::move(init); + I tail = ranges::next(first, last); + while (tail != first) + { + tail = ranges::prev(tail); + accum = etl::invoke(f, *tail, etl::move(accum)); + } + return accum; + } + + template>> + constexpr auto operator()(R&& r, T init, F f) const + -> etl::decay_t, T>> + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(init), etl::move(f)); + } + }; + + inline constexpr fold_right_fn fold_right {}; + + struct fold_right_last_fn + { + template>> + constexpr auto operator()(I first, S last, F f) const + -> etl::decay_t, etl::iter_value_t>> + { + using U = etl::decay_t, etl::iter_value_t>>; + I tail = ranges::next(first, last); + if (tail == first) + { + return U{}; + } + tail = ranges::prev(tail); + U accum = *tail; + while (tail != first) + { + tail = ranges::prev(tail); + accum = etl::invoke(f, *tail, etl::move(accum)); + } + return accum; + } + + template>> + constexpr auto operator()(R&& r, F f) const + -> etl::decay_t, etl::ranges::range_value_t>> + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(f)); + } + }; + + inline constexpr fold_right_last_fn fold_right_last {}; + + struct copy_fn + { + template>> + constexpr ranges::copy_result + operator()(I first, S last, O result) const + { + for (; first != last; ++first, ++result) + { + *result = *first; + } + return {etl::move(first), etl::move(result)}; + } + + template>> + constexpr ranges::copy_result, O> + operator()(R&& r, O result) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(result)); + } + }; + + inline constexpr copy_fn copy {}; + + struct copy_n_fn + { + template + constexpr ranges::copy_n_result + operator()(I first, etl::iter_difference_t n, O result) const + { + for (etl::iter_difference_t i = 0; i < n; ++i, ++first, ++result) + { + *result = *first; + } + return {etl::move(first), etl::move(result)}; + } + }; + + inline constexpr copy_n_fn copy_n {}; + + struct copy_if_fn + { + template>> + constexpr ranges::copy_if_result + operator()(I first, S last, O result, Pred pred, Proj proj = {}) const + { + for (; first != last; ++first) + { + if (etl::invoke(pred, etl::invoke(proj, *first))) + { + *result = *first; + ++result; + } + } + return {etl::move(first), etl::move(result)}; + } + + template>> + constexpr ranges::copy_if_result, O> + operator()(R&& r, O result, Pred pred, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(result), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr copy_if_fn copy_if {}; + + struct copy_backward_fn + { + template>> + constexpr ranges::copy_backward_result + operator()(I1 first, S1 last, I2 result) const + { + I1 last_it = ranges::next(first, last); + I1 tail = last_it; + while (first != tail) + { + *(--result) = *(--tail); + } + return {etl::move(last_it), etl::move(result)}; + } + + template>> + constexpr ranges::copy_backward_result, I> + operator()(R&& r, I result) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(result)); + } + }; + + inline constexpr copy_backward_fn copy_backward {}; + + struct move_fn + { + template>> + constexpr ranges::move_result + operator()(I first, S last, O result) const + { + for (; first != last; ++first, ++result) + { + *result = etl::move(*first); + } + return {etl::move(first), etl::move(result)}; + } + + template>> + constexpr ranges::move_result, O> + operator()(R&& r, O result) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(result)); + } + }; + + inline constexpr move_fn move {}; + + struct move_backward_fn + { + template>> + constexpr ranges::move_backward_result + operator()(I1 first, S1 last, I2 result) const + { + I1 last_it = ranges::next(first, last); + I1 tail = last_it; + while (first != tail) + { + *(--result) = etl::move(*(--tail)); + } + return {etl::move(last_it), etl::move(result)}; + } + + template>> + constexpr ranges::move_backward_result, I> + operator()(R&& r, I result) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(result)); + } + }; + + inline constexpr move_backward_fn move_backward {}; + + struct swap_ranges_fn + { + template>> + constexpr ranges::swap_ranges_result + operator()(I1 first1, S1 last1, I2 first2, S2 last2) const + { + for (; first1 != last1 && first2 != last2; ++first1, ++first2) + { + etl::iter_swap(first1, first2); + } + return {etl::move(first1), etl::move(first2)}; + } + + template>> + constexpr ranges::swap_ranges_result, ranges::borrowed_iterator_t> + operator()(R1&& r1, R2&& r2) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), ranges::begin(r2), ranges::end(r2)); + } + }; + + inline constexpr swap_ranges_fn swap_ranges {}; + + struct replace_fn + { + template>> + constexpr I operator()(I first, S last, const T1& old_value, const T2& new_value, Proj proj = {}) const + { + for (; first != last; ++first) + { + if (etl::invoke(proj, *first) == old_value) + { + *first = new_value; + } + } + return first; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, const T1& old_value, const T2& new_value, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), old_value, new_value, etl::ref(proj)); + } + }; + + inline constexpr replace_fn replace {}; + + struct replace_if_fn + { + template>> + constexpr I operator()(I first, S last, Pred pred, const T& new_value, Proj proj = {}) const + { + for (; first != last; ++first) + { + if (etl::invoke(pred, etl::invoke(proj, *first))) + { + *first = new_value; + } + } + return first; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Pred pred, const T& new_value, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(pred), new_value, etl::ref(proj)); + } + }; + + inline constexpr replace_if_fn replace_if {}; + + struct replace_copy_fn + { + template>> + constexpr ranges::replace_copy_result + operator()(I first, S last, O result, const T1& old_value, const T2& new_value, Proj proj = {}) const + { + for (; first != last; ++first, ++result) + { + if (etl::invoke(proj, *first) == old_value) + { + *result = new_value; + } + else + { + *result = *first; + } + } + return {etl::move(first), etl::move(result)}; + } + + template>> + constexpr ranges::replace_copy_result, O> + operator()(R&& r, O result, const T1& old_value, const T2& new_value, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(result), old_value, new_value, etl::ref(proj)); + } + }; + + inline constexpr replace_copy_fn replace_copy {}; + + struct replace_copy_if_fn + { + template>> + constexpr ranges::replace_copy_if_result + operator()(I first, S last, O result, Pred pred, const T& new_value, Proj proj = {}) const + { + for (; first != last; ++first, ++result) + { + if (etl::invoke(pred, etl::invoke(proj, *first))) + { + *result = new_value; + } + else + { + *result = *first; + } + } + return {etl::move(first), etl::move(result)}; + } + + template>> + constexpr ranges::replace_copy_if_result, O> + operator()(R&& r, O result, Pred pred, const T& new_value, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(result), etl::ref(pred), new_value, etl::ref(proj)); + } + }; + + inline constexpr replace_copy_if_fn replace_copy_if {}; + + struct remove_fn + { + template>> + constexpr ranges::subrange + operator()(I first, S last, const T& value, Proj proj = {}) const + { + first = ranges::find(first, last, value, etl::ref(proj)); + + if (first != last) + { + I result = first; + + for (I it = result; ++it != last;) + { + if (!(etl::invoke(proj, *it) == value)) + { + *result = etl::move(*it); + ++result; + } + } + + return {result, last}; + } + + return {first, last}; + } + + template>> + constexpr ranges::borrowed_subrange_t + operator()(R&& r, const T& value, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), value, etl::ref(proj)); + } + }; + + inline constexpr remove_fn remove {}; + + struct remove_if_fn + { + template>> + constexpr ranges::subrange + operator()(I first, S last, Pred pred, Proj proj = {}) const + { + first = ranges::find_if(first, last, etl::ref(pred), etl::ref(proj)); + + if (first != last) + { + I result = first; + + for (I it = result; ++it != last;) + { + if (!etl::invoke(pred, etl::invoke(proj, *it))) + { + *result = etl::move(*it); + ++result; + } + } + + return {result, last}; + } + + return {first, last}; + } + + template>> + constexpr ranges::borrowed_subrange_t + operator()(R&& r, Pred pred, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr remove_if_fn remove_if {}; + + struct remove_copy_fn + { + template>> + constexpr ranges::remove_copy_result + operator()(I first, S last, O result, const T& value, Proj proj = {}) const + { + for (; first != last; ++first) + { + if (!(etl::invoke(proj, *first) == value)) + { + *result = *first; + ++result; + } + } + return {etl::move(first), etl::move(result)}; + } + + template>> + constexpr ranges::remove_copy_result, O> + operator()(R&& r, O result, const T& value, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(result), value, etl::ref(proj)); + } + }; + + inline constexpr remove_copy_fn remove_copy {}; + + struct fill_fn + { + template>> + constexpr I operator()(I first, S last, const T& value) const + { + for (; first != last; ++first) + { + *first = value; + } + return first; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, const T& value) const + { + return (*this)(ranges::begin(r), ranges::end(r), value); + } + }; + + inline constexpr fill_fn fill {}; + + struct fill_n_fn + { + template + constexpr I operator()(I first, etl::iter_difference_t n, const T& value) const + { + for (; n-- > 0; ++first) + { + *first = value; + } + return first; + } + }; + + inline constexpr fill_n_fn fill_n {}; + + struct generate_fn + { + template>> + constexpr I operator()(I first, S last, F gen) const + { + for (; first != last; ++first) + { + *first = gen(); + } + return first; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, F gen) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(gen)); + } + }; + + inline constexpr generate_fn generate {}; + + struct generate_n_fn + { + template + constexpr I operator()(I first, etl::iter_difference_t n, F gen) const + { + for (; n-- > 0; ++first) + { + *first = gen(); + } + return first; + } + }; + + inline constexpr generate_n_fn generate_n {}; + + struct iota_fn + { + template>> + constexpr ranges::iota_result + operator()(O first, S last, T value) const + { + while (first != last) + { + *first = value; + ++first; + ++value; + } + return {etl::move(first), etl::move(value)}; + } + + template>> + constexpr ranges::iota_result, T> + operator()(R&& r, T value) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(value)); + } + }; + + inline constexpr iota_fn iota {}; + + struct unique_fn + { + template>> + constexpr ranges::subrange + operator()(I first, S last, Pred pred = {}, Proj proj = {}) const + { + first = ranges::adjacent_find(first, last, etl::ref(pred), etl::ref(proj)); + + if (first != last) + { + I result = first; + ++first; + + while (++first != last) + { + if (!etl::invoke(pred, etl::invoke(proj, *result), etl::invoke(proj, *first))) + { + *++result = etl::move(*first); + } + } + + return {++result, last}; + } + + return {first, last}; + } + + template>> + constexpr ranges::borrowed_subrange_t + operator()(R&& r, Pred pred = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr unique_fn unique {}; + + struct unique_copy_fn + { + template>> + constexpr ranges::unique_copy_result + operator()(I first, S last, O result, Pred pred = {}, Proj proj = {}) const + { + if (first != last) + { + *result = *first; + ++result; + + auto previous = first; + ++first; + + for (; first != last; ++first) + { + if (!etl::invoke(pred, etl::invoke(proj, *previous), etl::invoke(proj, *first))) + { + *result = *first; + ++result; + } + previous = first; + } + } + + return {etl::move(first), etl::move(result)}; + } + + template>> + constexpr ranges::unique_copy_result, O> + operator()(R&& r, O result, Pred pred = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(result), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr unique_copy_fn unique_copy {}; + + struct transform_fn + { + // Unary: iterator + sentinel + template>> + constexpr ranges::unary_transform_result + operator()(I first, S last, O result, F op, Proj proj = {}) const + { + for (; first != last; ++first, ++result) + { + *result = etl::invoke(op, etl::invoke(proj, *first)); + } + return {etl::move(first), etl::move(result)}; + } + + // Unary: range + template && !etl::is_range_v>> + constexpr ranges::unary_transform_result, O> + operator()(R&& r, O result, F op, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(result), etl::ref(op), etl::ref(proj)); + } + + // Binary: iterator + sentinel + template>> + constexpr ranges::binary_transform_result + operator()(I1 first1, S1 last1, I2 first2, S2 last2, O result, F op, Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + for (; first1 != last1 && first2 != last2; ++first1, ++first2, ++result) + { + *result = etl::invoke(op, etl::invoke(proj1, *first1), etl::invoke(proj2, *first2)); + } + return {etl::move(first1), etl::move(first2), etl::move(result)}; + } + + // Binary: range + template && etl::is_range_v>> + constexpr ranges::binary_transform_result, ranges::borrowed_iterator_t, O> + operator()(R1&& r1, R2&& r2, O result, F op, Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(result), etl::ref(op), etl::ref(proj1), etl::ref(proj2)); + } + }; + + inline constexpr transform_fn transform {}; + + struct reverse_fn + { + template>> + constexpr I operator()(I first, S last) const + { + I tail = ranges::next(first, last); + I result = tail; + + for (; first != tail && first != --tail; ++first) + { + etl::iter_swap(first, tail); + } + + return result; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r) const + { + return (*this)(ranges::begin(r), ranges::end(r)); + } + }; + + inline constexpr reverse_fn reverse {}; + + template + using reverse_copy_result = in_out_result; + + struct reverse_copy_fn + { + template>> + constexpr ranges::reverse_copy_result + operator()(I first, S last, O result) const + { + I tail = ranges::next(first, last); + I end_it = tail; + + while (tail != first) + { + *result = *--tail; + ++result; + } + + return {etl::move(end_it), etl::move(result)}; + } + + template>> + constexpr ranges::reverse_copy_result, O> + operator()(R&& r, O result) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(result)); + } + }; + + inline constexpr reverse_copy_fn reverse_copy {}; + + template + using rotate_result = ranges::subrange; + + struct rotate_fn + { + template>> + constexpr ranges::rotate_result + operator()(I first, I middle, S last) const + { + if (first == middle) + { + I last_it = ranges::next(first, last); + return {last_it, last_it}; + } + + I last_it = ranges::next(first, last); + + if (middle == last_it) + { + return {first, last_it}; + } + + I new_first = etl::rotate(first, middle, last_it); + return {etl::move(new_first), etl::move(last_it)}; + } + + template>> + constexpr ranges::rotate_result> + operator()(R&& r, ranges::iterator_t middle) const + { + return (*this)(ranges::begin(r), etl::move(middle), ranges::end(r)); + } + }; + + inline constexpr rotate_fn rotate {}; + + struct rotate_copy_fn + { + template>> + constexpr ranges::rotate_copy_result + operator()(I first, I middle, S last, O result) const + { + I last_it = ranges::next(first, last); + O end_out = etl::copy(middle, last_it, result); + end_out = etl::copy(first, middle, end_out); + return {etl::move(last_it), etl::move(end_out)}; + } + + template>> + constexpr ranges::rotate_copy_result, O> + operator()(R&& r, ranges::iterator_t middle, O result) const + { + return (*this)(ranges::begin(r), etl::move(middle), ranges::end(r), etl::move(result)); + } + }; + + inline constexpr rotate_copy_fn rotate_copy {}; + + struct shift_left_fn + { + template>> + constexpr ranges::subrange + operator()(I first, S last, etl::iter_difference_t n) const + { + I last_it = ranges::next(first, last); + + if (n <= 0) + { + return {first, last_it}; + } + + I mid = first; + if (ranges::advance(mid, n, last_it) != 0) + { + return {first, first}; + } + + I result = ranges::move(mid, last_it, first).out; + return {first, etl::move(result)}; + } + + template>> + constexpr ranges::borrowed_subrange_t + operator()(R&& r, etl::ranges::range_difference_t n) const + { + return (*this)(ranges::begin(r), ranges::end(r), n); + } + }; + + inline constexpr shift_left_fn shift_left {}; + + struct shift_right_fn + { + template>> + constexpr ranges::subrange + operator()(I first, S last, etl::iter_difference_t n) const + { + I last_it = ranges::next(first, last); + + if (n <= 0) + { + return {first, last_it}; + } + + I trail = last_it; + if (ranges::advance(trail, -n, first) != 0) + { + return {last_it, last_it}; + } + + I new_first = ranges::move_backward(first, trail, last_it).out; + return {etl::move(new_first), etl::move(last_it)}; + } + + template>> + constexpr ranges::borrowed_subrange_t + operator()(R&& r, etl::ranges::range_difference_t n) const + { + return (*this)(ranges::begin(r), ranges::end(r), n); + } + }; + + inline constexpr shift_right_fn shift_right {}; + + struct shuffle_fn + { + template>> + I operator()(I first, S last, Gen&& gen) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator::value, "shuffle requires random access iterators"); + + using diff_t = etl::iter_difference_t; + using udiff_t = etl::make_unsigned_t; + using gen_t = etl::remove_reference_t; + using uresult_t = decltype(gen()); + + I last_it = ranges::next(first, last); + diff_t n = last_it - first; + + if (n <= 1) + { + return last_it; + } + + for (diff_t i = n - 1; i > 0; --i) + { + // Generate a uniformly distributed random index in [0, i] + // using rejection sampling to avoid modulo bias. + udiff_t range = static_cast(i); + constexpr uresult_t gmin = gen_t::min(); + constexpr uresult_t gmax = gen_t::max(); + constexpr uresult_t grange = gmax - gmin; + + uresult_t j; + + if ETL_IF_CONSTEXPR (grange == static_cast(-1)) + { + // Generator covers full range of uresult_t, just use modulo with rejection + uresult_t limit = (static_cast(-1) / (static_cast(range) + 1)) * (static_cast(range) + 1); + do + { + j = static_cast(gen() - gmin); + } while (j >= limit); + j %= (static_cast(range) + 1); + } + else + { + uresult_t limit = (grange / (static_cast(range) + 1)) * (static_cast(range) + 1); + do + { + j = static_cast(gen() - gmin); + } while (j >= limit); + j %= (static_cast(range) + 1); + } + + etl::iter_swap(first + i, first + static_cast(j)); + } + + return last_it; + } + + template>> + ranges::borrowed_iterator_t + operator()(R&& r, Gen&& gen) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator>::value, "shuffle requires a range with random access iterators"); + + return (*this)(ranges::begin(r), ranges::end(r), static_cast(gen)); + } + }; + + inline constexpr shuffle_fn shuffle {}; + + struct sample_fn + { + template>> + O operator()(I first, S last, O out, etl::iter_difference_t n, Gen&& gen) const + { + using diff_t = etl::iter_difference_t; + using udiff_t = etl::make_unsigned_t; + using gen_t = etl::remove_reference_t; + using uresult_t = decltype(gen()); + + if (n <= 0) + { + return out; + } + + // Compute the size of [first, last). + I first_copy = first; + diff_t pop_size = 0; + for (I it = first_copy; it != last; ++it) + { + ++pop_size; + } + + if (pop_size <= n) + { + // Copy all elements. + for (; first != last; ++first, ++out) + { + *out = *first; + } + return out; + } + + // Selection sampling (Algorithm S / Vitter). + // For each element, decide whether to include it. + diff_t remaining = pop_size; + diff_t needed = n; + + for (; first != last && needed > 0; ++first, --remaining) + { + // Generate a uniformly distributed random number in [0, remaining). + udiff_t range = static_cast(remaining - 1); + constexpr uresult_t gmin = gen_t::min(); + constexpr uresult_t gmax = gen_t::max(); + constexpr uresult_t grange = gmax - gmin; + + uresult_t j; + + if ETL_IF_CONSTEXPR (grange == static_cast(-1)) + { + if (range == 0) + { + j = 0; + } + else + { + uresult_t limit = (static_cast(-1) / (static_cast(range) + 1)) * (static_cast(range) + 1); + do + { + j = static_cast(gen() - gmin); + } while (j >= limit); + j %= (static_cast(range) + 1); + } + } + else + { + if (range == 0) + { + j = 0; + } + else + { + uresult_t limit = (grange / (static_cast(range) + 1)) * (static_cast(range) + 1); + do + { + j = static_cast(gen() - gmin); + } while (j >= limit); + j %= (static_cast(range) + 1); + } + } + + if (static_cast(j) < needed) + { + *out = *first; + ++out; + --needed; + } + } + + return out; + } + + template>> + O operator()(R&& r, O out, etl::ranges::range_difference_t n, Gen&& gen) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(out), n, static_cast(gen)); + } + }; + + inline constexpr sample_fn sample {}; + + struct sort_fn + { + template>> + constexpr I operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator::value, "sort requires random access iterators"); + + I last_it = ranges::next(first, last); + + if (first == last_it) + { + return last_it; + } + + // Shell sort with projection support + auto n = etl::distance(first, last_it); + + for (auto gap = n / 2; gap > 0; gap /= 2) + { + for (auto i = gap; i < n; ++i) + { + auto temp = etl::move(*(first + i)); + auto j = i; + + while (j >= gap && etl::invoke(comp, etl::invoke(proj, temp), etl::invoke(proj, *(first + (j - gap))))) + { + *(first + j) = etl::move(*(first + (j - gap))); + j -= gap; + } + + *(first + j) = etl::move(temp); + } + } + + return last_it; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator>::value, "sort requires a range with random access iterators"); + + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr sort_fn sort {}; + + struct stable_sort_fn + { + template>> + constexpr I operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + I last_it = ranges::next(first, last); + + if (first == last_it) + { + return last_it; + } + + // Insertion sort with projection support (stable) + for (I i = ranges::next(first); i != last_it; ++i) + { + auto temp = etl::move(*i); + I j = i; + + while (j != first) + { + I prev_j = ranges::prev(j); + if (etl::invoke(comp, etl::invoke(proj, temp), etl::invoke(proj, *prev_j))) + { + *j = etl::move(*prev_j); + j = prev_j; + } + else + { + break; + } + } + + *j = etl::move(temp); + } + + return last_it; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr stable_sort_fn stable_sort {}; + + struct partial_sort_fn + { + template>> + constexpr I operator()(I first, I middle, S last, Comp comp = {}, Proj proj = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator::value, "partial_sort requires random access iterators"); + + I last_it = ranges::next(first, last); + + if (first == middle || first == last_it) + { + return last_it; + } + + // Build a max-heap on [first, middle) + auto heap_size = etl::distance(first, middle); + + // Heapify: process from the last parent down to 0 + for (auto start = (heap_size - 1) / 2; start >= 0; --start) + { + sift_down(first, start, heap_size, comp, proj); + } + + // For each element in [middle, last_it), if it is smaller than the heap root, + // swap it in and re-heapify + for (I it = middle; it != last_it; ++it) + { + if (etl::invoke(comp, etl::invoke(proj, *it), etl::invoke(proj, *first))) + { + etl::iter_swap(it, first); + sift_down(first, decltype(heap_size){0}, heap_size, comp, proj); + } + } + + // Sort the heap to produce a sorted [first, middle) + // Repeatedly extract the max from the heap + for (auto heap_end = heap_size - 1; heap_end > 0; --heap_end) + { + etl::iter_swap(first, first + heap_end); + sift_down(first, decltype(heap_size){0}, heap_end, comp, proj); + } + + return last_it; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, ranges::iterator_t middle, Comp comp = {}, Proj proj = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator>::value, "partial_sort requires a range with random access iterators"); + + return (*this)(ranges::begin(r), etl::move(middle), ranges::end(r), etl::move(comp), etl::move(proj)); + } + + private: + + template + static constexpr void sift_down(I first, DiffType index, DiffType heap_size, Comp& comp, Proj& proj) + { + while (true) + { + auto largest = index; + auto left = 2 * index + 1; + auto right = 2 * index + 2; + + if (left < heap_size && + etl::invoke(comp, etl::invoke(proj, *(first + largest)), etl::invoke(proj, *(first + left)))) + { + largest = left; + } + + if (right < heap_size && + etl::invoke(comp, etl::invoke(proj, *(first + largest)), etl::invoke(proj, *(first + right)))) + { + largest = right; + } + + if (largest == index) + { + break; + } + + etl::iter_swap(first + index, first + largest); + index = largest; + } + } + }; + + inline constexpr partial_sort_fn partial_sort {}; + + struct partial_sort_copy_fn + { + template>> + constexpr ranges::partial_sort_copy_result + operator()(I1 first, S1 last, I2 result_first, S2 result_last, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator::value, "partial_sort_copy requires the output to be random access iterators"); + + I1 in_last = ranges::next(first, last); + I2 out_last = ranges::next(result_first, result_last); + + I2 r = result_first; + + // Copy elements from the input range into the output range + for (I1 it = first; it != in_last && r != out_last; ++it, ++r) + { + *r = *it; + } + + auto heap_size = etl::distance(result_first, r); + + if (heap_size == 0) + { + return {etl::move(in_last), etl::move(r)}; + } + + // Build a max-heap on [result_first, r) + for (auto start = (heap_size - 1) / 2; start >= 0; --start) + { + sift_down(result_first, start, heap_size, comp, proj2); + } + + // For remaining elements in [first + heap_size, in_last), if smaller than + // the heap root, swap it in and re-heapify + I1 it = first; + etl::advance(it, heap_size); + for (; it != in_last; ++it) + { + if (etl::invoke(comp, etl::invoke(proj1, *it), etl::invoke(proj2, *result_first))) + { + *result_first = *it; + sift_down(result_first, decltype(heap_size){0}, heap_size, comp, proj2); + } + } + + // Sort the heap to produce a sorted output range + for (auto heap_end = heap_size - 1; heap_end > 0; --heap_end) + { + etl::iter_swap(result_first, result_first + heap_end); + sift_down(result_first, decltype(heap_size){0}, heap_end, comp, proj2); + } + + return {etl::move(in_last), etl::move(r)}; + } + + template>> + constexpr ranges::partial_sort_copy_result, ranges::borrowed_iterator_t> + operator()(R1&& r, R2&& result_r, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator>::value, "partial_sort_copy requires the output range to have random access iterators"); + + return (*this)(ranges::begin(r), ranges::end(r), ranges::begin(result_r), ranges::end(result_r), etl::move(comp), etl::move(proj1), etl::move(proj2)); + } + + private: + + template + static constexpr void sift_down(I first, DiffType index, DiffType heap_size, Comp& comp, Proj& proj) + { + while (true) + { + auto largest = index; + auto left = 2 * index + 1; + auto right = 2 * index + 2; + + if (left < heap_size && + etl::invoke(comp, etl::invoke(proj, *(first + largest)), etl::invoke(proj, *(first + left)))) + { + largest = left; + } + + if (right < heap_size && + etl::invoke(comp, etl::invoke(proj, *(first + largest)), etl::invoke(proj, *(first + right)))) + { + largest = right; + } + + if (largest == index) + { + break; + } + + etl::iter_swap(first + index, first + largest); + index = largest; + } + } + }; + + inline constexpr partial_sort_copy_fn partial_sort_copy {}; + + struct nth_element_fn + { + template>> + constexpr I operator()(I first, I nth, S last, Comp comp = {}, Proj proj = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator::value, "nth_element requires random access iterators"); + + I last_it = ranges::next(first, last); + + if (first == last_it || ranges::next(first) == last_it) + { + return last_it; + } + + I lo = first; + I hi = ranges::prev(last_it); + + while (lo <= hi) + { + I p = nth_partition(lo, hi, comp, proj); + + if (p == nth) + { + return last_it; + } + else if (p > nth) + { + hi = ranges::prev(p); + } + else + { + lo = ranges::next(p); + } + } + + return last_it; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, ranges::iterator_t nth, Comp comp = {}, Proj proj = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator>::value, "nth_element requires a range with random access iterators"); + + return (*this)(ranges::begin(r), etl::move(nth), ranges::end(r), etl::move(comp), etl::move(proj)); + } + + private: + + template + static constexpr I nth_partition(I first, I last, Comp& comp, Proj& proj) + { + if (first == last) + { + return first; + } + + if (last - first == 1) + { + if (etl::invoke(comp, etl::invoke(proj, *last), etl::invoke(proj, *first))) + { + etl::iter_swap(first, last); + } + return first; + } + + // Median-of-three pivot selection + I mid = first + (last - first) / 2; + + if (etl::invoke(comp, etl::invoke(proj, *mid), etl::invoke(proj, *first))) + { + etl::iter_swap(first, mid); + } + + if (etl::invoke(comp, etl::invoke(proj, *last), etl::invoke(proj, *first))) + { + etl::iter_swap(first, last); + } + + if (etl::invoke(comp, etl::invoke(proj, *mid), etl::invoke(proj, *last))) + { + etl::iter_swap(mid, last); + } + + // Pivot is now at *last + I i = first; + I j = last; + + while (true) + { + while (etl::invoke(comp, etl::invoke(proj, *i), etl::invoke(proj, *last))) + { + ++i; + } + + --j; + + while (i < j && etl::invoke(comp, etl::invoke(proj, *last), etl::invoke(proj, *j))) + { + --j; + } + + if (i >= j) + { + break; + } + + etl::iter_swap(i, j); + ++i; + } + + etl::iter_swap(i, last); + return i; + } + }; + + inline constexpr nth_element_fn nth_element {}; + + struct partition_fn + { + template>> + constexpr ranges::subrange + operator()(I first, S last, Pred pred, Proj proj = {}) const + { + first = ranges::find_if_not(first, last, etl::ref(pred), etl::ref(proj)); + + if (first == last) + { + return {first, first}; + } + + for (I i = ranges::next(first); i != last; ++i) + { + if (etl::invoke(pred, etl::invoke(proj, *i))) + { + etl::iter_swap(i, first); + ++first; + } + } + + return {first, ranges::next(first, last)}; + } + + template>> + constexpr ranges::borrowed_subrange_t + operator()(R&& r, Pred pred, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr partition_fn partition {}; + + struct is_partitioned_fn + { + template>> + constexpr bool operator()(I first, S last, Pred pred, Proj proj = {}) const + { + for (; first != last; ++first) + { + if (!etl::invoke(pred, etl::invoke(proj, *first))) + { + break; + } + } + + for (; first != last; ++first) + { + if (etl::invoke(pred, etl::invoke(proj, *first))) + { + return false; + } + } + + return true; + } + + template>> + constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr is_partitioned_fn is_partitioned {}; + + struct partition_copy_fn + { + template>> + constexpr ranges::partition_copy_result + operator()(I first, S last, O1 out_true, O2 out_false, Pred pred, Proj proj = {}) const + { + for (; first != last; ++first) + { + if (etl::invoke(pred, etl::invoke(proj, *first))) + { + *out_true = *first; + ++out_true; + } + else + { + *out_false = *first; + ++out_false; + } + } + + return {etl::move(first), etl::move(out_true), etl::move(out_false)}; + } + + template>> + constexpr ranges::partition_copy_result, O1, O2> + operator()(R&& r, O1 out_true, O2 out_false, Pred pred, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(out_true), etl::move(out_false), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr partition_copy_fn partition_copy {}; + + struct partition_point_fn + { + template>> + constexpr I operator()(I first, S last, Pred pred, Proj proj = {}) const + { + for (; first != last; ++first) + { + if (!etl::invoke(pred, etl::invoke(proj, *first))) + { + return first; + } + } + + return first; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Pred pred, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + }; + + inline constexpr partition_point_fn partition_point {}; + + struct stable_partition_fn + { + template>> + constexpr ranges::subrange + operator()(I first, S last, Pred pred, Proj proj = {}) const + { + // Find the first element that does not satisfy the predicate + first = ranges::find_if_not(first, last, etl::ref(pred), etl::ref(proj)); + + if (first == last) + { + return {first, first}; + } + + I last_it = ranges::next(first, last); + + I pp = stable_partition_impl(first, last_it, etl::ref(pred), etl::ref(proj), etl::distance(first, last_it)); + + return {pp, last_it}; + } + + template>> + constexpr ranges::borrowed_subrange_t + operator()(R&& r, Pred pred, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::ref(pred), etl::ref(proj)); + } + + private: + + template + static constexpr I stable_partition_impl(I first, I last, Pred pred, Proj proj, typename etl::iterator_traits::difference_type len) + { + if (len == 0) + { + return first; + } + + if (len == 1) + { + return etl::invoke(pred, etl::invoke(proj, *first)) ? ranges::next(first) : first; + } + + I middle = ranges::next(first, len / 2); + + I left_partition = stable_partition_impl(first, middle, etl::ref(pred), etl::ref(proj), len / 2); + I right_partition = stable_partition_impl(middle, last, etl::ref(pred), etl::ref(proj), len - len / 2); + + return etl::rotate(left_partition, middle, right_partition); + } + }; + + inline constexpr stable_partition_fn stable_partition {}; + + struct is_sorted_until_fn + { + template>> + constexpr I operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + if (first != last) + { + I next_it = ranges::next(first); + + while (next_it != last) + { + if (etl::invoke(comp, etl::invoke(proj, *next_it), etl::invoke(proj, *first))) + { + return next_it; + } + + first = next_it; + ++next_it; + } + } + + return ranges::next(first, last); + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr is_sorted_until_fn is_sorted_until {}; + + struct is_sorted_fn + { + template>> + constexpr bool operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + return ranges::is_sorted_until(first, last, etl::ref(comp), etl::ref(proj)) == last; + } + + template>> + constexpr bool operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr is_sorted_fn is_sorted {}; + + struct lower_bound_fn + { + template>> + constexpr I operator()(I first, S last, const T& value, Comp comp = {}, Proj proj = {}) const + { + auto len = etl::distance(first, last); + + while (len > 0) + { + auto half = len / 2; + I middle = ranges::next(first, half); + + if (etl::invoke(comp, etl::invoke(proj, *middle), value)) + { + first = ranges::next(middle); + len -= half + 1; + } + else + { + len = half; + } + } + + return first; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, const T& value, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), value, etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr lower_bound_fn lower_bound {}; + + struct upper_bound_fn + { + template>> + constexpr I operator()(I first, S last, const T& value, Comp comp = {}, Proj proj = {}) const + { + auto len = etl::distance(first, last); + + while (len > 0) + { + auto half = len / 2; + I middle = ranges::next(first, half); + + if (!etl::invoke(comp, value, etl::invoke(proj, *middle))) + { + first = ranges::next(middle); + len -= half + 1; + } + else + { + len = half; + } + } + + return first; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, const T& value, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), value, etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr upper_bound_fn upper_bound {}; + + struct equal_range_fn + { + template>> + constexpr ranges::subrange + operator()(I first, S last, const T& value, Comp comp = {}, Proj proj = {}) const + { + return {ranges::lower_bound(first, last, value, etl::ref(comp), etl::ref(proj)), + ranges::upper_bound(first, last, value, etl::ref(comp), etl::ref(proj))}; + } + + template>> + constexpr ranges::borrowed_subrange_t + operator()(R&& r, const T& value, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), value, etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr equal_range_fn equal_range {}; + + struct binary_search_fn + { + template>> + ETL_NODISCARD + constexpr bool operator()(I first, S last, const T& value, Comp comp = {}, Proj proj = {}) const + { + first = ranges::lower_bound(first, last, value, etl::ref(comp), etl::ref(proj)); + + return (!(first == last) && !(etl::invoke(comp, value, etl::invoke(proj, *first)))); + } + + template>> + ETL_NODISCARD + constexpr bool operator()(R&& r, const T& value, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), value, etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr binary_search_fn binary_search {}; + + struct includes_fn + { + template>> + ETL_NODISCARD + constexpr bool + operator()(I1 first1, S1 last1, I2 first2, S2 last2, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + for (; first2 != last2; ++first1) + { + if (first1 == last1) + { + return false; + } + + if (etl::invoke(comp, etl::invoke(proj2, *first2), etl::invoke(proj1, *first1))) + { + return false; + } + + if (!etl::invoke(comp, etl::invoke(proj1, *first1), etl::invoke(proj2, *first2))) + { + ++first2; + } + } + + return true; + } + + template>> + ETL_NODISCARD + constexpr bool + operator()(R1&& r1, R2&& r2, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(comp), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr includes_fn includes {}; + + struct merge_fn + { + template>> + constexpr ranges::merge_result + operator()(I1 first1, S1 last1, I2 first2, S2 last2, O result, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + while (first1 != last1 && first2 != last2) + { + if (etl::invoke(comp, etl::invoke(proj2, *first2), etl::invoke(proj1, *first1))) + { + *result = *first2; + ++first2; + } + else + { + *result = *first1; + ++first1; + } + ++result; + } + + while (first1 != last1) + { + *result = *first1; + ++first1; + ++result; + } + + while (first2 != last2) + { + *result = *first2; + ++first2; + ++result; + } + + return {etl::move(first1), etl::move(first2), etl::move(result)}; + } + + template>> + constexpr ranges::merge_result, ranges::borrowed_iterator_t, O> + operator()(R1&& r1, R2&& r2, O result, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(result), etl::move(comp), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr merge_fn merge {}; + + struct inplace_merge_fn + { + template>> + constexpr I operator()(I first, I middle, S last, Comp comp = {}, Proj proj = {}) const + { + I last_it = ranges::next(first, last); + + if (first == middle || middle == last_it) + { + return last_it; + } + + inplace_merge_impl(first, middle, last_it, comp, proj, + etl::distance(first, middle), + etl::distance(middle, last_it)); + + return last_it; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, ranges::iterator_t middle, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), etl::move(middle), ranges::end(r), etl::move(comp), etl::move(proj)); + } + + private: + + template + static constexpr void inplace_merge_impl(I first, I middle, I last, Comp& comp, Proj& proj, + typename etl::iterator_traits::difference_type len1, + typename etl::iterator_traits::difference_type len2) + { + if (len1 == 0 || len2 == 0) + { + return; + } + + if (len1 + len2 == 2) + { + if (etl::invoke(comp, etl::invoke(proj, *middle), etl::invoke(proj, *first))) + { + etl::iter_swap(first, middle); + } + return; + } + + I first_cut; + I second_cut; + typename etl::iterator_traits::difference_type new_len1; + typename etl::iterator_traits::difference_type new_len2; + + if (len1 > len2) + { + new_len1 = len1 / 2; + first_cut = ranges::next(first, new_len1); + second_cut = ranges::lower_bound(middle, last, etl::invoke(proj, *first_cut), etl::ref(comp), etl::ref(proj)); + new_len2 = etl::distance(middle, second_cut); + } + else + { + new_len2 = len2 / 2; + second_cut = ranges::next(middle, new_len2); + first_cut = ranges::upper_bound(first, middle, etl::invoke(proj, *second_cut), etl::ref(comp), etl::ref(proj)); + new_len1 = etl::distance(first, first_cut); + } + + I new_middle; + // Due to a non-standard etl::rotate implementation, we need to handle + // the case where one of the cuts is the middle separately to avoid + // returning an iterator outside of [first, last) + // As soon as etl::rotate is fixed to return an iterator in the middle + // of the rotated range, this can be simplified to just calling etl::rotate + if (first_cut == middle) + { + new_middle = second_cut; + } + else if (second_cut == middle) + { + new_middle = first_cut; + } + else + { + new_middle = etl::rotate(first_cut, middle, second_cut); + } + + inplace_merge_impl(first, first_cut, new_middle, comp, proj, + new_len1, new_len2); + inplace_merge_impl(new_middle, second_cut, last, comp, proj, + len1 - new_len1, len2 - new_len2); + } + }; + + inline constexpr inplace_merge_fn inplace_merge {}; + + struct set_union_fn + { + template>> + constexpr ranges::set_union_result + operator()(I1 first1, S1 last1, I2 first2, S2 last2, O result, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + while (first1 != last1 && first2 != last2) + { + if (etl::invoke(comp, etl::invoke(proj1, *first1), etl::invoke(proj2, *first2))) + { + *result = *first1; + ++first1; + } + else if (etl::invoke(comp, etl::invoke(proj2, *first2), etl::invoke(proj1, *first1))) + { + *result = *first2; + ++first2; + } + else + { + *result = *first1; + ++first1; + ++first2; + } + ++result; + } + + while (first1 != last1) + { + *result = *first1; + ++first1; + ++result; + } + + while (first2 != last2) + { + *result = *first2; + ++first2; + ++result; + } + + return {etl::move(first1), etl::move(first2), etl::move(result)}; + } + + template>> + constexpr ranges::set_union_result, ranges::borrowed_iterator_t, O> + operator()(R1&& r1, R2&& r2, O result, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(result), etl::move(comp), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr set_union_fn set_union {}; + + struct set_intersection_fn + { + template>> + constexpr ranges::set_intersection_result + operator()(I1 first1, S1 last1, I2 first2, S2 last2, O result, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + while (first1 != last1 && first2 != last2) + { + if (etl::invoke(comp, etl::invoke(proj1, *first1), etl::invoke(proj2, *first2))) + { + ++first1; + } + else if (etl::invoke(comp, etl::invoke(proj2, *first2), etl::invoke(proj1, *first1))) + { + ++first2; + } + else + { + *result = *first1; + ++first1; + ++first2; + ++result; + } + } + + return {etl::move(first1), etl::move(first2), etl::move(result)}; + } + + template>> + constexpr ranges::set_intersection_result, ranges::borrowed_iterator_t, O> + operator()(R1&& r1, R2&& r2, O result, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(result), etl::move(comp), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr set_intersection_fn set_intersection {}; + + struct set_difference_fn + { + template>> + constexpr ranges::set_difference_result + operator()(I1 first1, S1 last1, I2 first2, S2 last2, O result, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + while (first1 != last1 && first2 != last2) + { + if (etl::invoke(comp, etl::invoke(proj1, *first1), etl::invoke(proj2, *first2))) + { + *result = *first1; + ++first1; + ++result; + } + else if (etl::invoke(comp, etl::invoke(proj2, *first2), etl::invoke(proj1, *first1))) + { + ++first2; + } + else + { + ++first1; + ++first2; + } + } + + while (first1 != last1) + { + *result = *first1; + ++first1; + ++result; + } + + return {etl::move(first1), etl::move(result)}; + } + + template>> + constexpr ranges::set_difference_result, O> + operator()(R1&& r1, R2&& r2, O result, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(result), etl::move(comp), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr set_difference_fn set_difference {}; + + struct set_symmetric_difference_fn + { + template>> + constexpr ranges::set_symmetric_difference_result + operator()(I1 first1, S1 last1, I2 first2, S2 last2, O result, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + while (first1 != last1 && first2 != last2) + { + if (etl::invoke(comp, etl::invoke(proj1, *first1), etl::invoke(proj2, *first2))) + { + *result = *first1; + ++first1; + ++result; + } + else if (etl::invoke(comp, etl::invoke(proj2, *first2), etl::invoke(proj1, *first1))) + { + *result = *first2; + ++first2; + ++result; + } + else + { + ++first1; + ++first2; + } + } + + while (first1 != last1) + { + *result = *first1; + ++first1; + ++result; + } + + while (first2 != last2) + { + *result = *first2; + ++first2; + ++result; + } + + return {etl::move(first1), etl::move(first2), etl::move(result)}; + } + + template>> + constexpr ranges::set_symmetric_difference_result, ranges::borrowed_iterator_t, O> + operator()(R1&& r1, R2&& r2, O result, Comp comp = {}, + Proj1 proj1 = {}, Proj2 proj2 = {}) const + { + return (*this)(ranges::begin(r1), ranges::end(r1), + ranges::begin(r2), ranges::end(r2), + etl::move(result), etl::move(comp), etl::move(proj1), etl::move(proj2)); + } + }; + + inline constexpr set_symmetric_difference_fn set_symmetric_difference {}; + + struct make_heap_fn + { + private: + + template + static constexpr void sift_down(I first, typename etl::iterator_traits::difference_type index, + typename etl::iterator_traits::difference_type length, + Comp& comp, Proj& proj) + { + while (true) + { + auto child = 2 * index + 1; + + if (child >= length) + { + break; + } + + if ((child + 1 < length) && + etl::invoke(comp, etl::invoke(proj, *(first + child)), + etl::invoke(proj, *(first + (child + 1))))) + { + ++child; + } + + if (!etl::invoke(comp, etl::invoke(proj, *(first + index)), + etl::invoke(proj, *(first + child)))) + { + break; + } + + etl::iter_swap(first + index, first + child); + index = child; + } + } + + public: + + template>> + constexpr I operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + I last_it = ranges::next(first, last); + + auto length = etl::distance(first, last_it); + + if (length < 2) + { + return last_it; + } + + auto parent = (length - 2) / 2; + + while (true) + { + sift_down(first, parent, length, comp, proj); + + if (parent == 0) + { + break; + } + + --parent; + } + + return last_it; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr make_heap_fn make_heap {}; + + struct push_heap_fn + { + template>> + constexpr I operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator::value, "push_heap requires random access iterators"); + + I last_it = ranges::next(first, last); + + auto length = etl::distance(first, last_it); + + if (length < 2) + { + return last_it; + } + + auto value_index = length - 1; + auto parent = (value_index - 1) / 2; + auto value = etl::move(*(first + value_index)); + + while ((value_index > 0) && + etl::invoke(comp, etl::invoke(proj, *(first + parent)), + etl::invoke(proj, value))) + { + *(first + value_index) = etl::move(*(first + parent)); + value_index = parent; + parent = (value_index - 1) / 2; + } + + *(first + value_index) = etl::move(value); + + return last_it; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr push_heap_fn push_heap {}; + + struct pop_heap_fn + { + private: + + template + static constexpr void sift_down(I first, typename etl::iterator_traits::difference_type index, + typename etl::iterator_traits::difference_type length, + Comp& comp, Proj& proj) + { + while (true) + { + auto child = 2 * index + 1; + + if (child >= length) + { + break; + } + + if ((child + 1 < length) && + etl::invoke(comp, etl::invoke(proj, *(first + child)), + etl::invoke(proj, *(first + (child + 1))))) + { + ++child; + } + + if (!etl::invoke(comp, etl::invoke(proj, *(first + index)), + etl::invoke(proj, *(first + child)))) + { + break; + } + + etl::iter_swap(first + index, first + child); + index = child; + } + } + + public: + + template>> + constexpr I operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator::value, "pop_heap requires random access iterators"); + + I last_it = ranges::next(first, last); + + auto length = etl::distance(first, last_it); + + if (length < 2) + { + return last_it; + } + + --last_it; + + etl::iter_swap(first, last_it); + + sift_down(first, decltype(length)(0), etl::distance(first, last_it), comp, proj); + + return ranges::next(first, last); + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr pop_heap_fn pop_heap {}; + + struct is_heap_until_fn + { + template>> + constexpr I operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator::value, "is_heap_until requires random access iterators"); + + I last_it = ranges::next(first, last); + + auto length = etl::distance(first, last_it); + + decltype(length) parent = 0; + + for (decltype(length) child = 1; child < length; ++child) + { + if (etl::invoke(comp, etl::invoke(proj, *(first + parent)), + etl::invoke(proj, *(first + child)))) + { + return first + child; + } + + if ((child & 1) == 0) + { + ++parent; + } + } + + return last_it; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr is_heap_until_fn is_heap_until {}; + + struct is_heap_fn + { + template>> + constexpr bool operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + return ranges::is_heap_until(first, last, etl::ref(comp), etl::ref(proj)) == last; + } + + template>> + constexpr bool operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr is_heap_fn is_heap {}; + + struct sort_heap_fn + { + template>> + constexpr I operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + ETL_STATIC_ASSERT(etl::is_random_access_iterator::value, "sort_heap requires random access iterators"); + + I last_it = ranges::next(first, last); + I current_last = last_it; + + while (first != current_last) + { + ranges::pop_heap(first, current_last, comp, proj); + --current_last; + } + + return last_it; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr sort_heap_fn sort_heap {}; + + struct min_fn + { + template + constexpr const T& operator()(const T& a, const T& b, Comp comp = {}, Proj proj = {}) const + { + return etl::invoke(comp, etl::invoke(proj, b), etl::invoke(proj, a)) ? b : a; + } + + template + constexpr T operator()(std::initializer_list r, Comp comp = {}, Proj proj = {}) const + { + auto first = r.begin(); + auto last = r.end(); + + auto smallest = first; + while (++first != last) + { + if (etl::invoke(comp, etl::invoke(proj, *first), etl::invoke(proj, *smallest))) + { + smallest = first; + } + } + return *smallest; + } + + template>> + constexpr ranges::range_value_t operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + auto first = ranges::begin(r); + auto last = ranges::end(r); + + auto smallest = first; + while (++first != last) + { + if (etl::invoke(comp, etl::invoke(proj, *first), etl::invoke(proj, *smallest))) + { + smallest = first; + } + } + return *smallest; + } + }; + + inline constexpr min_fn min {}; + + struct min_element_fn + { + template>> + constexpr I operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + if (first == last) + { + return first; + } + + I smallest = first; + ++first; + + for (; first != last; ++first) + { + if (etl::invoke(comp, etl::invoke(proj, *first), etl::invoke(proj, *smallest))) + { + smallest = first; + } + } + + return smallest; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr min_element_fn min_element {}; + + struct max_fn + { + template + constexpr const T& operator()(const T& a, const T& b, Comp comp = {}, Proj proj = {}) const + { + return etl::invoke(comp, etl::invoke(proj, a), etl::invoke(proj, b)) ? b : a; + } + + template + constexpr T operator()(std::initializer_list r, Comp comp = {}, Proj proj = {}) const + { + auto first = r.begin(); + auto last = r.end(); + + auto largest = first; + while (++first != last) + { + if (etl::invoke(comp, etl::invoke(proj, *largest), etl::invoke(proj, *first))) + { + largest = first; + } + } + return *largest; + } + + template>> + constexpr ranges::range_value_t operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + auto first = ranges::begin(r); + auto last = ranges::end(r); + + auto largest = first; + while (++first != last) + { + if (etl::invoke(comp, etl::invoke(proj, *largest), etl::invoke(proj, *first))) + { + largest = first; + } + } + return *largest; + } + }; + + inline constexpr max_fn max {}; + + struct max_element_fn + { + template>> + constexpr I operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + if (first == last) + { + return first; + } + + I largest = first; + ++first; + + for (; first != last; ++first) + { + if (etl::invoke(comp, etl::invoke(proj, *largest), etl::invoke(proj, *first))) + { + largest = first; + } + } + + return largest; + } + + template>> + constexpr ranges::borrowed_iterator_t + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr max_element_fn max_element {}; + + struct minmax_fn + { + template + constexpr ranges::minmax_result operator()(const T& a, const T& b, Comp comp = {}, Proj proj = {}) const + { + if (etl::invoke(comp, etl::invoke(proj, b), etl::invoke(proj, a))) + { + return {b, a}; + } + return {a, b}; + } + + template + constexpr ranges::minmax_result operator()(std::initializer_list r, Comp comp = {}, Proj proj = {}) const + { + auto first = r.begin(); + auto last = r.end(); + + auto smallest = first; + auto largest = first; + + while (++first != last) + { + if (etl::invoke(comp, etl::invoke(proj, *first), etl::invoke(proj, *smallest))) + { + smallest = first; + } + if (etl::invoke(comp, etl::invoke(proj, *largest), etl::invoke(proj, *first))) + { + largest = first; + } + } + return {*smallest, *largest}; + } + + template>> + constexpr ranges::minmax_result> operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + auto first = ranges::begin(r); + auto last = ranges::end(r); + + auto smallest = first; + auto largest = first; + + while (++first != last) + { + if (etl::invoke(comp, etl::invoke(proj, *first), etl::invoke(proj, *smallest))) + { + smallest = first; + } + if (etl::invoke(comp, etl::invoke(proj, *largest), etl::invoke(proj, *first))) + { + largest = first; + } + } + return {*smallest, *largest}; + } + }; + + inline constexpr minmax_fn minmax {}; + + struct minmax_element_fn + { + template>> + constexpr ranges::minmax_element_result operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + if (first == last) + { + return {first, first}; + } + + I smallest = first; + I largest = first; + ++first; + + for (; first != last; ++first) + { + if (etl::invoke(comp, etl::invoke(proj, *first), etl::invoke(proj, *smallest))) + { + smallest = first; + } + if (etl::invoke(comp, etl::invoke(proj, *largest), etl::invoke(proj, *first))) + { + largest = first; + } + } + + return {smallest, largest}; + } + + template>> + constexpr ranges::minmax_element_result> + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr minmax_element_fn minmax_element {}; + + struct clamp_fn + { + template + constexpr const T& operator()(const T& value, const T& low, const T& high, Comp comp = {}, Proj proj = {}) const + { + auto&& projected_value = etl::invoke(proj, value); + + return etl::invoke(comp, projected_value, etl::invoke(proj, low)) ? low : + etl::invoke(comp, etl::invoke(proj, high), projected_value) ? high : + value; + } + }; + + inline constexpr clamp_fn clamp {}; + + struct next_permutation_fn + { + template>> + constexpr ranges::next_permutation_result + operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + I last_it = ranges::next(first, last); + + // Empty or single-element range: already at last permutation + if (first == last_it) + { + return {etl::move(last_it), false}; + } + + I i = last_it; + --i; + + if (i == first) + { + return {etl::move(last_it), false}; + } + + for (;;) + { + I i1 = i; + --i; + + // Find the rightmost element where projected *i < projected *i1 + if (etl::invoke(comp, etl::invoke(proj, *i), etl::invoke(proj, *i1))) + { + // Find the rightmost element j where projected *j > projected *i + I j = last_it; + while (!etl::invoke(comp, etl::invoke(proj, *i), etl::invoke(proj, *--j))) + { + } + + etl::iter_swap(i, j); + + // Reverse from i1 to last + I left = i1; + I right = last_it; + while (left != right && left != --right) + { + etl::iter_swap(left, right); + ++left; + } + + return {etl::move(last_it), true}; + } + + if (i == first) + { + // Already at last (ascending) permutation: wrap to first (descending) + I left = first; + I right = last_it; + while (left != right && left != --right) + { + etl::iter_swap(left, right); + ++left; + } + + return {etl::move(last_it), false}; + } + } + } + + template>> + constexpr ranges::next_permutation_result> + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr next_permutation_fn next_permutation {}; + + struct prev_permutation_fn + { + template>> + constexpr ranges::prev_permutation_result + operator()(I first, S last, Comp comp = {}, Proj proj = {}) const + { + I last_it = ranges::next(first, last); + + // Empty or single-element range: already at last permutation + if (first == last_it) + { + return {etl::move(last_it), false}; + } + + I i = last_it; + --i; + + if (i == first) + { + return {etl::move(last_it), false}; + } + + for (;;) + { + I i1 = i; + --i; + + // Find the rightmost element where projected *i > projected *i1 + if (etl::invoke(comp, etl::invoke(proj, *i1), etl::invoke(proj, *i))) + { + // Find the rightmost element j where projected *j < projected *i + I j = last_it; + while (!etl::invoke(comp, etl::invoke(proj, *--j), etl::invoke(proj, *i))) + { + } + + etl::iter_swap(i, j); + + // Reverse from i1 to last + I left = i1; + I right = last_it; + while (left != right && left != --right) + { + etl::iter_swap(left, right); + ++left; + } + + return {etl::move(last_it), true}; + } + + if (i == first) + { + // Already at last (descending) permutation: wrap to first (ascending) + I left = first; + I right = last_it; + while (left != right && left != --right) + { + etl::iter_swap(left, right); + ++left; + } + + return {etl::move(last_it), false}; + } + } + } + + template>> + constexpr ranges::prev_permutation_result> + operator()(R&& r, Comp comp = {}, Proj proj = {}) const + { + return (*this)(ranges::begin(r), ranges::end(r), etl::move(comp), etl::move(proj)); + } + }; + + inline constexpr prev_permutation_fn prev_permutation {}; +} +#endif + } #include "private/minmax_pop.h" diff --git a/include/etl/alignment.h b/include/etl/alignment.h index 41e8a20c..d46c582d 100644 --- a/include/etl/alignment.h +++ b/include/etl/alignment.h @@ -37,7 +37,6 @@ SOFTWARE. #include "error_handler.h" #include "exception.h" #include "utility.h" -#include "algorithm.h" #include diff --git a/include/etl/expected.h b/include/etl/expected.h index 758e2404..1ba1a1b4 100644 --- a/include/etl/expected.h +++ b/include/etl/expected.h @@ -41,6 +41,7 @@ SOFTWARE. #include "initializer_list.h" #include "type_traits.h" #include "invoke.h" +#include "memory.h" namespace etl { diff --git a/include/etl/functional.h b/include/etl/functional.h index e313325e..f0a85d71 100644 --- a/include/etl/functional.h +++ b/include/etl/functional.h @@ -79,6 +79,17 @@ namespace etl return *t; } +#if ETL_USING_CPP11 + // implementation without etl::invoke, which would add a circular dependency + template + ETL_CONSTEXPR20 auto operator()(TArgs&&... args) const + noexcept(noexcept(etl::declval()(etl::declval()...))) + -> decltype(etl::declval()(etl::declval()...)) + { + return get()(etl::forward(args)...); + } +#endif + private: T* t; @@ -646,7 +657,41 @@ namespace etl return private_functional::const_mem_fn_impl(member_function); } #endif + +#if ETL_USING_CPP14 + struct identity + { + template + constexpr T&& operator()(T&& t) const noexcept + { + return etl::forward(t); + } + }; +#endif + +#if ETL_USING_CPP17 +namespace ranges +{ + struct equal_to + { + template + constexpr auto operator()(T&& t, U&& u) const -> decltype(static_cast(t) == static_cast(u)) + { + return static_cast(t) == static_cast(u); + } + }; + + struct less + { + template + constexpr auto operator()(T&& t, U&& u) const -> decltype(static_cast(t) < static_cast(u)) + { + return static_cast(t) < static_cast(u); + } + }; +} +#endif + } #endif - diff --git a/include/etl/invoke.h b/include/etl/invoke.h index be9bbbfb..4336b4e9 100644 --- a/include/etl/invoke.h +++ b/include/etl/invoke.h @@ -110,7 +110,7 @@ namespace etl //**************************************************************************** /// Pointer to member object + object (or derived) reference - template >::value && !etl::is_pointer>::value && @@ -121,11 +121,37 @@ namespace etl return etl::forward(obj).*f; } + //**************************************************************************** + /// reference_wrapper callable (unwrap and call directly) + template >::value && + !etl::is_member_pointer().get())>>::value>> + ETL_CONSTEXPR auto invoke(TFunction&& f, TArgs&&... args) + -> decltype(f.get()(etl::forward(args)...)) + { + return f.get()(etl::forward(args)...); + } + + //**************************************************************************** + /// reference_wrapper callable wrapping a member pointer (unwrap and re-invoke) + template >::value && + etl::is_member_pointer().get())>>::value>, + typename = void> + ETL_CONSTEXPR auto invoke(TFunction&& f, TArgs&&... args) + -> decltype(etl::invoke(f.get(), etl::forward(args)...)) + { + return etl::invoke(f.get(), etl::forward(args)...); + } + //**************************************************************************** /// General callable (function object / lambda / function pointer) template >::value>> + typename = etl::enable_if_t>::value && + !etl::is_reference_wrapper>::value>> ETL_CONSTEXPR auto invoke(TFunction&& f, TArgs&&... args) -> decltype(etl::forward(f)(etl::forward(args)...)) { @@ -204,11 +230,30 @@ namespace etl using invoke_result_impl_t = typename invoke_result_impl::type; //******************************************* - // Map raw function type to pointer. + // Unwrap reference_wrapper to its underlying type T&, + // forwarding to etl::unwrap_ref_decay for reference_wrapper detection. + template >::value> + struct unwrap_ref_callable + { + using type = TFunction; + }; + + template + struct unwrap_ref_callable + { + using type = etl::unwrap_ref_decay_t; + }; + + template + using unwrap_ref_callable_t = typename unwrap_ref_callable::type; + + //******************************************* + // Map raw function type to pointer, and unwrap reference_wrapper + // so that function_traits sees the actual callable type. template using effective_callable_t = etl::conditional_t>::value, etl::add_pointer_t>, - TFunction>; + unwrap_ref_callable_t>; } //**************************************************************************** diff --git a/include/etl/iterator.h b/include/etl/iterator.h index d3b47d20..292298c4 100644 --- a/include/etl/iterator.h +++ b/include/etl/iterator.h @@ -34,6 +34,7 @@ SOFTWARE. #include "platform.h" #include "type_traits.h" #include "utility.h" +#include "invoke.h" #include "private/addressof.h" #if ETL_USING_STL || defined(ETL_IN_UNIT_TEST) @@ -92,10 +93,19 @@ namespace etl //*************************************************************************** // advance + template + ETL_CONSTEXPR14 void advance_helper(TIterator& itr, TDistance n, ETL_OR_STD::input_iterator_tag) + { + while (n-- > 0) + { + ++itr; + } + } + template ETL_CONSTEXPR14 void advance_helper(TIterator& itr, TDistance n, ETL_OR_STD::output_iterator_tag) { - while (n--) + while (n-- > 0) { ++itr; } @@ -104,7 +114,7 @@ namespace etl template ETL_CONSTEXPR14 void advance_helper(TIterator& itr, TDistance n, ETL_OR_STD::forward_iterator_tag) { - while (n--) + while (n-- > 0) { ++itr; } @@ -1212,6 +1222,725 @@ namespace etl #define ETL_ARRAY_SIZE(a) sizeof(etl::array_size(a)) +#if ETL_USING_CPP17 + template + using iter_value_t = typename etl::iterator_traits>::value_type; + + template + using iter_reference_t = decltype(*etl::declval()); + +#if ETL_USING_CPP20 + template + using iter_const_reference_t = typename etl::common_reference_t&&, etl::iter_reference_t>; +#endif + + template + using iter_difference_t = typename etl::iterator_traits>::difference_type; + + template + using projected_value_t = etl::remove_cvref_t>>; + + namespace ranges + { + namespace private_ranges + { + struct begin + { + template + constexpr auto operator()(T& t) const + { + return ETL_OR_STD::begin(t); + } + }; + + struct end + { + template + constexpr auto operator()(T& t) const + { + return ETL_OR_STD::end(t); + } + }; + + struct cbegin + { + template + constexpr auto operator()(T& t) const + { + return ETL_OR_STD::cbegin(t); + } + }; + + struct cend + { + template + constexpr auto operator()(T& t) const + { + return ETL_OR_STD::cend(t); + } + }; + + struct rbegin + { + template + constexpr auto operator()(T& t) const + { + return ETL_OR_STD::rbegin(t); + } + }; + + struct rend + { + template + constexpr auto operator()(T& t) const + { + return ETL_OR_STD::rend(t); + } + }; + + struct crbegin + { + template + constexpr auto operator()(T& t) const + { + return ETL_OR_STD::crbegin(t); + } + }; + + struct crend + { + template + constexpr auto operator()(T& t) const + { + return ETL_OR_STD::crend(t); + } + }; + + template + struct has_size_member : etl::false_type {}; + + template + struct has_size_member().size())>> : etl::true_type {}; + + template + struct has_empty_member : etl::false_type {}; + + template + struct has_empty_member().empty())>> : etl::true_type {}; + + struct distance + { + // Overload for common ranges (iterator == sentinel type) + template::value || etl::is_output_iterator_concept::value>> + constexpr etl::iter_difference_t operator()(I first, I last) const + { + if constexpr (etl::is_random_access_iterator_concept::value) + { + return last - first; + } + else + { + etl::iter_difference_t n = 0; + while (!(first == last)) + { + ++first; + ++n; + } + return n; + } + } + + // Overload for non-common ranges (iterator != sentinel type) + template::value || etl::is_output_iterator_concept::value) && + !etl::is_same::value>> + constexpr etl::iter_difference_t operator()(I first, S last) const + { + etl::iter_difference_t n = 0; + while (!(first == last)) + { + ++first; + ++n; + } + return n; + } + }; + + struct size + { + template + constexpr size_t operator()(T&& t) const + { + using U = etl::remove_cvref_t; + + if constexpr (has_size_member::value) + { + return static_cast(t.size()); + } + else + { + using iter_type = decltype(ETL_OR_STD::begin(t)); + static_assert(etl::is_forward_iterator_concept::value, + "ranges::size requires a sized range or at least a forward range; " + "single-pass input ranges are not supported"); + return static_cast(distance{}(ETL_OR_STD::begin(t), ETL_OR_STD::end(t))); + } + } + }; + + struct ssize + { + template + constexpr auto operator()(T&& t) const + { + using U = etl::remove_cvref_t; + + if constexpr (has_size_member::value) + { + return static_cast(t.size()); + } + else + { + using iter_type = decltype(ETL_OR_STD::begin(t)); + static_assert(etl::is_forward_iterator_concept::value, + "ranges::ssize requires a sized range or at least a forward range; " + "single-pass input ranges are not supported"); + return static_cast(distance{}(ETL_OR_STD::begin(t), ETL_OR_STD::end(t))); + } + } + }; + + struct empty + { + template + constexpr auto operator()(T&& t) const + { + using U = etl::remove_cvref_t; + + if constexpr (has_empty_member::value) + { + return t.empty(); + } + else + { + return ETL_OR_STD::cbegin(t) == ETL_OR_STD::cend(t); + } + } + }; + + struct data + { + template + constexpr auto operator()(T& t) const + { + return ETL_OR_STD::data(t); + } + }; + + struct cdata + { + template + constexpr etl::add_pointer_t()))>>> operator()(T& t) const + { + return ETL_OR_STD::data(t); + } + }; + } + + inline constexpr private_ranges::begin begin; + inline constexpr private_ranges::end end; + inline constexpr private_ranges::cbegin cbegin; + inline constexpr private_ranges::cend cend; + inline constexpr private_ranges::rbegin rbegin; + inline constexpr private_ranges::rend rend; + inline constexpr private_ranges::crbegin crbegin; + inline constexpr private_ranges::crend crend; + inline constexpr private_ranges::size size; + inline constexpr private_ranges::ssize ssize; + inline constexpr private_ranges::empty empty; + inline constexpr private_ranges::data data; + inline constexpr private_ranges::cdata cdata; + inline constexpr private_ranges::distance distance; + + //************************************************************************* + /// Range primitives. + //************************************************************************* + + template + using iterator_t = decltype(etl::ranges::begin(etl::declval())); + + template + using const_iterator_t = decltype(etl::ranges::cbegin(etl::declval())); + + template + using sentinel_t = decltype(etl::ranges::end(etl::declval())); + + template + using const_sentinel_t = decltype(etl::ranges::cend(etl::declval())); + + template + using range_size_t = decltype(etl::ranges::size(etl::declval())); + + template + using range_difference_t = etl::iter_difference_t>; + + template + using range_value_t = etl::iter_value_t>; + + template + using range_reference_t = etl::iter_reference_t>; + + struct advance_fn + { + template::value || etl::is_output_iterator_concept::value) && etl::is_integral>::value>> + constexpr void operator()(I& i, etl::iter_difference_t n) const + { + if constexpr (etl::is_random_access_iterator_concept::value) + { + i += n; + } + else + { + while (n > 0) + { + --n; + ++i; + } + + if constexpr (etl::is_bidirectional_iterator_concept::value) + { + while (n < 0) + { + ++n; + --i; + } + } + } + } + + template::value || etl::is_output_iterator_concept::value) && !etl::is_integral::value>> + constexpr void operator()(I& i, S bound) const + { + if constexpr (etl::is_assignable_v) + { + i = etl::move(bound); + } + else if constexpr (etl::is_same_v && etl::is_random_access_iterator_concept::value) + { + (*this)(i, bound - i); + } + else + { + while (!(i == bound)) + { + ++i; + } + } + } + + template::value || etl::is_output_iterator_concept::value>> + constexpr etl::iter_difference_t + operator()(I& i, etl::iter_difference_t n, S bound) const + { + if constexpr (etl::is_same_v && etl::is_random_access_iterator_concept::value) + { + const auto dist = bound - i; + + if ((n >= 0 && dist >= 0 && n >= dist) || + (n <= 0 && dist <= 0 && n <= dist)) + { + (*this)(i, bound); + return n - dist; + } + + (*this)(i, n); + return 0; + } + else + { + while (n > 0 && !(i == bound)) + { + --n; + ++i; + } + + if constexpr (etl::is_bidirectional_iterator_concept::value) + { + while (n < 0 && !(i == bound)) + { + ++n; + --i; + } + } + + return n; + } + } + }; + + inline constexpr auto advance = advance_fn(); + + struct prev_fn + { + template::value>> + constexpr I operator()(I i) const + { + --i; + return i; + } + + template::value>> + constexpr I operator()(I i, etl::iter_difference_t n) const + { + ranges::advance(i, -n); + return i; + } + + template::value>> + constexpr I operator()(I i, etl::iter_difference_t n, I bound) const + { + ranges::advance(i, -n, bound); + return i; + } + }; + + inline constexpr auto prev = prev_fn(); + + struct next_fn + { + template::value || etl::is_output_iterator_concept::value>> + constexpr I operator()(I i) const + { + ++i; + return i; + } + + template::value || etl::is_output_iterator_concept::value) && etl::is_integral>::value>> + constexpr I operator()(I i, etl::iter_difference_t n) const + { + ranges::advance(i, n); + return i; + } + + template::value || etl::is_output_iterator_concept::value) && !etl::is_integral::value>> + constexpr I operator()(I i, S bound) const + { + ranges::advance(i, bound); + return i; + } + + template::value || etl::is_output_iterator_concept::value) && !etl::is_integral::value>> + constexpr I operator()(I i, etl::iter_difference_t n, S bound) const + { + ranges::advance(i, n, bound); + return i; + } + }; + + inline constexpr auto next = next_fn(); + } + + struct unreachable_sentinel_t + { + }; + + inline constexpr unreachable_sentinel_t unreachable_sentinel{}; + + template + constexpr bool operator==(unreachable_sentinel_t, const I&) noexcept + { + return false; + } + + template + constexpr bool operator==(const I&, unreachable_sentinel_t) noexcept + { + return false; + } + + template + constexpr bool operator!=(unreachable_sentinel_t, const I& i) noexcept + { + return !(unreachable_sentinel_t{} == i); + } + + template + constexpr bool operator!=(const I& i, unreachable_sentinel_t) noexcept + { + return !(i == unreachable_sentinel_t{}); + } + + struct default_sentinel_t + { + }; + + inline constexpr default_sentinel_t default_sentinel{}; + + namespace private_iterator + { + template + struct has_arrow_operator : etl::false_type {}; + + template + struct has_arrow_operator().operator->())>> : etl::true_type {}; + + //*********************************** + /// Proxy that owns a copy of the dereferenced value so that operator-> + /// can safely return a pointer to it. Used when the wrapped iterator + /// has no member operator-> and is not a raw pointer (i.e. *it may + /// yield a prvalue / proxy whose address would otherwise dangle). + //*********************************** + template + struct arrow_proxy + { + TValue stored; + + constexpr arrow_proxy(TValue value) : stored(etl::move(value)) {} + constexpr const TValue* operator->() const noexcept { return etl::addressof(stored); } + }; + } + + template + class counted_iterator + { + template friend class counted_iterator; + + public: + using iterator_type = I; + using value_type = etl::iter_value_t; + using difference_type = etl::iter_difference_t; + using iterator_category = typename etl::iterator_traits::iterator_category; + using pointer = typename etl::iterator_traits::pointer; + using reference = typename etl::iterator_traits::reference; + + constexpr counted_iterator() = default; + + constexpr counted_iterator(I x, etl::iter_difference_t n): current(etl::move(x)), length(n) + { + } + + template + constexpr counted_iterator(const counted_iterator& other): current(other.current), length(other.length) + { + } + + template + constexpr counted_iterator& operator=(const counted_iterator& other) + { + current = other.current; + length = other.length; + return *this; + } + + constexpr const I& base() const& noexcept + { + return current; + } + + constexpr I base() && + { + return etl::move(current); + } + + constexpr etl::iter_difference_t count() const noexcept + { + return length; + } + + constexpr decltype(auto) operator*() const + { + return *current; + } + + // operator-> for iterator types that provide a member operator-> + template::value || etl::is_output_iterator_concept::value) && + private_iterator::has_arrow_operator::value, int> = 0> + constexpr auto operator->() const noexcept + { + return current.operator->(); + } + + // operator-> fallback for raw-pointer iterators (addressof is always safe) + template::value || etl::is_output_iterator_concept::value) && + !private_iterator::has_arrow_operator::value && + etl::is_pointer::value, int> = 0> + constexpr auto operator->() const noexcept + { + return current; + } + + // operator-> fallback for class-type iterators without member operator-> + // When *current yields an lvalue reference, just take its address. + template::value || etl::is_output_iterator_concept::value) && + !private_iterator::has_arrow_operator::value && + !etl::is_pointer::value && + etl::is_lvalue_reference())>::value, int> = 0> + constexpr auto operator->() const noexcept + { + return etl::addressof(*current); + } + + // operator-> fallback for class-type iterators without member operator-> + // When *current yields a prvalue / proxy, use an owning proxy so the + // address remains valid. + template::value || etl::is_output_iterator_concept::value) && + !private_iterator::has_arrow_operator::value && + !etl::is_pointer::value && + !etl::is_lvalue_reference())>::value, int> = 0> + constexpr auto operator->() const + { + return private_iterator::arrow_proxy{*current}; + } + + template::value, int> = 0> + constexpr decltype(auto) operator[](etl::iter_difference_t n) const + { + return current[n]; + } + + constexpr counted_iterator& operator++() + { + ++current; + --length; + return *this; + } + + constexpr counted_iterator operator++(int) + { + counted_iterator tmp = *this; + current++; + length--; + return tmp; + } + + template::value, int> = 0> + constexpr counted_iterator& operator+=(etl::iter_difference_t n) + { + current += n; + length -= n; + return *this; + } + + template::value, int> = 0> + constexpr counted_iterator operator+(etl::iter_difference_t n) const + { + counted_iterator result{*this}; + result += n; + return result; + } + + constexpr counted_iterator& operator--() + { + --current; + ++length; + return *this; + } + + constexpr counted_iterator operator--(int) + { + counted_iterator tmp = *this; + current--; + length++; + return tmp; + } + + template::value, int> = 0> + constexpr counted_iterator& operator-=(etl::iter_difference_t n) + { + current -= n; + length += n; + return *this; + } + + template::value, int> = 0> + constexpr counted_iterator operator-(etl::iter_difference_t n) const + { + counted_iterator result{*this}; + result -= n; + return result; + } + + friend constexpr bool operator==(const counted_iterator& x, const counted_iterator& y) + { + return x.length == y.length; + } + + friend constexpr bool operator==(const counted_iterator& x, etl::default_sentinel_t) + { + return x.count() == 0; + } + + friend constexpr bool operator==(etl::default_sentinel_t, const counted_iterator& x) + { + return x.count() == 0; + } + + friend constexpr bool operator!=(const counted_iterator& x, etl::default_sentinel_t) + { + return x.count() != 0; + } + + friend constexpr bool operator!=(etl::default_sentinel_t, const counted_iterator& y) + { + return y.count() != 0; + } + + template::value, int> = 0> + friend constexpr counted_iterator operator+(etl::iter_difference_t n, const counted_iterator& x) + { + return counted_iterator(x.current + n, x.length - n); + } + + friend constexpr etl::iter_difference_t operator-(const counted_iterator& x, const counted_iterator& y) + { + return y.length - x.length; + } + + friend constexpr etl::iter_difference_t operator-(const counted_iterator& x, etl::default_sentinel_t) + { + return -x.length; + } + + friend constexpr etl::iter_difference_t operator-(etl::default_sentinel_t, const counted_iterator& y) + { + return y.length; + } + + private: + I current{}; + difference_type length{}; + }; + + template, TIterator>::value>> + constexpr typename etl::iterator_traits::difference_type distance(TIterator first, etl::default_sentinel_t) + { + return first.count(); + } +#endif + +#if ETL_USING_CPP14 + template + struct is_range: etl::false_type + { + }; + + template + struct is_range())), + decltype(ETL_OR_STD::end(etl::declval()))>>: etl::true_type + { + }; + +#if ETL_USING_CPP17 + template + inline constexpr bool is_range_v = is_range::value; +#endif +#endif + #if ETL_NOT_USING_STL || ETL_CPP17_NOT_SUPPORTED //************************************************************************** /// Returns a pointer to the block of memory containing the elements of the range. @@ -1256,4 +1985,3 @@ namespace etl } #endif - diff --git a/include/etl/memory.h b/include/etl/memory.h index 1c282452..dcef8246 100644 --- a/include/etl/memory.h +++ b/include/etl/memory.h @@ -322,6 +322,190 @@ namespace etl } #endif +#if ETL_USING_CPP17 +namespace ranges { + //***************************************************************************** + /// Copies a range of objects to uninitialised memory. + /// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_copy + ///\ingroup memory + //***************************************************************************** + struct uninitialized_copy_fn + { + template>> + ranges::uninitialized_copy_result + operator()(I ifirst, S1 ilast, O ofirst, S2 olast) const + { + using value_type = typename etl::iterator_traits::value_type; + + O ofirst_original = ofirst; + +#if ETL_USING_EXCEPTIONS + try + { +#endif + for (; ifirst != ilast && ofirst != olast; ++ifirst, ++ofirst) + { + ::new (static_cast(etl::to_address(ofirst))) + value_type(*ifirst); + } + + return {etl::move(ifirst), etl::move(ofirst)}; +#if ETL_USING_EXCEPTIONS + } + catch (...) + { + for (; ofirst_original != ofirst; ++ofirst_original) + { + etl::to_address(ofirst_original)->~value_type(); + } + throw; + } +#endif + } + + template>> + ranges::uninitialized_copy_result, ranges::borrowed_iterator_t> + operator()(IR&& in_range, OR&& out_range) const + { + return (*this)(ranges::begin(in_range), ranges::end(in_range), + ranges::begin(out_range), ranges::end(out_range)); + } + }; + + inline constexpr uninitialized_copy_fn uninitialized_copy {}; + + //***************************************************************************** + /// Copies N objects to uninitialised memory. + /// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_copy_n + ///\ingroup memory + //***************************************************************************** + struct uninitialized_copy_n_fn + { + template>> + ranges::uninitialized_copy_n_result + operator()(I ifirst, etl::iter_difference_t n, O ofirst, S olast) const + { + using value_type = typename etl::iterator_traits::value_type; + + O ofirst_original = ofirst; + +#if ETL_USING_EXCEPTIONS + try + { +#endif + for (; n > 0 && ofirst != olast; ++ifirst, ++ofirst, --n) + { + ::new (static_cast(etl::to_address(ofirst))) + value_type(*ifirst); + } + + return {etl::move(ifirst), etl::move(ofirst)}; +#if ETL_USING_EXCEPTIONS + } + catch (...) + { + for (; ofirst_original != ofirst; ++ofirst_original) + { + etl::to_address(ofirst_original)->~value_type(); + } + throw; + } +#endif + } + }; + + inline constexpr uninitialized_copy_n_fn uninitialized_copy_n {}; + + //***************************************************************************** + /// Fills uninitialised memory range with a value. + /// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_fill + ///\ingroup memory + //***************************************************************************** + struct uninitialized_fill_fn + { + template>> + I operator()(I first, S last, const T& value) const + { + using value_type = typename etl::iterator_traits::value_type; + + I current = first; + +#if ETL_USING_EXCEPTIONS + try + { +#endif + for (; current != last; ++current) + { + ::new (static_cast(etl::to_address(current))) + value_type(value); + } + + return current; +#if ETL_USING_EXCEPTIONS + } + catch (...) + { + for (; first != current; ++first) + { + etl::to_address(first)->~value_type(); + } + throw; + } +#endif + } + + template>> + ranges::borrowed_iterator_t operator()(R&& r, const T& value) const + { + return (*this)(ranges::begin(r), ranges::end(r), value); + } + }; + + inline constexpr uninitialized_fill_fn uninitialized_fill {}; + + //***************************************************************************** + /// Fills uninitialised memory with N copies of a value. + /// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_fill_n + ///\ingroup memory + //***************************************************************************** + struct uninitialized_fill_n_fn + { + template + I operator()(I first, etl::iter_difference_t n, const T& value) const + { + using value_type = typename etl::iterator_traits::value_type; + + I current = first; + +#if ETL_USING_EXCEPTIONS + try + { +#endif + for (; n > 0; ++current, --n) + { + ::new (static_cast(etl::to_address(current))) + value_type(value); + } + + return current; +#if ETL_USING_EXCEPTIONS + } + catch (...) + { + for (; first != current; ++first) + { + etl::to_address(first)->~value_type(); + } + throw; + } +#endif + } + }; + + inline constexpr uninitialized_fill_n_fn uninitialized_fill_n {}; +} +#endif + #if ETL_USING_STL && ETL_USING_CPP11 //***************************************************************************** /// Copies N objects to uninitialised memory. @@ -638,6 +822,102 @@ namespace etl } #endif +#if ETL_USING_CPP17 +namespace ranges { + //***************************************************************************** + /// Moves a range of objects to uninitialised memory. + /// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_move + ///\ingroup memory + //***************************************************************************** + struct uninitialized_move_fn + { + template>> + ranges::uninitialized_move_result + operator()(I ifirst, S1 ilast, O ofirst, S2 olast) const + { + using value_type = typename etl::iterator_traits::value_type; + + O ofirst_original = ofirst; + +#if ETL_USING_EXCEPTIONS + try + { +#endif + for (; ifirst != ilast && ofirst != olast; ++ifirst, ++ofirst) + { + ::new (static_cast(etl::to_address(ofirst))) + value_type(etl::move(*ifirst)); + } + + return {etl::move(ifirst), etl::move(ofirst)}; +#if ETL_USING_EXCEPTIONS + } + catch (...) + { + for (; ofirst_original != ofirst; ++ofirst_original) + { + etl::to_address(ofirst_original)->~value_type(); + } + throw; + } +#endif + } + + template>> + ranges::uninitialized_move_result, ranges::borrowed_iterator_t> + operator()(IR&& in_range, OR&& out_range) const + { + return (*this)(ranges::begin(in_range), ranges::end(in_range), + ranges::begin(out_range), ranges::end(out_range)); + } + }; + + inline constexpr uninitialized_move_fn uninitialized_move {}; + + //***************************************************************************** + /// Moves N objects to uninitialised memory. + /// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_move_n + ///\ingroup memory + //***************************************************************************** + struct uninitialized_move_n_fn + { + template>> + ranges::uninitialized_move_n_result + operator()(I ifirst, etl::iter_difference_t n, O ofirst, S olast) const + { + using value_type = typename etl::iterator_traits::value_type; + + O ofirst_original = ofirst; + +#if ETL_USING_EXCEPTIONS + try + { +#endif + for (; n > 0 && ofirst != olast; ++ifirst, ++ofirst, --n) + { + ::new (static_cast(etl::to_address(ofirst))) + value_type(etl::move(*ifirst)); + } + + return {etl::move(ifirst), etl::move(ofirst)}; +#if ETL_USING_EXCEPTIONS + } + catch (...) + { + for (; ofirst_original != ofirst; ++ofirst_original) + { + etl::to_address(ofirst_original)->~value_type(); + } + throw; + } +#endif + } + }; + + inline constexpr uninitialized_move_n_fn uninitialized_move_n {}; +} +#endif + #if ETL_USING_STL && ETL_USING_CPP17 //***************************************************************************** /// Default initialises a range of objects to uninitialised memory. @@ -818,6 +1098,98 @@ namespace etl } #endif +#if ETL_USING_CPP17 +namespace ranges { + //***************************************************************************** + /// Default constructs objects in uninitialised memory range. + /// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_default_construct + ///\ingroup memory + //***************************************************************************** + struct uninitialized_default_construct_fn + { + template>> + I operator()(I first, S last) const + { + using value_type = typename etl::iterator_traits::value_type; + + I current = first; + +#if ETL_USING_EXCEPTIONS + try + { +#endif + for (; current != last; ++current) + { + ::new (static_cast(etl::to_address(current))) + value_type; + } + + return current; +#if ETL_USING_EXCEPTIONS + } + catch (...) + { + for (; first != current; ++first) + { + etl::to_address(first)->~value_type(); + } + throw; + } +#endif + } + + template>> + ranges::borrowed_iterator_t operator()(R&& r) const + { + return (*this)(ranges::begin(r), ranges::end(r)); + } + }; + + inline constexpr uninitialized_default_construct_fn uninitialized_default_construct {}; + + //***************************************************************************** + /// Default constructs N objects in uninitialised memory. + /// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_default_construct_n + ///\ingroup memory + //***************************************************************************** + struct uninitialized_default_construct_n_fn + { + template + I operator()(I first, etl::iter_difference_t n) const + { + using value_type = typename etl::iterator_traits::value_type; + + I current = first; + +#if ETL_USING_EXCEPTIONS + try + { +#endif + for (; n > 0; ++current, --n) + { + ::new (static_cast(etl::to_address(current))) + value_type; + } + + return current; +#if ETL_USING_EXCEPTIONS + } + catch (...) + { + for (; first != current; ++first) + { + etl::to_address(first)->~value_type(); + } + throw; + } +#endif + } + }; + + inline constexpr uninitialized_default_construct_n_fn uninitialized_default_construct_n {}; +} +#endif + #if ETL_USING_STL && ETL_USING_CPP17 //***************************************************************************** /// Default initialises a range of objects to uninitialised memory. @@ -951,6 +1323,98 @@ namespace etl } #endif +#if ETL_USING_CPP17 +namespace ranges { + //***************************************************************************** + /// Value constructs objects in uninitialised memory range. + /// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_value_construct + ///\ingroup memory + //***************************************************************************** + struct uninitialized_value_construct_fn + { + template>> + I operator()(I first, S last) const + { + using value_type = typename etl::iterator_traits::value_type; + + I current = first; + +#if ETL_USING_EXCEPTIONS + try + { +#endif + for (; current != last; ++current) + { + ::new (static_cast(etl::to_address(current))) + value_type(); + } + + return current; +#if ETL_USING_EXCEPTIONS + } + catch (...) + { + for (; first != current; ++first) + { + etl::to_address(first)->~value_type(); + } + throw; + } +#endif + } + + template>> + ranges::borrowed_iterator_t operator()(R&& r) const + { + return (*this)(ranges::begin(r), ranges::end(r)); + } + }; + + inline constexpr uninitialized_value_construct_fn uninitialized_value_construct {}; + + //***************************************************************************** + /// Value constructs N objects in uninitialised memory. + /// https://en.cppreference.com/w/cpp/memory/ranges/uninitialized_value_construct_n + ///\ingroup memory + //***************************************************************************** + struct uninitialized_value_construct_n_fn + { + template + I operator()(I first, etl::iter_difference_t n) const + { + using value_type = typename etl::iterator_traits::value_type; + + I current = first; + +#if ETL_USING_EXCEPTIONS + try + { +#endif + for (; n > 0; ++current, --n) + { + ::new (static_cast(etl::to_address(current))) + value_type(); + } + + return current; +#if ETL_USING_EXCEPTIONS + } + catch (...) + { + for (; first != current; ++first) + { + etl::to_address(first)->~value_type(); + } + throw; + } +#endif + } + }; + + inline constexpr uninitialized_value_construct_n_fn uninitialized_value_construct_n {}; +} +#endif + #if ETL_USING_STL && ETL_USING_CPP20 //***************************************************************************** /// Constructs an item at address p with value constructed from 'args'. @@ -996,6 +1460,26 @@ namespace etl } #endif +#if ETL_USING_CPP17 +namespace ranges { + //***************************************************************************** + /// Constructs an item at address p with value constructed from 'args'. + /// https://en.cppreference.com/w/cpp/memory/ranges/construct_at + ///\ingroup memory + //***************************************************************************** + struct construct_at_fn + { + template + constexpr T* operator()(T* p, Args&&... args) const + { + return etl::construct_at(p, etl::forward(args)...); + } + }; + + inline constexpr construct_at_fn construct_at {}; +} +#endif + #if ETL_USING_STL && ETL_USING_CPP20 //***************************************************************************** /// Destroys an item at address p. @@ -1254,6 +1738,74 @@ namespace etl } #endif +#if ETL_USING_CPP17 +namespace ranges { + //***************************************************************************** + /// Destroys an item at address p. + /// https://en.cppreference.com/w/cpp/memory/ranges/destroy_at + ///\ingroup memory + //***************************************************************************** + struct destroy_at_fn + { + template + constexpr void operator()(T* p) const + { + etl::destroy_at(p); + } + }; + + inline constexpr destroy_at_fn destroy_at {}; + + //***************************************************************************** + /// Destroys a range of items. + /// https://en.cppreference.com/w/cpp/memory/ranges/destroy + ///\ingroup memory + //***************************************************************************** + struct destroy_fn + { + template>> + I operator()(I first, S last) const + { + for (; first != last; ++first) + { + etl::destroy_at(etl::to_address(first)); + } + + return first; + } + + template>> + ranges::borrowed_iterator_t operator()(R&& r) const + { + return (*this)(ranges::begin(r), ranges::end(r)); + } + }; + + inline constexpr destroy_fn destroy {}; + + //***************************************************************************** + /// Destroys a number of items. + /// https://en.cppreference.com/w/cpp/memory/ranges/destroy_n + ///\ingroup memory + //***************************************************************************** + struct destroy_n_fn + { + template + I operator()(I first, etl::iter_difference_t n) const + { + for (; n > 0; ++first, --n) + { + etl::destroy_at(etl::to_address(first)); + } + + return first; + } + }; + + inline constexpr destroy_n_fn destroy_n {}; +} +#endif + //***************************************************************************** /// Default deleter. ///\tparam T The pointed to type type. diff --git a/include/etl/private/ranges_mini_variant.h b/include/etl/private/ranges_mini_variant.h new file mode 100644 index 00000000..734ab7fc --- /dev/null +++ b/include/etl/private/ranges_mini_variant.h @@ -0,0 +1,412 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 BMW AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_RANGES_MINI_VARIANT_INCLUDED +#define ETL_RANGES_MINI_VARIANT_INCLUDED + +#include "../platform.h" +#include "../error_handler.h" +#include "../utility.h" + +#if ETL_USING_CPP17 + +namespace etl +{ + namespace ranges + { + namespace private_ranges + { + //********************************************************************* + /// mini_variant + /// A minimal, self-contained variant used internally by ranges, + /// to prevent cyclic dependencies from algorithm.h and ranges.h. + //********************************************************************* + + // Helper: get the I-th type from a parameter pack. + template + struct type_at_index; + + template + struct type_at_index : type_at_index {}; + + template + struct type_at_index<0, Head, Tail...> + { + using type = Head; + }; + + template + using type_at_index_t = typename type_at_index::type; + + // Helper: maximum of sizeof... values + template + struct max_size; + + template + struct max_size + { + static constexpr size_t value = sizeof(T); + }; + + template + struct max_size + { + static constexpr size_t value = (sizeof(T) > max_size::value) ? sizeof(T) : max_size::value; + }; + + // Helper: maximum of alignof... values + template + struct max_align; + + template + struct max_align + { + static constexpr size_t value = alignof(T); + }; + + template + struct max_align + { + static constexpr size_t value = (alignof(T) > max_align::value) ? alignof(T) : max_align::value; + }; + + // Index value representing "no active alternative" + inline constexpr size_t mini_variant_npos = ~size_t(0); + + // Detection trait: is a single type equality-comparable? + template + struct is_equality_comparable : etl::false_type {}; + + template + struct is_equality_comparable() == etl::declval())>> + : etl::true_type {}; + + // Conjunction: all types in the pack are equality-comparable + template + struct all_equality_comparable : etl::bool_constant<(is_equality_comparable::value && ...)> {}; + + // Detection trait: is a single type nothrow-move-constructible? + template + struct is_nothrow_move_constructible + { + private: + template + static auto test(int) -> etl::bool_constant()))>; + + template + static etl::false_type test(...); + + public: + static constexpr bool value = decltype(test(0))::value; + }; + + // Conjunction: all types in the pack are nothrow-move-constructible + template + struct all_nothrow_move_constructible : etl::bool_constant<(is_nothrow_move_constructible::value && ...)> {}; + + // Detection trait: is a single type nothrow-destructible? + template + struct is_nothrow_destructible + { + private: + template + static auto test(int) -> etl::bool_constant().~U())>; + + template + static etl::false_type test(...); + + public: + static constexpr bool value = decltype(test(0))::value; + }; + + // Conjunction: all types in the pack are nothrow-destructible + template + struct all_nothrow_destructible : etl::bool_constant<(is_nothrow_destructible::value && ...)> {}; + + template + class mini_variant + { + static_assert(sizeof...(Ts) > 0, "mini_variant requires at least one type"); + + static constexpr size_t storage_size = max_size::value; + static constexpr size_t storage_align = max_align::value; + + alignas(storage_align) unsigned char _storage[storage_size]; + size_t _index; + + // ---- Destruction dispatch table ---- + using destroy_fn = void(*)(void*); + + template + static void destroy_impl(void* ptr) + { + using T = type_at_index_t; + static_cast(ptr)->~T(); + } + + template + static const destroy_fn* make_destroy_table(etl::index_sequence) + { + static const destroy_fn table[] = { &destroy_impl... }; + return table; + } + + static const destroy_fn* destroy_table() + { + static const destroy_fn* t = make_destroy_table(etl::make_index_sequence{}); + return t; + } + + // ---- Copy dispatch table ---- + using copy_fn = void(*)(void* /*dst*/, const void* /*src*/); + + template + static void copy_impl(void* dst, const void* src) + { + using T = type_at_index_t; + ::new (dst) T(*static_cast(src)); + } + + template + static const copy_fn* make_copy_table(etl::index_sequence) + { + static const copy_fn table[] = { ©_impl... }; + return table; + } + + static const copy_fn* copy_table() + { + static const copy_fn* t = make_copy_table(etl::make_index_sequence{}); + return t; + } + + // ---- Move dispatch table ---- + using move_fn = void(*)(void* /*dst*/, void* /*src*/); + + template + static void move_impl(void* dst, void* src) + { + using T = type_at_index_t; + ::new (dst) T(etl::move(*static_cast(src))); + } + + template + static const move_fn* make_move_table(etl::index_sequence) + { + static const move_fn table[] = { &move_impl... }; + return table; + } + + static const move_fn* move_table() + { + static const move_fn* t = make_move_table(etl::make_index_sequence{}); + return t; + } + + // ---- Equality dispatch table ---- + using equal_fn = bool(*)(const void* /*lhs*/, const void* /*rhs*/); + + template + static bool equal_impl(const void* lhs, const void* rhs) + { + using T = type_at_index_t; + return *static_cast(lhs) == *static_cast(rhs); + } + + template + static const equal_fn* make_equal_table(etl::index_sequence) + { + static const equal_fn table[] = { &equal_impl... }; + return table; + } + + static const equal_fn* equal_table() + { + static const equal_fn* t = make_equal_table(etl::make_index_sequence{}); + return t; + } + + void destroy_current() + { + if (_index != mini_variant_npos) + { + destroy_table()[_index](&_storage); + _index = mini_variant_npos; + } + } + + public: + mini_variant() : _index{mini_variant_npos} + { + } + + mini_variant(const mini_variant& other) : _index{mini_variant_npos} + { + if (other._index != mini_variant_npos) + { + copy_table()[other._index](&_storage, &other._storage); + _index = other._index; + } + } + + mini_variant& operator=(const mini_variant& other) + { + if (this != &other) + { + destroy_current(); + if (other._index != mini_variant_npos) + { + copy_table()[other._index](&_storage, &other._storage); + _index = other._index; + } + } + return *this; + } + + mini_variant(mini_variant&& other) noexcept(all_nothrow_move_constructible::value && all_nothrow_destructible::value) + : _index{mini_variant_npos} + { + if (other._index != mini_variant_npos) + { + move_table()[other._index](&_storage, &other._storage); + _index = other._index; + other.destroy_current(); + } + } + + mini_variant& operator=(mini_variant&& other) noexcept(all_nothrow_move_constructible::value && all_nothrow_destructible::value) + { + if (this != &other) + { + destroy_current(); + if (other._index != mini_variant_npos) + { + move_table()[other._index](&_storage, &other._storage); + _index = other._index; + other.destroy_current(); + } + } + return *this; + } + + ~mini_variant() noexcept(all_nothrow_destructible::value) + { + destroy_current(); + } + + template + void emplace(Args&&... args) + { + static_assert(I < sizeof...(Ts), "Index out of range"); + using T = type_at_index_t; + destroy_current(); + ::new (&_storage) T(etl::forward(args)...); + _index = I; + } + + constexpr size_t index() const + { + return _index; + } + + template + type_at_index_t& get_ref() + { + static_assert(I < sizeof...(Ts), "Index out of range"); + ETL_ASSERT(_index == I, ETL_ERROR_GENERIC("mini_variant: bad index")); + using T = type_at_index_t; + return *reinterpret_cast(&_storage); + } + + template + const type_at_index_t& get_ref() const + { + static_assert(I < sizeof...(Ts), "Index out of range"); + ETL_ASSERT(_index == I, ETL_ERROR_GENERIC("mini_variant: bad index")); + using T = type_at_index_t; + return *reinterpret_cast(&_storage); + } + + template::value, etl::enable_if_t = 0> + friend bool operator==(const mini_variant& lhs, const mini_variant& rhs) + { + if (lhs._index != rhs._index) + { + return false; + } + if (lhs._index == mini_variant_npos) + { + return true; + } + return equal_table()[lhs._index](&lhs._storage, &rhs._storage); + } + + template::value, etl::enable_if_t = 0> + friend bool operator!=(const mini_variant& lhs, const mini_variant& rhs) + { + return !(lhs == rhs); + } + }; + } // namespace private_ranges + + } // namespace ranges (temporarily close to define get<> in etl namespace) + + template + typename ranges::private_ranges::type_at_index_t& + get(ranges::private_ranges::mini_variant& v) + { + return v.template get_ref(); + } + + template + const typename ranges::private_ranges::type_at_index_t& + get(const ranges::private_ranges::mini_variant& v) + { + return v.template get_ref(); + } + + template + typename ranges::private_ranges::type_at_index_t&& + get(ranges::private_ranges::mini_variant&& v) + { + return etl::move(v.template get_ref()); + } + + template + const typename ranges::private_ranges::type_at_index_t&& + get(const ranges::private_ranges::mini_variant&& v) + { + return etl::move(v.template get_ref()); + } + +} // namespace etl + +#endif // ETL_USING_CPP17 +#endif diff --git a/include/etl/private/variant_legacy.h b/include/etl/private/variant_legacy.h index e6c01156..1e9bb5c2 100644 --- a/include/etl/private/variant_legacy.h +++ b/include/etl/private/variant_legacy.h @@ -30,7 +30,6 @@ SOFTWARE. #include "../platform.h" #include "../utility.h" -#include "../array.h" #include "../largest.h" #include "../exception.h" #include "../type_traits.h" @@ -39,6 +38,7 @@ SOFTWARE. #include "../alignment.h" #include "../error_handler.h" #include "../null_type.h" +#include "../parameter_type.h" #include "../placement_new.h" #include "../monostate.h" diff --git a/include/etl/ranges.h b/include/etl/ranges.h new file mode 100644 index 00000000..9e47d984 --- /dev/null +++ b/include/etl/ranges.h @@ -0,0 +1,5927 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 BMW AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_RANGES_INCLUDED +#define ETL_RANGES_INCLUDED + +#include "platform.h" + +#include "iterator.h" +#include "limits.h" +#include "tuple.h" +#include "type_traits.h" +#include "invoke.h" +#include "error_handler.h" +#include "private/ranges_mini_variant.h" + +#if ETL_USING_CPP17 + +namespace etl +{ + namespace ranges + { + //************************************************************************* + /// Range adaptors. + //************************************************************************* + + namespace private_ranges + { + template + struct iterator_trait; + + template + struct iterator_trait>> + { + using iterator = typename etl::conditional_t, typename T::const_iterator, typename T::iterator>; + using const_iterator = typename T::const_iterator; + + using value_type = typename etl::iterator_traits::value_type; + using difference_type = typename etl::iterator_traits::difference_type; + using pointer = typename etl::iterator_traits::pointer; + using reference = typename etl::iterator_traits::reference; + }; + + template + struct iterator_trait && !etl::is_array_v>>> + { + using iterator = typename etl::conditional_t>, typename etl::remove_reference::type::const_iterator, typename etl::remove_reference::type::iterator>; + using const_iterator = typename etl::remove_reference::type::const_iterator; + + using value_type = typename etl::iterator_traits::value_type; + using difference_type = typename etl::iterator_traits::difference_type; + using pointer = typename etl::iterator_traits::pointer; + using reference = typename etl::iterator_traits::reference; + }; + + template + struct iterator_trait>>> + { + using value_type = typename etl::remove_all_extents>::type; + using iterator = value_type*; + using const_iterator = const value_type*; + using difference_type = ptrdiff_t; + using pointer = const value_type*; + using reference = const value_type&; + }; + } + + template + class view_interface + { + public: + view_interface() = default; + + constexpr bool empty() const + { + return cbegin() == cend(); + } + + auto cbegin() const + { + return static_cast(this)->begin(); + } + + auto cend() const + { + return static_cast(this)->end(); + } + + operator bool() const + { + return !empty(); + } + + size_t size() const + { + return etl::distance(cbegin(), cend()); + } + + constexpr decltype(auto) front() + { + return *(static_cast(this)->begin()); + } + + constexpr decltype(auto) front() const + { + return *cbegin(); + } + + template().begin())>::value, int> = 0> + constexpr decltype(auto) back() + { + return *(static_cast(this)->end() - 1); + } + + template().begin())>::value, int> = 0> + constexpr decltype(auto) back() const + { + return *etl::prev(cend()); + } + + constexpr decltype(auto) operator[](size_t i) + { + auto it{static_cast(this)->begin()}; + etl::advance(it, i); + return *it; + } + + template().begin())>::value, int> = 0> + constexpr decltype(auto) operator[](size_t i) + { + return static_cast(this)->begin()[i]; + } + + template().begin())>::value, int> = 0> + constexpr decltype(auto) operator[](size_t i) const + { + return cbegin()[i]; + } + }; + + template + class range_iterator + { + public: + auto get() const + { + return **(static_cast(this)); + } + }; + + template + class subrange: public etl::ranges::view_interface> + { + public: + subrange(I i, S s): _begin{i}, _end{s} + { + } + + constexpr I begin() const + { + return _begin; + } + + constexpr S end() const + { + return _end; + } + + constexpr subrange& advance(etl::iter_difference_t n) + { + etl::advance(_begin, n); + return *this; + } + + constexpr subrange prev(etl::iter_difference_t n = 1) + { + auto result = subrange{_begin, _end}; + result.advance(-n); + return result; + } + + constexpr subrange next(etl::iter_difference_t n = 1) + { + auto result = subrange{_begin, _end}; + result.advance(n); + return result; + } + + private: + I _begin; + S _end; + }; + + template + subrange(I, S) -> subrange; + + template + class empty_view: public etl::ranges::view_interface> + { + public: + using iterator = T*; + + constexpr empty_view() = default; + + static constexpr iterator begin() noexcept + { + return nullptr; + } + + static constexpr iterator end() noexcept + { + return nullptr; + } + + static constexpr T* data() noexcept + { + return nullptr; + } + + static constexpr size_t size() noexcept + { + return 0; + } + + static constexpr bool empty() noexcept + { + return true; + } + }; + + struct dangling + { + constexpr dangling() noexcept = default; + + template + constexpr dangling(Args&&...) noexcept + { + } + }; + + template + constexpr bool enable_borrowed_range = false; + + template + struct is_borrowed_range + { + static constexpr bool value = etl::is_range_v || etl::ranges::enable_borrowed_range; + }; + + template + inline constexpr bool is_borrowed_range_v = is_borrowed_range::value; + + template + using borrowed_iterator_t = etl::conditional_t, + etl::ranges::iterator_t, etl::ranges::dangling>; + + template + using borrowed_subrange_t = etl::conditional_t, + etl::ranges::subrange>, etl::ranges::dangling>; + + namespace views + { + template + constexpr empty_view empty{}; + } + + template + class single_view: public etl::ranges::view_interface> + { + public: + using value_type = T; + using iterator = value_type*; + using const_iterator = const value_type*; + + constexpr single_view(const T& t) noexcept: _value(t) + { + } + + constexpr single_view(T&& t) noexcept: _value(etl::move(t)) + { + } + + constexpr single_view(const single_view& other): _value(other._value) {} + + constexpr single_view(single_view&& other): _value(etl::move(other._value)) {} + + constexpr single_view& operator=(const single_view& other) + { + _value = other._value; + return *this; + } + + constexpr single_view& operator=(single_view&& other) + { + _value = etl::move(other._value); + return *this; + } + + constexpr iterator begin() noexcept + { + return data(); + } + + constexpr const_iterator begin() const noexcept + { + return data(); + } + + constexpr iterator end() noexcept + { + return data() + 1; + } + + constexpr const_iterator end() const noexcept + { + return data() + 1; + } + + constexpr const T* data() const noexcept + { + return &_value; + } + + constexpr T* data() noexcept + { + return &_value; + } + + constexpr size_t size() const noexcept + { + return 1; + } + + constexpr bool empty() const noexcept + { + return false; + } + + private: + value_type _value; + }; + + template + single_view(T) -> single_view; + + namespace views + { + namespace private_views + { + struct single + { + template + constexpr auto operator()(T&& t) const + { + return etl::ranges::single_view(t); + } + }; + } + + inline constexpr private_views::single single; + } + + template + struct iota_iterator: public range_iterator> + { + public: + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; + + using iterator_category = ETL_OR_STD::random_access_iterator_tag; + + constexpr explicit iota_iterator(T i): _i{i} + { + } + + constexpr iota_iterator(const iota_iterator& other): _i{other._i} + { + } + + iota_iterator& operator++() + { + ++_i; + return *this; + } + + iota_iterator operator++(int) + { + iota_iterator tmp = *this; + _i++; + return tmp; + } + + iota_iterator& operator--() + { + --_i; + return *this; + } + + iota_iterator operator--(int) + { + iota_iterator tmp = *this; + _i--; + return tmp; + } + + iota_iterator& operator+=(difference_type n) + { + _i += n; + return *this; + } + + iota_iterator operator+(difference_type n) const + { + return iota_iterator{static_cast(_i + n)}; + } + + iota_iterator operator-(difference_type n) const + { + return iota_iterator{static_cast(_i - n)}; + } + + difference_type operator-(iota_iterator other) const + { + return _i - other._i; + } + + iota_iterator& operator=(const iota_iterator& other) + { + _i = other._i; + return *this; + } + + constexpr bool operator==(const iota_iterator& other) const + { + return _i == other._i; + } + + constexpr bool operator!=(const iota_iterator& other) const + { + return _i != other._i; + } + + constexpr value_type operator*() const + { + return _i; + } + + constexpr value_type operator*() + { + return _i; + } + + private: + value_type _i; + }; + + template + class iota_view: public etl::ranges::view_interface> + { + public: + using iterator = iota_iterator; + using const_iterator = iota_iterator; + + iota_view() = default; + + constexpr explicit iota_view(T value, T bound = etl::numeric_limits::max()): _value(value), _bound(bound) + { + } + + constexpr iterator begin() const noexcept + { + return iterator(_value); + } + + constexpr iterator end() const noexcept + { + return iterator(_bound); + } + + constexpr size_t size() const noexcept + { + if (_bound == etl::numeric_limits::max()) + { + return etl::numeric_limits::max(); + } + return _bound - _value; + } + + constexpr bool empty() const noexcept + { + return _value == _bound; + } + + private: + T _value; + T _bound; + }; + + template + iota_view(T) -> iota_view; + + namespace views + { + namespace private_views + { + struct iota + { + template + constexpr auto operator()(T&& t, B&& b) const + { + return etl::ranges::iota_view(t, b); + } + }; + } + + inline constexpr private_views::iota iota; + } + + template + struct repeat_iterator: public range_iterator> + { + public: + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; + + using iterator_category = ETL_OR_STD::random_access_iterator_tag; + + constexpr explicit repeat_iterator(T value, B i = etl::numeric_limits::max()): _value{value}, _i{i} + { + } + + constexpr repeat_iterator(const repeat_iterator& other) = default; + + repeat_iterator& operator++() + { + --_i; + return *this; + } + + repeat_iterator operator++(int) + { + repeat_iterator tmp(*this); + _i--; + return tmp; + } + + repeat_iterator& operator--() + { + ++_i; + return *this; + } + + repeat_iterator operator--(int) + { + repeat_iterator tmp(*this); + _i++; + return tmp; + } + + repeat_iterator& operator+=(size_t n) + { + _i -= n; + return *this; + } + + repeat_iterator operator+(size_t n) const + { + return repeat_iterator{_value, static_cast(_i - n)}; + } + + repeat_iterator operator-(size_t n) const + { + return repeat_iterator{_value, static_cast(_i + n)}; + } + + difference_type operator-(repeat_iterator other) const + { + return other._i - _i; + } + + repeat_iterator& operator=(const repeat_iterator& other) + { + _i = other._i; + _value = other._value; + return *this; + } + + constexpr bool operator==(const repeat_iterator& other) const + { + return _i == other._i; + } + + constexpr bool operator!=(const repeat_iterator& other) const + { + return _i != other._i; + } + + constexpr value_type operator*() const + { + return _value; + } + + constexpr value_type operator*() + { + return _value; + } + + private: + value_type _value; + B _i; + }; + + template + class repeat_view: public etl::ranges::view_interface> + { + public: + using iterator = repeat_iterator; + using const_iterator = repeat_iterator; + + repeat_view() = default; + + constexpr explicit repeat_view(T value, B bound = etl::numeric_limits::max()): _value(value), _bound(bound) + { + } + + constexpr iterator begin() const noexcept + { + return iterator(_value, _bound); + } + + constexpr iterator end() const noexcept + { + return iterator(_value, 0); + } + + constexpr size_t size() const noexcept + { + return _bound; + } + + constexpr bool empty() const noexcept + { + return _bound == 0; + } + + private: + T _value; + B _bound; + }; + + template + repeat_view(T, B = B()) -> repeat_view; + + namespace views + { + namespace private_views + { + struct repeat + { + template + constexpr auto operator()(T&& t, B&& b) const + { + return etl::ranges::repeat_view(t, b); + } + }; + } + + inline constexpr private_views::repeat repeat; + } + + template + class range_adapter_closure + { + }; + + template + class ref_view: public etl::ranges::view_interface> + { + public: + using iterator = typename etl::ranges::private_ranges::iterator_trait::iterator; + using const_iterator = typename etl::ranges::private_ranges::iterator_trait::const_iterator; + using pointer = typename etl::ranges::private_ranges::iterator_trait::pointer; + + ref_view(Range& r): _r{&r} + { + } + + constexpr Range& base() const + { + return *_r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(*_r)); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(*_r)); + } + + constexpr bool empty() const + { + return begin() == end(); + } + + constexpr size_t size() const + { + return etl::distance(begin(), end()); + } + + constexpr pointer data() const + { + return &(*begin()); + } + + private: + Range* _r; + }; + + template + ref_view(Range&) -> ref_view; + + struct ref_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = ref_view; + + ref_range_adapter_closure() = default; + + template + ref_view operator()(Range& r) + { + return ref_view(r); + } + }; + + namespace views + { + namespace private_views + { + struct ref + { + template + constexpr auto operator()(Range& r) const + { + return ranges::ref_view(r); + } + + constexpr auto operator()() const + { + return ranges::ref_range_adapter_closure(); + } + }; + } + + inline constexpr private_views::ref ref; + } + + template + class owning_view: public etl::ranges::view_interface> + { + public: + using iterator = typename etl::ranges::private_ranges::iterator_trait::iterator; + using const_iterator = typename etl::ranges::private_ranges::iterator_trait::const_iterator; + using pointer = typename etl::ranges::private_ranges::iterator_trait::pointer; + + owning_view() = default; + + owning_view(owning_view&& other) = default; + + constexpr owning_view(Range&& r) : _r(etl::move(r)) + { + } + + owning_view& operator=(const owning_view&) = delete; + + owning_view& operator=(owning_view&& other) + { + _r = etl::move(other._r); + return *this; + } + + constexpr Range& base() noexcept + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r)); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r)); + } + + constexpr bool empty() const + { + return begin() == end(); + } + + constexpr size_t size() const + { + return etl::distance(begin(), end()); + } + + constexpr pointer data() + { + return &(*begin()); + } + + private: + Range _r; + }; + + template + owning_view(Range&&) -> owning_view; + + struct owning_range_adapter_closure: public range_adapter_closure + { + template> + using target_view_type = owning_view; + + owning_range_adapter_closure() = default; + + template> + owning_view operator()(Range&& r) + { + return owning_view(etl::move(r)); + } + }; + + namespace views + { + namespace private_views + { + struct owning + { + template + constexpr auto operator()(Range&& r) const + { + return ranges::owning_view(etl::forward(r)); + } + + constexpr auto operator()() const + { + return ranges::owning_range_adapter_closure(); + } + }; + } + + inline constexpr private_views::owning owning; + } + + namespace views + { + namespace private_views + { + struct all + { + template>, etl::decay_t>, int> = 0> + constexpr etl::decay_t operator()(Range&& r) const + { + return r; + } + + template>, etl::decay_t>, int> = 0> + constexpr auto operator()(Range&& r) const + { + if constexpr(etl::is_lvalue_reference_v) + { + return etl::ranges::ref_view(etl::forward(r)); + } + else + { + return etl::ranges::owning_view(etl::forward(r)); + } + } + }; + } + + inline constexpr private_views::all all; + + template + using all_t = decltype(views::all(etl::declval())); + } + + template + class filter_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using iterator = typename trait::iterator; + using const_iterator = typename trait::const_iterator; + using value_type = typename trait::value_type; + using difference_type = typename trait::difference_type; + using pointer = typename trait::pointer; + using reference = typename trait::reference; + + using iterator_category = ETL_OR_STD::bidirectional_iterator_tag; + + filter_iterator(const_iterator it, const_iterator it_end, const Pred& p): _it{it}, _it_begin{it}, _it_end{it_end}, _p{p} + { + while (_it != _it_end && !_p(*_it)) + { + ++_it; + } + } + + filter_iterator(const filter_iterator& other): _it{other._it}, _it_begin{other._it_begin}, _it_end{other._it_end}, _p{other._p} + { + while (_it != _it_end && !_p(*_it)) + { + ++_it; + } + } + + filter_iterator& operator++() + { + ++_it; + while (_it != _it_end && !_p(*_it)) + { + ++_it; + } + return *this; + } + + filter_iterator operator++(int) + { + filter_iterator tmp = *this; + + _it++; + while (_it != _it_end && !_p(*_it)) + { + _it++; + } + + return tmp; + } + + filter_iterator& operator--() + { + --_it; + while (_it != _it_begin && !_p(*_it)) + { + --_it; + } + return *this; + } + + filter_iterator operator--(int) + { + filter_iterator tmp = *this; + + _it--; + while (_it != _it_begin && !_p(*_it)) + { + _it--; + } + + return tmp; + } + + filter_iterator& operator+=(size_t n) + { + for (size_t i = 0; i < n; i++) + { + if (_it != _it_end) + { + ++(*this); + } + } + + return *this; + } + + filter_iterator& operator-=(size_t n) + { + for (size_t i = 0; i < n; i++) + { + if (_it != _it_begin) + { + --(*this); + } + } + + return *this; + } + + filter_iterator& operator=(const filter_iterator& other) + { + _it = other._it; + _it_begin = other._it_begin; + _it_end = other._it_end; + ETL_ASSERT(&_p == &other._p, ETL_ERROR_GENERIC("Predicates need to be the same")); + return *this; + } + + value_type operator*() + { + return *_it; + } + + bool operator==(const filter_iterator& other) const + { + return other._it == _it; + } + + bool operator!=(const filter_iterator& other) const + { + return !(*this == other); + } + + private: + const_iterator _it; + const_iterator _it_begin; + const_iterator _it_end; + const Pred& _p; + }; + + template + constexpr typename filter_iterator::difference_type operator-(const filter_iterator& lhs, const filter_iterator& rhs) + { + typename filter_iterator::difference_type result{0}; + filter_iterator it_up{rhs}; + while (it_up != lhs) + { + ++it_up; + ++result; + } + return result; + } + + template + class filter_view: public etl::ranges::view_interface> + { + public: + using iterator = filter_iterator; + using const_iterator = filter_iterator; + + filter_view(Range&& r, const Pred& pred): _pred{pred}, _r{etl::move(r)} + { + } + + constexpr Range& base() const& + { + return _r; + } + + constexpr const Pred& pred() const + { + return _pred; + } + + constexpr const_iterator begin() const + { + return const_iterator(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r), _pred); + } + + constexpr const_iterator end() const + { + return const_iterator(ETL_OR_STD::cend(_r), ETL_OR_STD::cend(_r), _pred); + } + + private: + const Pred _pred; + Range _r; + }; + + template + filter_view(Range&&, Pred) -> filter_view, Pred>; + + template + struct filter_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = filter_view; + + filter_range_adapter_closure(const Pred& p): _p{p} + { + } + + template + constexpr auto operator()(Range&& r) + { + return filter_view(views::all(etl::forward(r)), _p); + } + + const Pred _p; + }; + + namespace views + { + namespace private_views + { + struct filter + { + template + constexpr auto operator()(Range&& r, const Pred& p) const + { + return filter_view(views::all(etl::forward(r)), p); + } + + template + constexpr auto operator()(const Pred& p) const + { + return ranges::filter_range_adapter_closure(p); + } + }; + } + + inline constexpr private_views::filter filter; + } + + template + class transform_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using iterator = typename trait::iterator; + using const_iterator = typename trait::const_iterator; + using value_type = typename trait::value_type; + using difference_type = typename trait::difference_type; + using pointer = typename trait::pointer; + using reference = typename trait::reference; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + transform_iterator(const_iterator it, const Fun& f): _it(it), _f(f) + { + } + + transform_iterator(const transform_iterator& other): _it{other._it}, _f{other._f} + { + } + + transform_iterator& operator++() + { + ++_it; + return *this; + } + + transform_iterator operator++(int) + { + transform_iterator tmp = *this; + _it++; + return tmp; + } + + transform_iterator& operator=(const transform_iterator& other) + { + _it = other._it; + ETL_ASSERT(&_f == &other._f, ETL_ERROR_GENERIC("Transform functions need to be the same")); + return *this; + } + + value_type operator*() + { + return static_cast(_f(*_it)); + } + + bool operator==(const transform_iterator& other) const + { + return other._it == _it; + } + + bool operator!=(const transform_iterator& other) const + { + return !(*this == other); + } + + private: + const_iterator _it; + const Fun& _f; + }; + + template + class transform_view: public etl::ranges::view_interface> + { + public: + using iterator = transform_iterator; + using const_iterator = transform_iterator; + + transform_view(Range&& r, const Fun& fun): _fun{fun}, _r{etl::move(r)} + { + } + + constexpr Range& base() const& + { + return _r; + } + + constexpr const_iterator begin() const + { + return const_iterator(ETL_OR_STD::begin(_r), _fun); + } + + constexpr const_iterator end() const + { + return const_iterator(ETL_OR_STD::end(_r), _fun); + } + + constexpr size_t size() const + { + return etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r)); + } + private: + const Fun _fun; + Range _r; + }; + + template + transform_view(Range&&, Fun) -> transform_view, Fun>; + + template + struct transform_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = transform_view; + + transform_range_adapter_closure(const Fun& f): _f{f} + { + } + + template + constexpr auto operator()(Range&& r) + { + return transform_view(views::all(etl::forward(r)), _f); + } + + const Fun _f; + }; + + namespace views + { + namespace private_views + { + struct transform + { + template + constexpr auto operator()(Range&& r, const Fun& f) const + { + return transform_view(views::all(etl::forward(r)), f); + } + + template + constexpr auto operator()(const Fun& f) const + { + return ranges::transform_range_adapter_closure(f); + } + }; + } + + inline constexpr private_views::transform transform; + } + + template + class as_rvalue_view: public etl::ranges::view_interface> + { + public: + + using iterator = typename etl::move_iterator::iterator>; + + as_rvalue_view(const as_rvalue_view& other) = default; + + as_rvalue_view(Range&& r): _r{etl::move(r)} + { + } + + constexpr Range& base() const + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r)); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r)); + } + + constexpr size_t size() const + { + return etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r)); + } + + private: + Range _r; + }; + + template + as_rvalue_view(Range&&) -> as_rvalue_view>; + + struct as_rvalue_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = as_rvalue_view; + + as_rvalue_range_adapter_closure() = default; + + template + constexpr auto operator()(Range&& r) + { + return as_rvalue_view(views::all(etl::forward(r))); + } + }; + + namespace views + { + namespace private_views + { + struct as_rvalue + { + template + constexpr auto operator()(Range&& r) const + { + return as_rvalue_view(views::all(etl::forward(r))); + } + + constexpr auto operator()() const + { + return ranges::as_rvalue_range_adapter_closure(); + } + }; + } + + inline constexpr private_views::as_rvalue as_rvalue; + } + + template + class as_const_view: public etl::ranges::view_interface> + { + public: + + using iterator = typename etl::ranges::private_ranges::iterator_trait::const_iterator; + using const_iterator = iterator; + + as_const_view(const as_const_view& other) = default; + + as_const_view(Range&& r): _r{etl::move(r)} + { + } + + constexpr Range& base() const + { + return _r; + } + + constexpr iterator begin() const + { + return ETL_OR_STD::cbegin(_r); + } + + constexpr iterator end() const + { + return ETL_OR_STD::cend(_r); + } + + constexpr size_t size() const + { + return etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r)); + } + + private: + mutable Range _r; + }; + + template + as_const_view(Range&&) -> as_const_view>; + + struct as_const_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = as_const_view; + + as_const_range_adapter_closure() = default; + + template + constexpr auto operator()(Range&& r) + { + return as_const_view(views::all(etl::forward(r))); + } + }; + + namespace views + { + namespace private_views + { + struct as_const + { + template + constexpr auto operator()(Range&& r) const + { + return as_const_view(views::all(etl::forward(r))); + } + + constexpr auto operator()() const + { + return ranges::as_const_range_adapter_closure(); + } + }; + } + + inline constexpr private_views::as_const as_const; + } + + //************************************************************************* + /// cache_latest_cache + /// A lightweight cache for a single value, used internally by + /// cache_latest_view to avoid depending on etl::optional. + //************************************************************************* + namespace private_ranges + { + template + struct cache_latest_cache + { + cache_latest_cache(): _has_value{false}, _value{} + { + } + + cache_latest_cache(const cache_latest_cache&) = delete; + cache_latest_cache& operator=(const cache_latest_cache&) = delete; + + bool has_value() const { return _has_value; } + + void set(const T& v) + { + _value = v; + _has_value = true; + } + + void reset() + { + _has_value = false; + } + + T& value() { return _value; } + + bool _has_value; + T _value; + }; + } + + //************************************************************************* + /// cache_latest_iterator + //************************************************************************* + template + class cache_latest_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using iterator = typename trait::iterator; + using const_iterator = typename trait::const_iterator; + using value_type = typename trait::value_type; + using difference_type = typename trait::difference_type; + using pointer = value_type*; + using reference = value_type&; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + cache_latest_iterator() = default; + + cache_latest_iterator(const_iterator it, private_ranges::cache_latest_cache* cache) + : _it(it) + , _cache(cache) + { + } + + cache_latest_iterator(const cache_latest_iterator& other) + : _it(other._it) + , _cache(other._cache) + { + } + + cache_latest_iterator& operator++() + { + ++_it; + if (_cache) + { + _cache->reset(); + } + return *this; + } + + cache_latest_iterator operator++(int) + { + cache_latest_iterator tmp = *this; + ++(*this); + return tmp; + } + + cache_latest_iterator& operator=(const cache_latest_iterator& other) + { + _it = other._it; + _cache = other._cache; + return *this; + } + + reference operator*() const + { + if (_cache && !_cache->has_value()) + { + _cache->set(*_it); + } + return _cache->value(); + } + + pointer operator->() const + { + return &(**this); + } + + bool operator==(const cache_latest_iterator& other) const + { + return other._it == _it; + } + + bool operator!=(const cache_latest_iterator& other) const + { + return !(*this == other); + } + + private: + mutable const_iterator _it; + private_ranges::cache_latest_cache* _cache; + }; + + //************************************************************************* + /// cache_latest_view + /// A range adaptor that caches the most recently accessed element of the + /// underlying range. Useful when dereferencing the underlying iterator is + /// expensive and the result is needed more than once. + //************************************************************************* + template + class cache_latest_view: public etl::ranges::view_interface> + { + public: + using iterator = cache_latest_iterator; + using const_iterator = cache_latest_iterator; + using value_type = typename etl::ranges::private_ranges::iterator_trait::value_type; + + cache_latest_view(Range&& r): _r{etl::move(r)}, _cache{} + { + } + + cache_latest_view(const cache_latest_view& other): _r{other._r}, _cache{} + { + } + + constexpr Range& base() const& + { + return _r; + } + + constexpr iterator begin() const + { + _cache.reset(); + return iterator(ETL_OR_STD::begin(_r), &_cache); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r), &_cache); + } + + constexpr size_t size() const + { + return etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r)); + } + + private: + mutable Range _r; + mutable private_ranges::cache_latest_cache _cache; + }; + + template + cache_latest_view(Range&&) -> cache_latest_view>; + + struct cache_latest_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = cache_latest_view; + + cache_latest_range_adapter_closure() = default; + + template + constexpr auto operator()(Range&& r) + { + return cache_latest_view(views::all(etl::forward(r))); + } + }; + + namespace views + { + namespace private_views + { + struct cache_latest + { + template + constexpr auto operator()(Range&& r) const + { + return cache_latest_view(views::all(etl::forward(r))); + } + + constexpr auto operator()() const + { + return ranges::cache_latest_range_adapter_closure(); + } + }; + } + + inline constexpr private_views::cache_latest cache_latest; + } + + template + class reverse_view: public etl::ranges::view_interface> + { + public: + using iterator = ETL_OR_STD::reverse_iterator::iterator>; + using const_iterator = ETL_OR_STD::reverse_iterator::const_iterator>; + using difference_type = typename etl::ranges::private_ranges::iterator_trait::difference_type; + + constexpr reverse_view(Range&& r): _r{etl::move(r)} + { + } + + reverse_view(const reverse_view& other) = default; + + constexpr Range base() const& + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::end(_r)); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::begin(_r)); + } + + constexpr size_t size() const + { + return etl::distance(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r)); + } + + private: + Range _r; + }; + + template + reverse_view(Range&&) -> reverse_view>; + + template + struct is_reverse_view : etl::false_type + { + }; + + template + struct is_reverse_view> : etl::true_type + { + }; + + namespace views + { + namespace private_views + { + struct reverse + { + template + constexpr auto operator()(Range&& r) const + { + if constexpr (is_reverse_view>>::value) + { + return r.base(); + } + else + { + return reverse_view(views::all(etl::forward(r))); + } + } + }; + } + + inline constexpr private_views::reverse reverse; + } + + template + class drop_view: public etl::ranges::view_interface> + { + public: + using iterator = typename etl::ranges::private_ranges::iterator_trait::iterator; + using const_iterator = typename etl::ranges::private_ranges::iterator_trait::const_iterator; + using difference_type = typename etl::ranges::private_ranges::iterator_trait::difference_type; + + constexpr drop_view(Range&& r, size_t drop_n) + : _r{etl::move(r)}, _drop_n{drop_n}, _begin_cache{ETL_OR_STD::end(_r)}, _begin_cache_valid{false} + { + } + + drop_view(const drop_view& other) = default; + + constexpr Range base() const& + { + return _r; + } + + constexpr iterator begin() const + { + if (!_begin_cache_valid) + { + _begin_cache = drop_begin(); + _begin_cache_valid = true; + } + return _begin_cache; + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r)); + } + + constexpr size_t size() const + { + if (!_begin_cache_valid) + { + _begin_cache = drop_begin(); + _begin_cache_valid = true; + } + return etl::distance(_begin_cache, ETL_OR_STD::end(_r)); + } + + private: + constexpr iterator drop_begin() const + { + iterator result {ETL_OR_STD::end(_r)}; + if (static_cast(_drop_n) < etl::distance(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r))) + { + result = ETL_OR_STD::begin(_r); + etl::advance(result, _drop_n); + } + return result; + } + + Range _r; + size_t _drop_n; + mutable iterator _begin_cache; + mutable bool _begin_cache_valid; + }; + + template + drop_view(Range&&) -> drop_view>; + + struct drop_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = drop_view; + + constexpr drop_range_adapter_closure(size_t drop_n): _drop_n{drop_n} + { + } + + template + constexpr auto operator()(Range&& r) const + { + return drop_view(views::all(etl::forward(r)), _drop_n); + } + + const size_t _drop_n; + }; + + namespace views + { + namespace private_views + { + struct drop + { + template + constexpr auto operator()(Range&& r, size_t drop_n) const + { + return drop_view(views::all(etl::forward(r)), drop_n); + } + + constexpr auto operator()(size_t drop_n) const + { + return ranges::drop_range_adapter_closure(drop_n); + } + }; + } + + inline constexpr private_views::drop drop; + } + + template + class drop_while_view: public etl::ranges::view_interface> + { + public: + using const_iterator = typename etl::ranges::private_ranges::iterator_trait::const_iterator; + using difference_type = typename etl::ranges::private_ranges::iterator_trait::difference_type; + + constexpr drop_while_view(Range&& r, Pred pred) + : _r{etl::move(r)}, _pred{pred}, _begin_cache{}, _begin_cache_valid{false} + { + } + + constexpr const Range base() const& + { + return _r; + } + + constexpr const Pred& pred() const + { + return _pred; + } + + constexpr const_iterator begin() const + { + if (!_begin_cache_valid) + { + const_iterator result{ETL_OR_STD::cbegin(_r)}; + while (result != ETL_OR_STD::cend(_r) && _pred(*result)) + { + ++result; + } + _begin_cache = result; + _begin_cache_valid = true; + } + return _begin_cache; + } + + constexpr const_iterator end() const + { + return const_iterator(ETL_OR_STD::cend(_r)); + } + + private: + Range _r; + Pred _pred; + mutable const_iterator _begin_cache; + mutable bool _begin_cache_valid; + }; + + template + drop_while_view(Range&&, Pred) -> drop_while_view, Pred>; + + template + struct drop_while_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = drop_while_view; + + constexpr drop_while_range_adapter_closure(Pred& pred): _pred{pred} + { + } + + template + constexpr auto operator()(Range&& r) const + { + return drop_while_view(views::all(etl::forward(r)), _pred); + } + + Pred _pred; + }; + + namespace views + { + namespace private_views + { + struct drop_while + { + template + constexpr auto operator()(Range&& r, Pred pred) const + { + return drop_while_view(views::all(etl::forward(r)), pred); + } + + template + constexpr auto operator()(Pred pred) const + { + return ranges::drop_while_range_adapter_closure(pred); + } + }; + } + + inline constexpr private_views::drop_while drop_while; + } + + // Own implementation instead of using etl::min to avoid including algorithm.h + namespace private_views + { + template + constexpr T min(T a, T b) + { + return a < b ? a : b; + } + } + + template + class take_view: public etl::ranges::view_interface> + { + public: + using iterator = typename etl::ranges::private_ranges::iterator_trait::iterator; + using const_iterator = typename etl::ranges::private_ranges::iterator_trait::const_iterator; + using difference_type = typename etl::ranges::private_ranges::iterator_trait::difference_type; + + constexpr take_view(Range&& r, ranges::range_difference_t take_n) + : _r{etl::move(r)} + , _take_n{private_views::min>(take_n, etl::distance(ETL_OR_STD::cbegin(r), ETL_OR_STD::cend(r)))} + { + } + + take_view(const take_view& other) = default; + + constexpr Range base() const& + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r)); + } + + constexpr iterator end() const + { + iterator result {begin()}; + etl::advance(result, _take_n); + return result; + } + + constexpr ranges::range_difference_t size() const + { + return _take_n; + } + + private: + Range _r; + ranges::range_difference_t _take_n; + }; + + template + take_view(Range&&, ranges::range_difference_t) -> take_view>; + + struct take_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = take_view; + + template + constexpr take_range_adapter_closure(DifferenceType take_n): _take_n{static_cast(take_n)} + { + } + + template + constexpr auto operator()(Range&& r) const + { + return take_view(views::all(etl::forward(r)), _take_n); + } + + const size_t _take_n; + }; + + namespace views + { + namespace private_views + { + struct take + { + template + constexpr auto operator()(Range&& r, ranges::range_difference_t take_n) const + { + return take_view(views::all(etl::forward(r)), take_n); + } + + template + constexpr auto operator()(DifferenceType take_n) const + { + return ranges::take_range_adapter_closure(take_n); + } + }; + } + + inline constexpr private_views::take take; + } + + template + class take_while_view: public etl::ranges::view_interface> + { + public: + using iterator = typename etl::ranges::private_ranges::iterator_trait::iterator; + using const_iterator = typename etl::ranges::private_ranges::iterator_trait::const_iterator; + using difference_type = typename etl::ranges::private_ranges::iterator_trait::difference_type; + + constexpr take_while_view(Range&& r, Pred pred) + : _r{etl::move(r)}, _pred{etl::move(pred)}, _end_cache{}, _end_cache_valid{false} + { + } + + constexpr const Range base() const& + { + return _r; + } + + constexpr const Pred& pred() const + { + return _pred; + } + + constexpr auto begin() const + { + return ETL_OR_STD::begin(_r); + } + + constexpr auto end() const + { + if (!_end_cache_valid) + { + iterator result{ETL_OR_STD::begin(_r)}; + while (result != ETL_OR_STD::end(_r) && _pred(*result)) + { + ++result; + } + _end_cache = result; + _end_cache_valid = true; + } + return _end_cache; + } + + private: + Range _r; + Pred _pred; + mutable iterator _end_cache; + mutable bool _end_cache_valid; + }; + + template + take_while_view(Range&&, Pred) -> take_while_view, Pred>; + + template + struct take_while_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = take_while_view; + + constexpr take_while_range_adapter_closure(Pred pred): _pred{etl::move(pred)} + { + } + + template + constexpr auto operator()(Range&& r) const + { + return take_while_view(views::all(etl::forward(r)), _pred); + } + + Pred _pred; + }; + + namespace views + { + namespace private_views + { + struct take_while + { + template + constexpr auto operator()(Range&& r, Pred&& pred) const + { + return take_while_view(views::all(etl::forward(r)), etl::forward(pred)); + } + + template + constexpr auto operator()(Pred&& pred) const + { + return ranges::take_while_range_adapter_closure(etl::forward(pred)); + } + }; + } + + inline constexpr private_views::take_while take_while; + } + + template + class join_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using iterator = typename trait::iterator; + using const_iterator = typename trait::const_iterator; + using difference_type = typename trait::difference_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + using InnerRange = decltype(*(ETL_OR_STD::begin(etl::declval()))); + using inner_trait = typename etl::ranges::private_ranges::iterator_trait; + using inner_iterator = typename inner_trait::iterator; + + using value_type = typename inner_trait::value_type; + using pointer = typename inner_trait::pointer; + using reference = typename inner_trait::reference; + + join_iterator(iterator it, iterator it_end) + : _it(it) + , _it_end(it_end) + , _inner_it(it != it_end ? ETL_OR_STD::begin(*it) : inner_iterator{}) + , _inner_it_end(it != it_end ? ETL_OR_STD::end(*it) : inner_iterator{}) + { + adjust_iterator(); + } + + join_iterator(const join_iterator& other) = default; + + join_iterator& operator++() + { + if (_inner_it != _inner_it_end) + { + ++_inner_it; + } + + adjust_iterator(); + + return *this; + } + + join_iterator operator++(int) + { + join_iterator tmp{*this}; + + if (_inner_it != _inner_it_end) + { + _inner_it++; + } + + adjust_iterator(); + + return tmp; + } + + join_iterator& operator=(const join_iterator& other) + { + _it = other._it; + _it_end = other._it_end; + _inner_it = other._inner_it; + _inner_it_end = other._inner_it_end; + + adjust_iterator(); + + return *this; + } + + reference operator*() const + { + return *_inner_it; + } + + constexpr bool operator==(const join_iterator& other) const + { + return (_it == other._it && _inner_it == other._inner_it) || (_it == _it_end && other._it == other._it_end); + } + + constexpr bool operator!=(const join_iterator& other) const + { + return !(*this == other); + } + + private: + void adjust_iterator() + { + while (_it != _it_end && _inner_it == _inner_it_end) + { + ++_it; + if (_it != _it_end) + { + _inner_it = ETL_OR_STD::begin((*_it)); + _inner_it_end = ETL_OR_STD::end((*_it)); + } + } + } + + iterator _it; + iterator _it_end; + inner_iterator _inner_it; + inner_iterator _inner_it_end; + }; + + template + class join_view: public etl::ranges::view_interface> + { + public: + using iterator = join_iterator; + using const_iterator = join_iterator; + + join_view(Range&& r): _r{etl::move(r)} + { + } + + constexpr Range base() const& + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r)); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r)); + } + + private: + Range _r; + }; + + struct join_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = join_view; + + join_range_adapter_closure() = default; + + template + constexpr auto operator()(Range&& r) + { + return join_view(views::all(etl::forward(r))); + } + }; + + template + explicit join_view(Range&&) -> join_view>; + + namespace views + { + namespace private_views + { + struct join + { + template + constexpr auto operator()(Range&& r) const + { + return join_view(views::all(etl::forward(r))); + } + + constexpr auto operator()() const + { + return ranges::join_range_adapter_closure(); + } + }; + } + + inline constexpr private_views::join join; + } + + template + class join_with_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using iterator = typename trait::iterator; + using const_iterator = typename trait::const_iterator; + using difference_type = typename trait::difference_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + using InnerRange = decltype(*(ETL_OR_STD::begin(etl::declval()))); + using inner_trait = typename etl::ranges::private_ranges::iterator_trait; + using inner_iterator = typename inner_trait::iterator; + + using value_type = typename inner_trait::value_type; + using pointer = typename inner_trait::pointer; + using reference = typename inner_trait::reference; + + using pattern_trait = typename etl::ranges::private_ranges::iterator_trait; + using pattern_iterator = typename pattern_trait::iterator; + using pattern_const_iterator = typename pattern_trait::const_iterator; + + join_with_iterator(iterator it, iterator it_end, const Pattern& pattern) + : _it(it) + , _it_end(it_end) + , _inner_it(it != it_end ? ETL_OR_STD::begin(*it) : inner_iterator{}) + , _inner_it_end(it != it_end ? ETL_OR_STD::end(*it) : inner_iterator{}) + , _pattern(pattern) + , _pattern_it(pattern.cend()) + , _pattern_it_end(pattern.cend()) + { + adjust_iterator(); + } + + join_with_iterator(const join_with_iterator& other) = default; + + join_with_iterator& operator++() + { + if (_pattern_it != _pattern_it_end) + { + ++_pattern_it; + } + else if (_inner_it != _inner_it_end) + { + ++_inner_it; + } + + adjust_iterator(); + + return *this; + } + + join_with_iterator operator++(int) + { + join_with_iterator tmp{*this}; + + if (_pattern_it != _pattern_it_end) + { + _pattern_it++; + } + else if (_inner_it != _inner_it_end) + { + _inner_it++; + } + + adjust_iterator(); + + return tmp; + } + + join_with_iterator& operator=(const join_with_iterator& other) + { + _it = other._it; + _it_end = other._it_end; + _inner_it = other._inner_it; + _inner_it_end = other._inner_it_end; + _pattern_it = other._pattern_it; + _pattern_it_end = other._pattern_it_end; + + adjust_iterator(); + + return *this; + } + + value_type operator*() const + { + if (_pattern_it != _pattern_it_end) + { + return *_pattern_it; + } + return *_inner_it; + } + + constexpr bool operator==(const join_with_iterator& other) const + { + return (_it == other._it && _inner_it == other._inner_it && _pattern_it == other._pattern_it) || (_it == _it_end); + } + + constexpr bool operator!=(const join_with_iterator& other) const + { + return !(*this == other); + } + + private: + void adjust_iterator() + { + if (_it != _it_end && _inner_it == _inner_it_end && _pattern_it == _pattern_it_end) + { + ++_it; + if (_it != _it_end) + { + _pattern_it = ETL_OR_STD::cbegin(_pattern); + _pattern_it_end = ETL_OR_STD::cend(_pattern); + _inner_it = ETL_OR_STD::begin(*_it); + _inner_it_end = ETL_OR_STD::end(*_it); + } + } + } + + iterator _it; + iterator _it_end; + inner_iterator _inner_it; + inner_iterator _inner_it_end; + const Pattern& _pattern; + pattern_const_iterator _pattern_it; + pattern_const_iterator _pattern_it_end; + }; + + template + class join_with_view: public etl::ranges::view_interface> + { + public: + using iterator = join_with_iterator; + using const_iterator = join_with_iterator; + + join_with_view(Range&& r, Pattern&& pattern): _r{etl::move(r)}, _pattern{etl::move(pattern)} + { + } + + constexpr Range base() const& + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), _pattern); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), _pattern); + } + + private: + Range _r; + Pattern _pattern; + }; + + // For range as separator + template + join_with_view(Range&&, Pattern&&) -> join_with_view, views::all_t>; + + // For single value as separator + template + join_with_view(Range&&, etl::ranges::range_value_t>) + -> join_with_view, etl::ranges::single_view>>>; + + namespace private_ranges + { + template + constexpr auto make_pattern(Pattern&& pattern) + { + if constexpr(etl::is_base_of_v, Pattern>) + { + return etl::forward(pattern); + } + else + { + return etl::ranges::single_view(etl::forward(pattern)); + } + } + + template + constexpr auto make_pattern(const Pattern& pattern) + { + if constexpr(etl::is_array_v> || etl::is_range_v>) + { + return views::all(pattern); + } + else + { + return etl::ranges::single_view>(pattern); + } + } + } + + template + struct join_with_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = join_with_view; + + join_with_range_adapter_closure(const Pattern& pattern): _pattern(pattern) + { + } + + template + constexpr auto operator()(Range&& r) + { + return join_with_view(views::all(etl::forward(r)), private_ranges::make_pattern(_pattern)); + } + + const Pattern& _pattern; + }; + + namespace views + { + namespace private_views + { + struct join_with + { + template + constexpr auto operator()(Range&& r, Pattern&& pattern) const + { + return join_with_view(views::all(etl::forward(r)), views::all(etl::ranges::private_ranges::make_pattern(etl::forward(pattern)))); + } + + template + constexpr auto operator()(const Pattern& pattern) const + { + return ranges::join_with_range_adapter_closure(pattern); + } + }; + } + + inline constexpr private_views::join_with join_with; + } + + template + class split_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using iterator = typename trait::iterator; + using const_iterator = typename trait::const_iterator; + using difference_type = typename trait::difference_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + using pattern_trait = typename etl::ranges::private_ranges::iterator_trait; + using pattern_const_iterator = typename pattern_trait::const_iterator; + + using value_type = etl::ranges::subrange; + using pointer = value_type*; + using reference = value_type; + + split_iterator(const_iterator it, const_iterator it_end, const Pattern& pattern, bool is_end = false) + : _it(it) + , _it_end(it_end) + , _pattern(pattern) + , _next(find_next()) + , _trailing_empty(!is_end && _it == _it_end) + { + } + + split_iterator(const split_iterator& other) = default; + + split_iterator& operator++() + { + _it = _next; + + if (_it != _it_end) + { + // Skip past the delimiter + auto pat_size = etl::distance(ETL_OR_STD::cbegin(_pattern), ETL_OR_STD::cend(_pattern)); + for (difference_type i = 0; i < pat_size && _it != _it_end; ++i) + { + ++_it; + } + _next = find_next(); + // If we landed exactly at _it_end after skipping the delimiter, + // there is one more trailing empty segment to emit. + if (_it == _it_end && !_trailing_empty) + { + _trailing_empty = true; + } + } + else + { + // We were at _it_end; this was the trailing empty segment. + _trailing_empty = false; + } + + return *this; + } + + split_iterator operator++(int) + { + split_iterator tmp{*this}; + ++(*this); + return tmp; + } + + split_iterator& operator=(const split_iterator& other) + { + _it = other._it; + _it_end = other._it_end; + _next = other._next; + _trailing_empty = other._trailing_empty; + return *this; + } + + value_type operator*() const + { + return value_type(_it, _next); + } + + constexpr bool operator==(const split_iterator& other) const + { + if (_it == _it_end && other._it == other._it_end) + { + return _trailing_empty == other._trailing_empty; + } + return _it == other._it; + } + + constexpr bool operator!=(const split_iterator& other) const + { + return !(*this == other); + } + + private: + const_iterator find_next() const + { + auto pat_begin = ETL_OR_STD::cbegin(_pattern); + auto pat_end = ETL_OR_STD::cend(_pattern); + auto pat_size = etl::distance(pat_begin, pat_end); + + if (pat_size == 0) + { + // Empty pattern: split between each element + auto result = _it; + if (result != _it_end) + { + ++result; + } + return result; + } + + for (auto search = _it; search != _it_end; ++search) + { + // Try to match the full pattern starting at 'search' + auto s = search; + auto p = pat_begin; + bool match = true; + while (p != pat_end) + { + if (s == _it_end || !(*s == *p)) + { + match = false; + break; + } + ++s; + ++p; + } + if (match) + { + return search; + } + } + + return _it_end; + } + + const_iterator _it; + const_iterator _it_end; + const Pattern& _pattern; + const_iterator _next; + // there is still one empty segment to emit after the last delimiter if the last delimiter is at the end of the range + bool _trailing_empty; + }; + + template + class split_view: public etl::ranges::view_interface> + { + public: + using iterator = split_iterator; + using const_iterator = split_iterator; + + split_view(Range&& r, Pattern&& pattern): _r{etl::move(r)}, _pattern{etl::move(pattern)} + { + } + + constexpr Range& base() const& + { + return _r; + } + + constexpr const Pattern& pattern() const + { + return _pattern; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), _pattern); + } + + constexpr iterator end() const + { + auto it = iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), _pattern, true); + return it; + } + + private: + Range _r; + Pattern _pattern; + }; + + + // For range as delimiter (Pattern is a range, not a single value) + template >, int> = 0> + split_view(Range&&, Pattern&&) -> split_view, views::all_t>; + + // For single value as delimiter (Pattern is not a range) + template >, int> = 0> + split_view(Range&&, Pattern&&) + -> split_view, etl::ranges::single_view>>; + + template + struct split_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = split_view; + + split_range_adapter_closure(const Pattern& pattern): _pattern(pattern) + { + } + + + template + constexpr auto operator()(Range&& r) + { + // If Pattern is a range, use views::all. If not, wrap in single_view. + if constexpr (etl::is_class_v>) + { + return split_view(views::all(etl::forward(r)), views::all(_pattern)); + } + else + { + return split_view(views::all(etl::forward(r)), etl::ranges::single_view(_pattern)); + } + } + + const Pattern& _pattern; + }; + + namespace views + { + namespace private_views + { + struct split + { + // Range + Pattern (Pattern is a range) + template>, int> = 0> + constexpr auto operator()(Range&& r, Pattern&& pattern) const + { + return split_view(views::all(etl::forward(r)), views::all(etl::forward(pattern))); + } + + // Range + Pattern (Pattern is a single value) + template>, int> = 0> + constexpr auto operator()(Range&& r, Pattern&& pattern) const + { + return split_view(views::all(etl::forward(r)), etl::ranges::single_view(pattern)); + } + + // Pipe closure + template + constexpr auto operator()(const Pattern& pattern) const + { + return ranges::split_range_adapter_closure(pattern); + } + }; + } + + inline constexpr private_views::split split; + } + + //************************************************************************* + /// lazy_split_view: lazily splits a range by a pattern. + /// Unlike split_view (which eagerly computes subrange boundaries), + /// lazy_split_view yields an inner range whose elements are discovered + /// one at a time via a dedicated inner iterator. This makes it suitable + /// for input ranges that do not support multi-pass iteration. + //************************************************************************* + + template + class lazy_split_view; + + /// Inner range returned by dereferencing a lazy_split_iterator. + /// Walking this range lazily consumes source elements until the + /// delimiter pattern is found (or the source is exhausted). + template + class lazy_split_inner_range + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + using const_iterator_type = typename trait::const_iterator; + using value_type = typename trait::value_type; + + using pattern_trait = typename etl::ranges::private_ranges::iterator_trait; + using pattern_const_iterator = typename pattern_trait::const_iterator; + + class iterator + { + public: + using value_type = typename trait::value_type; + using difference_type = typename trait::difference_type; + using pointer = const value_type*; + using reference = const value_type&; + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + iterator() = default; + + iterator(const_iterator_type current, + const_iterator_type segment_end, + bool is_end) + : _current(current) + , _segment_end(segment_end) + , _is_end(is_end || (current == segment_end)) + { + } + + reference operator*() const + { + return *_current; + } + + pointer operator->() const + { + return &(*_current); + } + + iterator& operator++() + { + ++_current; + if (_current == _segment_end) + { + _is_end = true; + } + return *this; + } + + iterator operator++(int) + { + iterator tmp{*this}; + ++(*this); + return tmp; + } + + constexpr bool operator==(const iterator& other) const + { + if (_is_end && other._is_end) + { + return true; + } + if (_is_end != other._is_end) + { + return false; + } + return _current == other._current; + } + + constexpr bool operator!=(const iterator& other) const + { + return !(*this == other); + } + + private: + const_iterator_type _current{}; + const_iterator_type _segment_end{}; + bool _is_end = true; + }; + + using const_iterator = iterator; + + lazy_split_inner_range(const_iterator_type segment_begin, + const_iterator_type segment_end) + : _segment_begin(segment_begin) + , _segment_end(segment_end) + { + } + + iterator begin() const + { + return iterator(_segment_begin, _segment_end, false); + } + + iterator end() const + { + return iterator(_segment_end, _segment_end, true); + } + + bool empty() const + { + return _segment_begin == _segment_end; + } + + private: + const_iterator_type _segment_begin; + const_iterator_type _segment_end; + }; + + /// Outer iterator for lazy_split_view. + /// Each dereference yields a lazy_split_inner_range that lazily + /// walks the current segment up to the next pattern match. + template + class lazy_split_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using source_iterator = typename trait::iterator; + using const_iterator = typename trait::const_iterator; + using difference_type = typename trait::difference_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + using pattern_trait = typename etl::ranges::private_ranges::iterator_trait; + using pattern_const_iterator = typename pattern_trait::const_iterator; + + using value_type = lazy_split_inner_range; + using pointer = value_type*; + using reference = value_type; + + lazy_split_iterator(const_iterator it, + const_iterator it_end, + const Pattern& pattern, + bool is_end = false) + : _it(it) + , _it_end(it_end) + , _pattern(pattern) + , _next(find_next()) + , _trailing_empty(!is_end && _it == _it_end) + { + } + + lazy_split_iterator(const lazy_split_iterator& other) = default; + + lazy_split_iterator& operator=(const lazy_split_iterator& other) + { + _it = other._it; + _it_end = other._it_end; + _next = other._next; + _trailing_empty = other._trailing_empty; + return *this; + } + + lazy_split_iterator& operator++() + { + _it = _next; + + if (_it != _it_end) + { + // Skip past the matched delimiter + auto pat_size = etl::distance(ETL_OR_STD::cbegin(_pattern), + ETL_OR_STD::cend(_pattern)); + for (difference_type i = 0; i < pat_size && _it != _it_end; ++i) + { + ++_it; + } + _next = find_next(); + // If we landed exactly at _it_end after skipping the delimiter, + // there is one more trailing empty segment to emit. + if (_it == _it_end && !_trailing_empty) + { + _trailing_empty = true; + } + } + else + { + // We were at _it_end; this was the trailing empty segment. + _trailing_empty = false; + } + + return *this; + } + + lazy_split_iterator operator++(int) + { + lazy_split_iterator tmp{*this}; + ++(*this); + return tmp; + } + + /// Returns a lazy inner range — the caller iterates it element-by-element. + value_type operator*() const + { + return value_type(_it, _next); + } + + constexpr bool operator==(const lazy_split_iterator& other) const + { + if (_it == _it_end && other._it == other._it_end) + { + return _trailing_empty == other._trailing_empty; + } + return _it == other._it; + } + + constexpr bool operator!=(const lazy_split_iterator& other) const + { + return !(*this == other); + } + + private: + /// Scans forward from _it looking for the pattern; returns the + /// position of the first match (i.e. the end of the current segment). + const_iterator find_next() const + { + auto pat_begin = ETL_OR_STD::cbegin(_pattern); + auto pat_end = ETL_OR_STD::cend(_pattern); + auto pat_size = etl::distance(pat_begin, pat_end); + + if (pat_size == 0) + { + // Empty pattern: split between each element + auto result = _it; + if (result != _it_end) + { + ++result; + } + return result; + } + + for (auto search = _it; search != _it_end; ++search) + { + // Try to match the full pattern starting at 'search' + auto s = search; + auto p = pat_begin; + bool match = true; + while (p != pat_end) + { + if (s == _it_end || !(*s == *p)) + { + match = false; + break; + } + ++s; + ++p; + } + if (match) + { + return search; + } + } + + return _it_end; + } + + const_iterator _it; + const_iterator _it_end; + const Pattern& _pattern; + const_iterator _next; + bool _trailing_empty; + }; + + template + class lazy_split_view: public etl::ranges::view_interface> + { + public: + using iterator = lazy_split_iterator; + using const_iterator = lazy_split_iterator; + + lazy_split_view(Range&& r, Pattern&& pattern) + : _r{etl::move(r)} + , _pattern{etl::move(pattern)} + { + } + + constexpr Range& base() const& + { + return _r; + } + + constexpr const Pattern& pattern() const + { + return _pattern; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), _pattern); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), _pattern, true); + } + + private: + Range _r; + Pattern _pattern; + }; + + // Deduction guide: range delimiter (Pattern is a range) + template >, int> = 0> + lazy_split_view(Range&&, Pattern&&) -> lazy_split_view, views::all_t>; + + // Deduction guide: single-value delimiter (Pattern is not a range) + template >, int> = 0> + lazy_split_view(Range&&, Pattern&&) + -> lazy_split_view, etl::ranges::single_view>>; + + template + struct lazy_split_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = lazy_split_view; + + lazy_split_range_adapter_closure(const Pattern& pattern): _pattern(pattern) + { + } + + template + constexpr auto operator()(Range&& r) + { + if constexpr (etl::is_class_v>) + { + return lazy_split_view(views::all(etl::forward(r)), views::all(_pattern)); + } + else + { + return lazy_split_view(views::all(etl::forward(r)), etl::ranges::single_view(_pattern)); + } + } + + const Pattern& _pattern; + }; + + namespace views + { + namespace private_views + { + struct lazy_split + { + // Range + Pattern (Pattern is a range) + template>, int> = 0> + constexpr auto operator()(Range&& r, Pattern&& pattern) const + { + return lazy_split_view(views::all(etl::forward(r)), views::all(etl::forward(pattern))); + } + + // Range + Pattern (Pattern is a single value) + template>, int> = 0> + constexpr auto operator()(Range&& r, Pattern&& pattern) const + { + return lazy_split_view(views::all(etl::forward(r)), etl::ranges::single_view(pattern)); + } + + // Pipe closure + template + constexpr auto operator()(const Pattern& pattern) const + { + return ranges::lazy_split_range_adapter_closure(pattern); + } + }; + } + + inline constexpr private_views::lazy_split lazy_split; + } + + namespace views + { + namespace private_views + { + struct counted + { + template + constexpr auto operator()(Iterator&& it, DifferenceType&& count) const + { + using T = etl::decay_t; + using D = etl::iter_difference_t; + + // contiguous_iterator_tag not yet available + //if constexpr(etl::is_same_v::iterator_category, ETL_OR_STD::contiguous_iterator_tag>) + //{ + // return etl::span(etl::to_address(it), static_cast(static_cast>(count))); + //} + //else + if constexpr(etl::is_same_v::iterator_category, ETL_OR_STD::random_access_iterator_tag>) + { + return etl::ranges::subrange(it, it + static_cast(count)); + } + else + { + return etl::ranges::subrange(etl::counted_iterator(it, count), etl::default_sentinel); + } + } + }; + } + + inline constexpr private_views::counted counted; + } + + template + class concat_view; + + template + class concat_iterator + { + static_assert(sizeof...(Ranges) > 0, "Type list must be non-empty"); + + public: + using types = typename etl::type_list; + using first_range = typename etl::type_list_type_at_index_t; + using value_type = typename etl::ranges::private_ranges::iterator_trait::value_type; + using reference = typename etl::ranges::private_ranges::iterator_trait::reference; + using difference_type = ptrdiff_t; + + using iterator_variant_type = typename concat_view::iterator_variant_type; + + concat_iterator(size_t index, concat_view& view, iterator_variant_type current): _ranges_index{index}, _view(view), _current(current) + { + } + + concat_iterator(const concat_iterator& other) = default; + + constexpr reference operator*() const + { + return _view.get_value(_ranges_index, _current); + } + + constexpr decltype(auto) operator[] (difference_type pos) const + { + auto tmp = *this; + if (pos > 0) + { + for (difference_type i = 0; i < pos; ++i) + { + tmp._view.advance(tmp._ranges_index, tmp._current, 1); + } + } + if (pos < 0) + { + for (difference_type i = 0; i < -pos; ++i) + { + tmp._view.advance(tmp._ranges_index, tmp._current, -1); + } + } + return *tmp; + } + + constexpr concat_iterator& operator++() + { + _view.advance(_ranges_index, _current, 1); + return *this; + } + + constexpr concat_iterator operator++(int) + { + auto result = *this; + _view.advance(_ranges_index, _current, 1); + return result; + } + + constexpr concat_iterator& operator--() + { + _view.advance(_ranges_index, _current, -1); + return *this; + } + + constexpr concat_iterator operator--(int) + { + auto result = *this; + _view.advance(_ranges_index, _current, -1); + return result; + } + + constexpr concat_iterator& operator+=(difference_type n) + { + for (difference_type i = 0; i < n; ++i) + { + _view.advance(_ranges_index, _current, 1); + } + return *this; + } + + constexpr concat_iterator& operator-=(difference_type n) + { + for (difference_type i = 0; i < n; ++i) + { + _view.advance(_ranges_index, _current, -1); + } + return *this; + } + + friend constexpr bool operator==(const concat_iterator& x, etl::default_sentinel_t) + { + return x._ranges_index == x._view.number_of_ranges - 1 && + etl::get(x._current) == etl::get(x._view).end(); + } + + friend constexpr bool operator==(const concat_iterator&x, const concat_iterator&y) + { + return x._ranges_index == y._ranges_index && x._current.index() == y._current.index() && x._current == y._current; + } + + friend constexpr bool operator!=(const concat_iterator& x, etl::default_sentinel_t) + { + return !(x == etl::default_sentinel); + } + + friend constexpr bool operator!=(const concat_iterator&x, const concat_iterator&y) + { + return !(x == y); + } + + private: + size_t _ranges_index; + const concat_view& _view; + iterator_variant_type _current; + }; + + template + class concat_view: public etl::ranges::view_interface> + { + static_assert(sizeof...(Ranges) > 0, "Type list must be non-empty"); + + public: + using types = typename etl::type_list; + using first_range = typename etl::type_list_type_at_index_t; + using value_type = typename etl::ranges::private_ranges::iterator_trait::value_type; + using reference = typename etl::ranges::private_ranges::iterator_trait::reference; + using iterator = concat_iterator; + using const_iterator = concat_iterator; + using difference_type = typename etl::make_signed_t; + + using iterator_variant_type = etl::ranges::private_ranges::mini_variant::iterator ...>; + + using get_value_delegates_type = reference(*)(const iterator_variant_type& /*current*/); + using advance_delegates_type = void(*)(size_t& /*index*/, const etl::tuple& /*r*/, iterator_variant_type&/*current*/, difference_type/*n*/); + + static constexpr const size_t number_of_ranges = sizeof...(Ranges); + + constexpr concat_view(Ranges&&... r) + : _r{etl::move(r)...} + { + set_delegates(); + } + + concat_view(const concat_view& other) = default; + + constexpr iterator begin() + { + iterator_variant_type current; + current.template emplace<0>(etl::get<0>(_r).begin()); + return iterator{0, *this, current}; + } + + constexpr iterator end() + { + iterator_variant_type current; + current.template emplace(etl::get(_r).end()); + return iterator{number_of_ranges - 1, *this, current}; + } + + constexpr size_t size() const + { + return get_size(); + } + + private: + template + friend class concat_iterator; + + template + constexpr size_t get_size() const + { + if constexpr (n < etl::tuple_size_v) + { + return etl::get(_r).size() + get_size(); + } + else + { + return 0; + } + } + + // helper to advance iterator index+iterator variant + void advance(size_t& index, iterator_variant_type& current, difference_type n) const + { + advance_delegates[index](index, _r, current, n); + } + + template + void set_delegates() + { + if constexpr(i < number_of_ranges) + { + advance_delegates[i] = [](size_t& index, const etl::tuple& r, iterator_variant_type& current, difference_type n) + { + if (n > 0) + { + auto end = etl::get(r).end(); + auto& it = etl::get(current); + if (it != end) + { + ++it; + } + if (it == end) + { + if constexpr (i + 1 < number_of_ranges) + { + current.template emplace(etl::get(r).begin()); + index = i + 1; + } + else + { + // at end of last range + ETL_ASSERT(it == end && i + 1 == number_of_ranges, ETL_ERROR_GENERIC("Wrong iterator state at end")); + } + } + } + if (n < 0) + { + auto begin = etl::get(r).begin(); + auto& it = etl::get(current); + if (it == begin) + { + if constexpr (i > 0) + { + current.template emplace(etl::get(r).end()); + index = i - 1; + + auto begin2 = etl::get(r).begin(); + auto& it2 = etl::get(current); + if (it2 != begin2) + { + --it2; + } + } + else + { + // at beginning of first range + ETL_ASSERT(it == begin && i == 0, ETL_ERROR_GENERIC("Wrong iterator state at begin")); + } + } + else + { + it--; + } + } + }; + + get_value_delegates[i] = [](const iterator_variant_type& current) -> reference { + return *etl::get(current); + }; + + set_delegates(); + } + } + + reference get_value(size_t index, const iterator_variant_type& current) const + { + return get_value_delegates[index](current); + } + + etl::tuple _r; + get_value_delegates_type get_value_delegates[number_of_ranges]; + advance_delegates_type advance_delegates[number_of_ranges]; + }; + + template + concat_view(Ranges&& ...) -> concat_view...>; + + struct concat_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = concat_view; + + constexpr concat_range_adapter_closure() = default; + + template + constexpr auto operator()(Ranges&&... r) const + { + return concat_view(views::all(etl::forward(r))...); + } + }; + + namespace views + { + namespace private_views + { + struct concat + { + template + constexpr auto operator()(Ranges&&... r) const + { + return concat_view(views::all(etl::forward(r))...); + } + }; + } + + inline constexpr private_views::concat concat; + } + + //************************************************************************* + /// zip_iterator + /// An iterator adaptor that iterates over multiple ranges simultaneously, + /// producing etl::tuple of references to the current elements. + //************************************************************************* + template + class zip_iterator + { + static_assert(sizeof...(Ranges) > 0, "Type list must be non-empty"); + + public: + using iterators_type = etl::tuple::const_iterator...>; + using value_type = etl::tuple::value_type...>; + using difference_type = ptrdiff_t; + using pointer = const value_type*; + using reference = value_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + constexpr zip_iterator(iterators_type iters): _iters(iters) + { + } + + constexpr zip_iterator(const zip_iterator& other) = default; + + constexpr zip_iterator& operator=(const zip_iterator& other) = default; + + constexpr zip_iterator& operator++() + { + increment(etl::make_index_sequence{}); + return *this; + } + + constexpr zip_iterator operator++(int) + { + zip_iterator tmp = *this; + ++(*this); + return tmp; + } + + constexpr value_type operator*() const + { + return deref(etl::make_index_sequence{}); + } + + friend constexpr bool operator==(const zip_iterator& lhs, const zip_iterator& rhs) + { + return lhs.any_equal(rhs, etl::make_index_sequence{}); + } + + friend constexpr bool operator!=(const zip_iterator& lhs, const zip_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + template + constexpr void increment(etl::index_sequence) + { + ((++etl::get(_iters)), ...); + } + + template + constexpr value_type deref(etl::index_sequence) const + { + return value_type(*etl::get(_iters)...); + } + + // zip terminates when ANY iterator reaches its end (shortest range semantics) + template + constexpr bool any_equal(const zip_iterator& other, etl::index_sequence) const + { + return ((etl::get(_iters) == etl::get(other._iters)) || ...); + } + + iterators_type _iters; + }; + + //************************************************************************* + /// zip_view + /// A range adaptor that takes multiple ranges and produces a view of + /// tuples, where the i-th tuple contains the i-th elements from all + /// input ranges. The view has the length of the shortest input range. + //************************************************************************* + template + class zip_view: public etl::ranges::view_interface> + { + static_assert(sizeof...(Ranges) > 0, "Type list must be non-empty"); + + public: + using iterator = zip_iterator; + using const_iterator = zip_iterator; + + constexpr zip_view(Ranges&&... r): _r{etl::move(r)...} + { + } + + zip_view(const zip_view& other) = default; + + constexpr const_iterator begin() const + { + return make_begin(etl::make_index_sequence{}); + } + + constexpr const_iterator end() const + { + return make_end(etl::make_index_sequence{}); + } + + constexpr size_t size() const + { + return get_min_size(etl::make_index_sequence{}); + } + + private: + template + constexpr const_iterator make_begin(etl::index_sequence) const + { + return const_iterator(typename const_iterator::iterators_type(ETL_OR_STD::begin(etl::get(_r))...)); + } + + template + constexpr const_iterator make_end(etl::index_sequence) const + { + return const_iterator(typename const_iterator::iterators_type(ETL_OR_STD::end(etl::get(_r))...)); + } + + template + constexpr size_t get_min_size(etl::index_sequence) const + { + size_t sizes[] = { static_cast(etl::distance(ETL_OR_STD::cbegin(etl::get(_r)), ETL_OR_STD::cend(etl::get(_r))))... }; + size_t min_val = sizes[0]; + for (size_t i = 1; i < sizeof...(Ranges); ++i) + { + if (sizes[i] < min_val) + { + min_val = sizes[i]; + } + } + return min_val; + } + + mutable etl::tuple _r; + }; + + template + zip_view(Ranges&&...) -> zip_view...>; + + namespace views + { + namespace private_views + { + struct zip + { + template + constexpr auto operator()(Ranges&&... r) const + { + return zip_view(views::all(etl::forward(r))...); + } + }; + } + + inline constexpr private_views::zip zip; + } + + //************************************************************************* + /// zip_transform_iterator + /// An iterator that zips multiple ranges together and applies a + /// transformation function to the elements, producing transformed values. + //************************************************************************* + template + class zip_transform_iterator + { + static_assert(sizeof...(Ranges) > 0, "Type list must be non-empty"); + + public: + using iterators_type = etl::tuple::const_iterator...>; + using value_type = etl::invoke_result_t::value_type...>; + using difference_type = ptrdiff_t; + using pointer = const value_type*; + using reference = value_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + constexpr zip_transform_iterator(Fun f, iterators_type iters): _f(f), _iters(iters) + { + } + + constexpr zip_transform_iterator(const zip_transform_iterator& other) = default; + + constexpr zip_transform_iterator& operator=(const zip_transform_iterator& other) = default; + + constexpr zip_transform_iterator& operator++() + { + increment(etl::make_index_sequence{}); + return *this; + } + + constexpr zip_transform_iterator operator++(int) + { + zip_transform_iterator tmp = *this; + ++(*this); + return tmp; + } + + constexpr value_type operator*() const + { + return deref(etl::make_index_sequence{}); + } + + friend constexpr bool operator==(const zip_transform_iterator& lhs, const zip_transform_iterator& rhs) + { + return lhs.any_equal(rhs, etl::make_index_sequence{}); + } + + friend constexpr bool operator!=(const zip_transform_iterator& lhs, const zip_transform_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + template + constexpr void increment(etl::index_sequence) + { + ((++etl::get(_iters)), ...); + } + + template + constexpr value_type deref(etl::index_sequence) const + { + return etl::invoke(_f, *etl::get(_iters)...); + } + + // zip terminates when ANY iterator reaches its end (shortest range semantics) + template + constexpr bool any_equal(const zip_transform_iterator& other, etl::index_sequence) const + { + return ((etl::get(_iters) == etl::get(other._iters)) || ...); + } + + Fun _f; + iterators_type _iters; + }; + + //************************************************************************* + /// zip_transform_view + /// A range adaptor that takes a transformation function and multiple + /// ranges, and produces a view whose elements are the result of applying + /// the function to the corresponding elements from all input ranges. + /// The view has the length of the shortest input range. + //************************************************************************* + template + class zip_transform_view: public etl::ranges::view_interface> + { + static_assert(sizeof...(Ranges) > 0, "Type list must be non-empty"); + + public: + using iterator = zip_transform_iterator; + using const_iterator = zip_transform_iterator; + + constexpr zip_transform_view(Fun f, Ranges&&... r): _f{f}, _r{etl::move(r)...} + { + } + + zip_transform_view(const zip_transform_view& other) = default; + + constexpr const_iterator begin() const + { + return make_begin(etl::make_index_sequence{}); + } + + constexpr const_iterator end() const + { + return make_end(etl::make_index_sequence{}); + } + + constexpr size_t size() const + { + return get_min_size(etl::make_index_sequence{}); + } + + private: + template + constexpr const_iterator make_begin(etl::index_sequence) const + { + return const_iterator(_f, typename const_iterator::iterators_type(ETL_OR_STD::begin(etl::get(_r))...)); + } + + template + constexpr const_iterator make_end(etl::index_sequence) const + { + return const_iterator(_f, typename const_iterator::iterators_type(ETL_OR_STD::end(etl::get(_r))...)); + } + + template + constexpr size_t get_min_size(etl::index_sequence) const + { + size_t sizes[] = { static_cast(etl::distance(ETL_OR_STD::cbegin(etl::get(_r)), ETL_OR_STD::cend(etl::get(_r))))... }; + size_t min_val = sizes[0]; + for (size_t i = 1; i < sizeof...(Ranges); ++i) + { + if (sizes[i] < min_val) + { + min_val = sizes[i]; + } + } + return min_val; + } + + Fun _f; + mutable etl::tuple _r; + }; + + template + zip_transform_view(Fun, Ranges&&...) -> zip_transform_view...>; + + namespace views + { + namespace private_views + { + struct zip_transform + { + template + constexpr auto operator()(Fun&& f, Ranges&&... r) const + { + return zip_transform_view(etl::forward(f), views::all(etl::forward(r))...); + } + }; + } + + inline constexpr private_views::zip_transform zip_transform; + } + + //************************************************************************* + /// common_iterator + /// A type-erasing iterator that wraps either an iterator I or a sentinel S, + /// providing a common type for both. Used by common_view. + //************************************************************************* + template + class common_iterator + { + public: + using value_type = typename etl::iterator_traits::value_type; + using difference_type = typename etl::iterator_traits::difference_type; + using pointer = typename etl::iterator_traits::pointer; + using reference = typename etl::iterator_traits::reference; + using iterator_category = ETL_OR_STD::input_iterator_tag; + + constexpr common_iterator(): _is_sentinel{false}, _it{}, _sentinel{} + { + } + + constexpr common_iterator(I it): _is_sentinel{false}, _it{it}, _sentinel{} + { + } + + constexpr common_iterator(S s): _is_sentinel{true}, _it{}, _sentinel{s} + { + } + + constexpr common_iterator(const common_iterator& other) + : _is_sentinel{other._is_sentinel}, _it{other._it}, _sentinel{other._sentinel} + { + } + + constexpr common_iterator& operator=(const common_iterator& other) + { + _is_sentinel = other._is_sentinel; + _it = other._it; + _sentinel = other._sentinel; + return *this; + } + + constexpr decltype(auto) operator*() const + { + return *_it; + } + + constexpr decltype(auto) operator*() + { + return *_it; + } + + constexpr auto operator->() const + { + return &(*_it); + } + + constexpr common_iterator& operator++() + { + ++_it; + return *this; + } + + constexpr common_iterator operator++(int) + { + common_iterator tmp = *this; + ++_it; + return tmp; + } + + friend constexpr bool operator==(const common_iterator& lhs, const common_iterator& rhs) + { + if (lhs._is_sentinel && rhs._is_sentinel) + { + return true; + } + if (!lhs._is_sentinel && !rhs._is_sentinel) + { + return lhs._it == rhs._it; + } + if (lhs._is_sentinel) + { + return rhs._it == lhs._sentinel; + } + return lhs._it == rhs._sentinel; + } + + friend constexpr bool operator!=(const common_iterator& lhs, const common_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + bool _is_sentinel; + I _it; + S _sentinel; + }; + + //************************************************************************* + /// Helper to detect if a range is a "common range" + /// (i.e., begin() and end() return the same type) + //************************************************************************* + namespace private_ranges + { + template + struct is_common_range : etl::false_type {}; + + template + struct is_common_range())), + decltype(ETL_OR_STD::end(etl::declval())) + > + >> : etl::true_type {}; + } + + //************************************************************************* + /// common_view + /// Adapts a view so that its iterator and sentinel types are the same. + /// If the underlying range is already a common range, acts as a simple + /// wrapper (pass-through). Otherwise, wraps with common_iterator. + //************************************************************************* + template::value> + class common_view; + + // Specialization for ranges that are already common (begin/end same type) + template + class common_view: public etl::ranges::view_interface> + { + public: + using iterator = decltype(ETL_OR_STD::begin(etl::declval())); + using const_iterator = decltype(ETL_OR_STD::cbegin(etl::declval())); + using difference_type = typename etl::iterator_traits::difference_type; + + constexpr common_view(Range&& r): _r{etl::move(r)} + { + } + + common_view(const common_view& other) = default; + + constexpr Range base() const& + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r)); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r)); + } + + constexpr size_t size() const + { + return etl::distance(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r)); + } + + private: + Range _r; + }; + + // Specialization for ranges that are NOT common (begin/end differ) + template + class common_view: public etl::ranges::view_interface> + { + public: + using base_iterator = decltype(ETL_OR_STD::begin(etl::declval())); + using base_sentinel = decltype(ETL_OR_STD::end(etl::declval())); + using iterator = common_iterator; + using const_iterator = iterator; + using difference_type = typename etl::iterator_traits::difference_type; + + constexpr common_view(Range&& r): _r{etl::move(r)} + { + } + + common_view(const common_view& other) = default; + + constexpr Range base() const& + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r)); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r)); + } + + private: + Range _r; + }; + + template + common_view(Range&&) -> common_view>; + + struct common_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = common_view; + + common_range_adapter_closure() = default; + + template + constexpr auto operator()(Range&& r) + { + return common_view(views::all(etl::forward(r))); + } + }; + + namespace views + { + namespace private_views + { + struct common + { + template + constexpr auto operator()(Range&& r) const + { + return common_view(views::all(etl::forward(r))); + } + + constexpr auto operator()() const + { + return ranges::common_range_adapter_closure(); + } + }; + } + + inline constexpr private_views::common common; + } + + //************************************************************************* + /// enumerate_iterator + /// An iterator adaptor that pairs each element of the underlying range + /// with its index, producing etl::tuple. + //************************************************************************* + template + class enumerate_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using base_iterator = typename trait::const_iterator; + using base_value_type = typename trait::value_type; + using value_type = etl::tuple; + using difference_type = typename trait::difference_type; + using pointer = const value_type*; + using reference = value_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + enumerate_iterator(base_iterator it, size_t index): _it(it), _index(index) + { + } + + enumerate_iterator(const enumerate_iterator& other): _it{other._it}, _index{other._index} + { + } + + enumerate_iterator& operator++() + { + ++_it; + ++_index; + return *this; + } + + enumerate_iterator operator++(int) + { + enumerate_iterator tmp = *this; + ++_it; + ++_index; + return tmp; + } + + enumerate_iterator& operator=(const enumerate_iterator& other) + { + _it = other._it; + _index = other._index; + return *this; + } + + value_type operator*() const + { + return value_type(_index, *_it); + } + + bool operator==(const enumerate_iterator& other) const + { + return other._it == _it; + } + + bool operator!=(const enumerate_iterator& other) const + { + return !(*this == other); + } + + private: + base_iterator _it; + size_t _index; + }; + + //************************************************************************* + /// enumerate_view + /// A range adaptor that pairs each element of the underlying range + /// with its index, producing a view of etl::tuple. + //************************************************************************* + template + class enumerate_view: public etl::ranges::view_interface> + { + public: + using iterator = enumerate_iterator; + using const_iterator = enumerate_iterator; + + enumerate_view(Range&& r): _r{etl::move(r)} + { + } + + enumerate_view(const enumerate_view& other) = default; + + constexpr Range& base() const& + { + return _r; + } + + constexpr const_iterator begin() const + { + return const_iterator(ETL_OR_STD::begin(_r), 0); + } + + constexpr const_iterator end() const + { + return const_iterator(ETL_OR_STD::end(_r), etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r))); + } + + constexpr size_t size() const + { + return etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r)); + } + + private: + mutable Range _r; + }; + + template + enumerate_view(Range&&) -> enumerate_view>; + + struct enumerate_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = enumerate_view; + + enumerate_range_adapter_closure() = default; + + template + constexpr auto operator()(Range&& r) + { + return enumerate_view(views::all(etl::forward(r))); + } + }; + + namespace views + { + namespace private_views + { + struct enumerate + { + template + constexpr auto operator()(Range&& r) const + { + return enumerate_view(views::all(etl::forward(r))); + } + + constexpr auto operator()() const + { + return ranges::enumerate_range_adapter_closure(); + } + }; + } + + inline constexpr private_views::enumerate enumerate; + } + + //************************************************************************* + /// elements_iterator + /// An iterator adaptor that extracts the Nth element from a tuple-like + /// value type using etl::get or std::get (found via ADL). + //************************************************************************* + template + class elements_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using base_iterator = typename trait::const_iterator; + using base_value_type = typename trait::value_type; + using value_type = etl::tuple_element_t; + using difference_type = typename trait::difference_type; + using pointer = const value_type*; + using reference = const value_type&; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + elements_iterator(base_iterator it): _it(it) + { + } + + elements_iterator(const elements_iterator& other): _it{other._it} + { + } + + elements_iterator& operator++() + { + ++_it; + return *this; + } + + elements_iterator operator++(int) + { + elements_iterator tmp = *this; + _it++; + return tmp; + } + + elements_iterator& operator=(const elements_iterator& other) + { + _it = other._it; + return *this; + } + + decltype(auto) operator*() const + { + using etl::get; + return get(*_it); + } + + bool operator==(const elements_iterator& other) const + { + return other._it == _it; + } + + bool operator!=(const elements_iterator& other) const + { + return !(*this == other); + } + + private: + base_iterator _it; + }; + + //************************************************************************* + /// elements_view + /// A range adaptor that takes a view of tuple-like values and produces + /// a view of the Nth element of each tuple-like value. + //************************************************************************* + template + class elements_view: public etl::ranges::view_interface> + { + public: + using iterator = elements_iterator; + using const_iterator = elements_iterator; + + elements_view(Range&& r): _r{etl::move(r)} + { + } + + elements_view(const elements_view& other) = default; + + constexpr Range& base() const& + { + return _r; + } + + constexpr const_iterator begin() const + { + return const_iterator(ETL_OR_STD::begin(_r)); + } + + constexpr const_iterator end() const + { + return const_iterator(ETL_OR_STD::end(_r)); + } + + constexpr size_t size() const + { + return etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r)); + } + + private: + mutable Range _r; + }; + + template + elements_view(Range&&, etl::integral_constant) -> elements_view, N>; + + template + struct elements_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = elements_view; + + elements_range_adapter_closure() = default; + + template + constexpr auto operator()(Range&& r) + { + return elements_view, N>(views::all(etl::forward(r))); + } + }; + + /// keys_view is an alias for elements_view with N=0. + template + using keys_view = elements_view; + + /// values_view is an alias for elements_view with N=1. + template + using values_view = elements_view; + + namespace views + { + namespace private_views + { + template + struct elements_fn + { + template + constexpr auto operator()(Range&& r) const + { + return elements_view, N>(views::all(etl::forward(r))); + } + + constexpr auto operator()() const + { + return ranges::elements_range_adapter_closure(); + } + }; + } + + template + inline constexpr private_views::elements_fn elements{}; + + inline constexpr private_views::elements_fn<0> keys{}; + inline constexpr private_views::elements_fn<1> values{}; + } + + //************************************************************************* + /// Helper: create a tuple type that repeats T exactly N times. + //************************************************************************* + namespace private_ranges + { + template> + struct repeat_tuple; + + template + struct repeat_tuple> + { + template + using always = T; + + using type = etl::tuple...>; + }; + + template + using repeat_tuple_t = typename repeat_tuple::type; + + /// Helper: compute invoke_result_t with T repeated N times. + template> + struct repeat_invoke_result; + + template + struct repeat_invoke_result> + { + template + using always = T; + + using type = etl::invoke_result_t...>; + }; + + template + using repeat_invoke_result_t = typename repeat_invoke_result::type; + } + + //************************************************************************* + /// adjacent_iterator + /// An iterator adaptor that produces tuples of N consecutive elements + /// from the underlying range. Each increment advances all N internal + /// iterators by one position. + //************************************************************************* + template + class adjacent_iterator + { + static_assert(N > 0, "adjacent window size must be > 0"); + + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using base_iterator = typename trait::const_iterator; + using base_value_type = typename trait::value_type; + using value_type = private_ranges::repeat_tuple_t; + using difference_type = typename trait::difference_type; + using pointer = const value_type*; + using reference = value_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + /// Construct from an array of N iterators (the sliding window). + template + constexpr adjacent_iterator(base_iterator first, base_iterator last, etl::index_sequence) + : _iters{advance_copy(first, last, Is)...} + , _end{last} + { + } + + constexpr adjacent_iterator(const adjacent_iterator& other) = default; + + constexpr adjacent_iterator& operator=(const adjacent_iterator& other) = default; + + constexpr adjacent_iterator& operator++() + { + increment(etl::make_index_sequence{}); + return *this; + } + + constexpr adjacent_iterator operator++(int) + { + adjacent_iterator tmp = *this; + ++(*this); + return tmp; + } + + constexpr value_type operator*() const + { + return deref(etl::make_index_sequence{}); + } + + friend constexpr bool operator==(const adjacent_iterator& lhs, const adjacent_iterator& rhs) + { + // Compare the last iterator in the window (index N-1). + // When it reaches end, the window is exhausted. + return lhs._iters[N - 1] == rhs._iters[N - 1]; + } + + friend constexpr bool operator!=(const adjacent_iterator& lhs, const adjacent_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + static constexpr base_iterator advance_copy(base_iterator it, base_iterator last, size_t n) + { + for (size_t i = 0; i < n && it != last; ++i) + { + ++it; + } + return it; + } + + template + constexpr void increment(etl::index_sequence) + { + ((void)((_iters[Is] != _end) ? (void)++_iters[Is] : (void)0), ...); + } + + template + constexpr value_type deref(etl::index_sequence) const + { + return value_type(*_iters[Is]...); + } + + base_iterator _iters[N]; + base_iterator _end; + }; + + //************************************************************************* + /// adjacent_view + /// A range adaptor that takes a range and a compile-time window size N + /// and produces a view of tuples, where the i-th tuple contains the + /// elements at positions [i, i+1, ..., i+N-1] from the underlying range. + /// The resulting view has (size - N + 1) elements, or is empty if the + /// underlying range has fewer than N elements. + //************************************************************************* + template + class adjacent_view: public etl::ranges::view_interface> + { + static_assert(N > 0, "adjacent window size must be > 0"); + + public: + using iterator = adjacent_iterator; + using const_iterator = adjacent_iterator; + + constexpr adjacent_view(Range&& r): _r{etl::move(r)} + { + } + + adjacent_view(const adjacent_view& other) = default; + + constexpr Range& base() const& + { + return _r; + } + + constexpr const_iterator begin() const + { + return const_iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + } + + constexpr const_iterator end() const + { + // The end iterator has all N internal iterators at the end position. + return const_iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + } + + constexpr size_t size() const + { + auto total = static_cast(etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r))); + return (total >= N) ? (total - N + 1) : 0; + } + + private: + mutable Range _r; + }; + + template + adjacent_view(Range&&, etl::integral_constant) -> adjacent_view, N>; + + template + struct adjacent_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = adjacent_view; + + adjacent_range_adapter_closure() = default; + + template + constexpr auto operator()(Range&& r) + { + return adjacent_view, N>(views::all(etl::forward(r))); + } + }; + + namespace views + { + namespace private_views + { + template + struct adjacent_fn + { + template + constexpr auto operator()(Range&& r) const + { + return adjacent_view, N>(views::all(etl::forward(r))); + } + + constexpr auto operator()() const + { + return ranges::adjacent_range_adapter_closure(); + } + }; + } + + template + inline constexpr private_views::adjacent_fn adjacent{}; + + /// pairwise is an alias for adjacent<2>. + inline constexpr private_views::adjacent_fn<2> pairwise{}; + } + + //************************************************************************* + /// adjacent_transform_iterator + /// An iterator adaptor that takes a sliding window of N consecutive + /// elements from the underlying range and applies a transformation + /// function to them, producing a single value per window position. + //************************************************************************* + template + class adjacent_transform_iterator + { + static_assert(N > 0, "adjacent window size must be > 0"); + + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using base_iterator = typename trait::const_iterator; + using base_value_type = typename trait::value_type; + using value_type = private_ranges::repeat_invoke_result_t; + using difference_type = typename trait::difference_type; + using pointer = const value_type*; + using reference = value_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + /// Construct from a starting iterator, end sentinel, index sequence, and a function. + template + constexpr adjacent_transform_iterator(Fun f, base_iterator first, base_iterator last, etl::index_sequence) + : _f{f} + , _iters{advance_copy(first, last, Is)...} + , _end{last} + { + } + + constexpr adjacent_transform_iterator(const adjacent_transform_iterator& other) = default; + + constexpr adjacent_transform_iterator& operator=(const adjacent_transform_iterator& other) = default; + + constexpr adjacent_transform_iterator& operator++() + { + increment(etl::make_index_sequence{}); + return *this; + } + + constexpr adjacent_transform_iterator operator++(int) + { + adjacent_transform_iterator tmp = *this; + ++(*this); + return tmp; + } + + constexpr value_type operator*() const + { + return deref(etl::make_index_sequence{}); + } + + friend constexpr bool operator==(const adjacent_transform_iterator& lhs, const adjacent_transform_iterator& rhs) + { + // Compare the last iterator in the window (index N-1). + // When it reaches end, the window is exhausted. + return lhs._iters[N - 1] == rhs._iters[N - 1]; + } + + friend constexpr bool operator!=(const adjacent_transform_iterator& lhs, const adjacent_transform_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + static constexpr base_iterator advance_copy(base_iterator it, base_iterator last, size_t n) + { + for (size_t i = 0; i < n && it != last; ++i) + { + ++it; + } + return it; + } + + template + constexpr void increment(etl::index_sequence) + { + ((void)((_iters[Is] != _end) ? (void)++_iters[Is] : (void)0), ...); + } + + template + constexpr value_type deref(etl::index_sequence) const + { + return etl::invoke(_f, *_iters[Is]...); + } + + Fun _f; + base_iterator _iters[N]; + base_iterator _end; + }; + + //************************************************************************* + /// adjacent_transform_view + /// A range adaptor that takes a range, a compile-time window size N, and + /// a transformation function, and produces a view whose elements are the + /// result of applying the function to each sliding window of N consecutive + /// elements from the underlying range. + /// The resulting view has (size - N + 1) elements, or is empty if the + /// underlying range has fewer than N elements. + //************************************************************************* + template + class adjacent_transform_view: public etl::ranges::view_interface> + { + static_assert(N > 0, "adjacent window size must be > 0"); + + public: + using iterator = adjacent_transform_iterator; + using const_iterator = adjacent_transform_iterator; + + constexpr adjacent_transform_view(Fun f, Range&& r): _f{f}, _r{etl::move(r)} + { + } + + adjacent_transform_view(const adjacent_transform_view& other) = default; + + constexpr Range& base() const& + { + return _r; + } + + constexpr const_iterator begin() const + { + return const_iterator(_f, ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + } + + constexpr const_iterator end() const + { + // The end iterator has all N internal iterators at the end position. + return const_iterator(_f, ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), etl::make_index_sequence{}); + } + + constexpr size_t size() const + { + auto total = static_cast(etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r))); + return (total >= N) ? (total - N + 1) : 0; + } + + private: + Fun _f; + mutable Range _r; + }; + + template + adjacent_transform_view(Fun, Range&&, etl::integral_constant) -> adjacent_transform_view, Fun, N>; + + template + struct adjacent_transform_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = adjacent_transform_view; + + adjacent_transform_range_adapter_closure(Fun f): _f{f} + { + } + + template + constexpr auto operator()(Range&& r) + { + return adjacent_transform_view, Fun, N>(_f, views::all(etl::forward(r))); + } + + Fun _f; + }; + + namespace views + { + namespace private_views + { + template + struct adjacent_transform_fn + { + template + constexpr auto operator()(Range&& r, Fun&& f) const + { + return adjacent_transform_view, etl::decay_t, N>(etl::forward(f), views::all(etl::forward(r))); + } + + template + constexpr auto operator()(Fun&& f) const + { + return ranges::adjacent_transform_range_adapter_closure>(etl::forward(f)); + } + }; + } + + template + inline constexpr private_views::adjacent_transform_fn adjacent_transform{}; + + /// pairwise_transform is an alias for adjacent_transform<2>. + inline constexpr private_views::adjacent_transform_fn<2> pairwise_transform{}; + } + + //************************************************************************* + /// chunk_iterator: an iterator that yields subrange chunks of a range. + //************************************************************************* + template + class chunk_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using inner_iterator = typename trait::iterator; + using const_inner_iterator = typename trait::const_iterator; + using difference_type = typename trait::difference_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + using value_type = etl::ranges::subrange; + using pointer = value_type*; + using reference = value_type; + + chunk_iterator(const_inner_iterator it, const_inner_iterator it_end, difference_type chunk_size) + : _it(it) + , _it_end(it_end) + , _chunk_size(chunk_size) + { + } + + chunk_iterator(const chunk_iterator& other) = default; + + chunk_iterator& operator=(const chunk_iterator& other) = default; + + chunk_iterator& operator++() + { + difference_type remaining = etl::distance(_it, _it_end); + difference_type step = (_chunk_size < remaining) ? _chunk_size : remaining; + etl::advance(_it, step); + return *this; + } + + chunk_iterator operator++(int) + { + chunk_iterator tmp{*this}; + ++(*this); + return tmp; + } + + value_type operator*() const + { + difference_type remaining = etl::distance(_it, _it_end); + difference_type step = (_chunk_size < remaining) ? _chunk_size : remaining; + const_inner_iterator chunk_end = _it; + etl::advance(chunk_end, step); + return value_type(_it, chunk_end); + } + + constexpr bool operator==(const chunk_iterator& other) const + { + return _it == other._it; + } + + constexpr bool operator!=(const chunk_iterator& other) const + { + return !(*this == other); + } + + private: + const_inner_iterator _it; + const_inner_iterator _it_end; + difference_type _chunk_size; + }; + + //************************************************************************* + /// chunk_view: splits a range into non-overlapping chunks of a given size. + /// The last chunk may be smaller if the range size is not evenly divisible + /// by the chunk size. + //************************************************************************* + template + class chunk_view: public etl::ranges::view_interface> + { + public: + using iterator = chunk_iterator; + using const_iterator = chunk_iterator; + using difference_type = typename etl::ranges::private_ranges::iterator_trait::difference_type; + + constexpr chunk_view(Range&& r, difference_type chunk_size) + : _r{etl::move(r)}, _chunk_size{chunk_size} + { + } + + chunk_view(const chunk_view& other) = default; + + constexpr Range base() const& + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), _chunk_size); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), _chunk_size); + } + + private: + Range _r; + difference_type _chunk_size; + }; + + template + chunk_view(Range&&, typename etl::ranges::private_ranges::iterator_trait::difference_type) -> chunk_view>; + + struct chunk_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = chunk_view; + + template + constexpr chunk_range_adapter_closure(DifferenceType chunk_size): _chunk_size{static_cast(chunk_size)} + { + } + + template + constexpr auto operator()(Range&& r) const + { + return chunk_view(views::all(etl::forward(r)), _chunk_size); + } + + const size_t _chunk_size; + }; + + namespace views + { + namespace private_views + { + struct chunk + { + template + constexpr auto operator()(Range&& r, ranges::range_difference_t chunk_size) const + { + return chunk_view(views::all(etl::forward(r)), chunk_size); + } + + template + constexpr auto operator()(DifferenceType chunk_size) const + { + return ranges::chunk_range_adapter_closure(chunk_size); + } + }; + } + + inline constexpr private_views::chunk chunk; + } + + //************************************************************************* + /// slide_iterator: an iterator that yields overlapping subrange windows + /// of a given size from the underlying range. + //************************************************************************* + template + class slide_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using inner_iterator = typename trait::iterator; + using const_inner_iterator = typename trait::const_iterator; + using difference_type = typename trait::difference_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + using value_type = etl::ranges::subrange; + using pointer = value_type*; + using reference = value_type; + + slide_iterator(const_inner_iterator it, const_inner_iterator it_end, difference_type window_size) + : _it(it) + , _it_end(it_end) + , _window_size(window_size) + { + } + + slide_iterator(const slide_iterator& other) = default; + + slide_iterator& operator=(const slide_iterator& other) = default; + + slide_iterator& operator++() + { + ++_it; + return *this; + } + + slide_iterator operator++(int) + { + slide_iterator tmp{*this}; + ++(*this); + return tmp; + } + + value_type operator*() const + { + const_inner_iterator window_end = _it; + etl::advance(window_end, _window_size); + return value_type(_it, window_end); + } + + constexpr bool operator==(const slide_iterator& other) const + { + return _it == other._it; + } + + constexpr bool operator!=(const slide_iterator& other) const + { + return !(*this == other); + } + + private: + const_inner_iterator _it; + const_inner_iterator _it_end; + difference_type _window_size; + }; + + //************************************************************************* + /// slide_view: produces a view of overlapping subranges (sliding windows) + /// of a given size from the underlying range. + /// For a range of size S and window size N, the resulting view has + /// max(S - N + 1, 0) elements. Each element is a subrange of N + /// consecutive elements from the underlying range. + //************************************************************************* + template + class slide_view: public etl::ranges::view_interface> + { + public: + using iterator = slide_iterator; + using const_iterator = slide_iterator; + using difference_type = typename etl::ranges::private_ranges::iterator_trait::difference_type; + + constexpr slide_view(Range&& r, difference_type window_size) + : _r{etl::move(r)}, _window_size{window_size} + { + } + + slide_view(const slide_view& other) = default; + + constexpr Range base() const& + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), _window_size); + } + + constexpr iterator end() const + { + auto total = static_cast(etl::distance(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r))); + if (total < _window_size) + { + // Empty view: begin == end + return iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), _window_size); + } + auto end_it = ETL_OR_STD::begin(_r); + etl::advance(end_it, total - _window_size + 1); + return iterator(end_it, ETL_OR_STD::end(_r), _window_size); + } + + constexpr size_t size() const + { + auto total = static_cast(etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r))); + return (total >= static_cast(_window_size)) ? (total - static_cast(_window_size) + 1) : 0; + } + + private: + Range _r; + difference_type _window_size; + }; + + template + slide_view(Range&&, typename etl::ranges::private_ranges::iterator_trait::difference_type) -> slide_view>; + + struct slide_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = slide_view; + + template + constexpr slide_range_adapter_closure(DifferenceType window_size): _window_size{static_cast(window_size)} + { + } + + template + constexpr auto operator()(Range&& r) const + { + return slide_view(views::all(etl::forward(r)), _window_size); + } + + const size_t _window_size; + }; + + namespace views + { + namespace private_views + { + struct slide + { + template + constexpr auto operator()(Range&& r, ranges::range_difference_t window_size) const + { + return slide_view(views::all(etl::forward(r)), window_size); + } + + template + constexpr auto operator()(DifferenceType window_size) const + { + return ranges::slide_range_adapter_closure(window_size); + } + }; + } + + inline constexpr private_views::slide slide; + } + + //************************************************************************* + /// chunk_by_iterator: an iterator that yields subrange chunks of a range, + /// where chunk boundaries are determined by a binary predicate. + /// A new chunk starts whenever the predicate returns false for an + /// adjacent pair of elements. + //************************************************************************* + template + class chunk_by_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using inner_iterator = typename trait::iterator; + using const_inner_iterator = typename trait::const_iterator; + using difference_type = typename trait::difference_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + using value_type = etl::ranges::subrange; + using pointer = value_type*; + using reference = value_type; + + chunk_by_iterator(const_inner_iterator it, const_inner_iterator it_end, const Pred& pred) + : _it(it) + , _it_end(it_end) + , _pred(pred) + { + _chunk_end = find_next_chunk_end(); + } + + chunk_by_iterator(const chunk_by_iterator& other) = default; + + chunk_by_iterator& operator=(const chunk_by_iterator& other) = default; + + chunk_by_iterator& operator++() + { + _it = _chunk_end; + _chunk_end = find_next_chunk_end(); + return *this; + } + + chunk_by_iterator operator++(int) + { + chunk_by_iterator tmp{*this}; + ++(*this); + return tmp; + } + + value_type operator*() const + { + return value_type(_it, _chunk_end); + } + + constexpr bool operator==(const chunk_by_iterator& other) const + { + return _it == other._it; + } + + constexpr bool operator!=(const chunk_by_iterator& other) const + { + return !(*this == other); + } + + private: + const_inner_iterator find_next_chunk_end() const + { + if (_it == _it_end) + { + return _it_end; + } + + const_inner_iterator it_prev = _it; + const_inner_iterator it_curr = _it; + ++it_curr; + + while (it_curr != _it_end) + { + if (!_pred(*it_prev, *it_curr)) + { + return it_curr; + } + it_prev = it_curr; + ++it_curr; + } + + return _it_end; + } + + const_inner_iterator _it; + const_inner_iterator _it_end; + const_inner_iterator _chunk_end; + Pred _pred; + }; + + //************************************************************************* + /// chunk_by_view: splits a range into subranges between adjacent elements + /// for which the given binary predicate returns false. + /// Each chunk is a maximal subrange of consecutive elements where the + /// predicate holds for every adjacent pair. + //************************************************************************* + template + class chunk_by_view: public etl::ranges::view_interface> + { + public: + using iterator = chunk_by_iterator; + using const_iterator = chunk_by_iterator; + + chunk_by_view(Range&& r, const Pred& pred) + : _r{etl::move(r)}, _pred{pred} + { + } + + chunk_by_view(const chunk_by_view& other) = default; + + constexpr Range base() const& + { + return _r; + } + + constexpr const Pred& pred() const + { + return _pred; + } + + constexpr const_iterator begin() const + { + return const_iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), _pred); + } + + constexpr const_iterator end() const + { + return const_iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), _pred); + } + + private: + Range _r; + const Pred _pred; + }; + + template + chunk_by_view(Range&&, Pred) -> chunk_by_view, Pred>; + + template + struct chunk_by_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = chunk_by_view; + + chunk_by_range_adapter_closure(const Pred& p): _p{p} + { + } + + template + constexpr auto operator()(Range&& r) const + { + return chunk_by_view(views::all(etl::forward(r)), _p); + } + + const Pred _p; + }; + + namespace views + { + namespace private_views + { + struct chunk_by + { + template + constexpr auto operator()(Range&& r, const Pred& p) const + { + return chunk_by_view(views::all(etl::forward(r)), p); + } + + template + constexpr auto operator()(const Pred& p) const + { + return ranges::chunk_by_range_adapter_closure(p); + } + }; + } + + inline constexpr private_views::chunk_by chunk_by; + } + + //************************************************************************* + /// stride_iterator: an iterator adaptor that advances the underlying + /// iterator by a fixed stride on each increment. + //************************************************************************* + template + class stride_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using inner_iterator = typename trait::iterator; + using const_inner_iterator = typename trait::const_iterator; + using difference_type = typename trait::difference_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + using value_type = typename trait::value_type; + using pointer = typename trait::pointer; + using reference = typename trait::reference; + + constexpr stride_iterator(const_inner_iterator it, const_inner_iterator it_end, difference_type stride_n) + : _it(it) + , _it_end(it_end) + , _stride_n(stride_n) + { + } + + stride_iterator(const stride_iterator& other) = default; + + stride_iterator& operator=(const stride_iterator& other) = default; + + constexpr stride_iterator& operator++() + { + difference_type remaining = etl::distance(_it, _it_end); + difference_type step = (_stride_n < remaining) ? _stride_n : remaining; + etl::advance(_it, step); + return *this; + } + + constexpr stride_iterator operator++(int) + { + stride_iterator tmp{*this}; + ++(*this); + return tmp; + } + + constexpr auto operator*() const + { + return *_it; + } + + constexpr auto operator->() const + { + return &(*_it); + } + + constexpr bool operator==(const stride_iterator& other) const + { + return _it == other._it; + } + + constexpr bool operator!=(const stride_iterator& other) const + { + return !(*this == other); + } + + private: + mutable const_inner_iterator _it; + const_inner_iterator _it_end; + difference_type _stride_n; + }; + + //************************************************************************* + /// stride_view: a range adaptor that yields every Nth element from the + /// underlying range, starting with the first element. + //************************************************************************* + template + class stride_view: public etl::ranges::view_interface> + { + public: + using iterator = stride_iterator; + using const_iterator = stride_iterator; + using difference_type = typename etl::ranges::private_ranges::iterator_trait::difference_type; + + constexpr stride_view(Range&& r, difference_type stride_n) + : _r{etl::move(r)}, _stride_n{stride_n} + { + } + + stride_view(const stride_view& other) = default; + + constexpr Range base() const& + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r), ETL_OR_STD::end(_r), _stride_n); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r), ETL_OR_STD::end(_r), _stride_n); + } + + private: + Range _r; + difference_type _stride_n; + }; + + template + stride_view(Range&&, typename etl::ranges::private_ranges::iterator_trait::difference_type) -> stride_view>; + + struct stride_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = stride_view; + + template + constexpr stride_range_adapter_closure(DifferenceType stride_n): _stride_n{static_cast(stride_n)} + { + } + + template + constexpr auto operator()(Range&& r) const + { + return stride_view(views::all(etl::forward(r)), _stride_n); + } + + const size_t _stride_n; + }; + + namespace views + { + namespace private_views + { + struct stride + { + template + constexpr auto operator()(Range&& r, ranges::range_difference_t stride_n) const + { + return stride_view(views::all(etl::forward(r)), stride_n); + } + + template + constexpr auto operator()(DifferenceType stride_n) const + { + return ranges::stride_range_adapter_closure(stride_n); + } + }; + } + + inline constexpr private_views::stride stride; + } + + //************************************************************************* + /// cartesian_product_iterator + /// An iterator adaptor that iterates over the Cartesian product of + /// multiple ranges, producing etl::tuple of values from each range. + /// The iteration order is lexicographic with the last range varying + /// fastest (like nested for loops). + //************************************************************************* + template + class cartesian_product_iterator + { + static_assert(sizeof...(Ranges) > 0, "Type list must be non-empty"); + + public: + using iterators_type = etl::tuple::const_iterator...>; + using value_type = etl::tuple::value_type...>; + using difference_type = ptrdiff_t; + using pointer = const value_type*; + using reference = value_type; + + using iterator_category = ETL_OR_STD::forward_iterator_tag; + + constexpr cartesian_product_iterator(iterators_type current, iterators_type begins, iterators_type ends, bool is_end = false) + : _current(current) + , _begins(begins) + , _ends(ends) + , _is_end(is_end) + { + } + + constexpr cartesian_product_iterator(const cartesian_product_iterator& other) = default; + + constexpr cartesian_product_iterator& operator=(const cartesian_product_iterator& other) = default; + + constexpr cartesian_product_iterator& operator++() + { + increment(); + return *this; + } + + constexpr cartesian_product_iterator operator++(int) + { + cartesian_product_iterator tmp = *this; + ++(*this); + return tmp; + } + + constexpr value_type operator*() const + { + return deref(etl::make_index_sequence{}); + } + + friend constexpr bool operator==(const cartesian_product_iterator& lhs, const cartesian_product_iterator& rhs) + { + return lhs._is_end == rhs._is_end && (lhs._is_end || lhs.all_equal(rhs, etl::make_index_sequence{})); + } + + friend constexpr bool operator!=(const cartesian_product_iterator& lhs, const cartesian_product_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + // Increment with carry: increment the last range, and carry over to the previous range when a range wraps around + constexpr void increment() + { + if (_is_end) return; + increment_at(); + } + + template + constexpr etl::enable_if_t<(I > 0)> increment_at() + { + auto& it = etl::get(_current); + ++it; + if (it == etl::get(_ends)) + { + it = etl::get(_begins); + increment_at(); + } + } + + template + constexpr etl::enable_if_t<(I == 0)> increment_at() + { + auto& it = etl::get<0>(_current); + ++it; + if (it == etl::get<0>(_ends)) + { + _is_end = true; + } + } + + template + constexpr value_type deref(etl::index_sequence) const + { + return value_type(*etl::get(_current)...); + } + + template + constexpr bool all_equal(const cartesian_product_iterator& other, etl::index_sequence) const + { + return ((etl::get(_current) == etl::get(other._current)) && ...); + } + + iterators_type _current; + iterators_type _begins; + iterators_type _ends; + bool _is_end; + }; + + //************************************************************************* + /// cartesian_product_view + /// A range adaptor that computes the Cartesian product of multiple + /// ranges. Given N ranges, produces a view of tuples where each tuple + /// contains one element from each range. The total number of elements + /// is the product of all input range sizes. The last range varies + /// fastest (like nested for loops). + //************************************************************************* + template + class cartesian_product_view: public etl::ranges::view_interface> + { + static_assert(sizeof...(Ranges) > 0, "Type list must be non-empty"); + + public: + using iterator = cartesian_product_iterator; + using const_iterator = cartesian_product_iterator; + + constexpr cartesian_product_view(Ranges&&... r): _r{etl::move(r)...} + { + } + + cartesian_product_view(const cartesian_product_view& other) = default; + + constexpr const_iterator begin() const + { + if (any_empty(etl::make_index_sequence{})) + { + return end(); + } + return make_begin(etl::make_index_sequence{}); + } + + constexpr const_iterator end() const + { + return make_end(etl::make_index_sequence{}); + } + + constexpr size_t size() const + { + return get_product_size(etl::make_index_sequence{}); + } + + private: + template + constexpr const_iterator make_begin(etl::index_sequence) const + { + return const_iterator( + typename const_iterator::iterators_type(ETL_OR_STD::begin(etl::get(_r))...), + typename const_iterator::iterators_type(ETL_OR_STD::begin(etl::get(_r))...), + typename const_iterator::iterators_type(ETL_OR_STD::end(etl::get(_r))...), + false + ); + } + + template + constexpr const_iterator make_end(etl::index_sequence) const + { + return const_iterator( + typename const_iterator::iterators_type(ETL_OR_STD::end(etl::get(_r))...), + typename const_iterator::iterators_type(ETL_OR_STD::begin(etl::get(_r))...), + typename const_iterator::iterators_type(ETL_OR_STD::end(etl::get(_r))...), + true + ); + } + + template + constexpr bool any_empty(etl::index_sequence) const + { + return ((ETL_OR_STD::begin(etl::get(_r)) == ETL_OR_STD::end(etl::get(_r))) || ...); + } + + template + constexpr size_t get_product_size(etl::index_sequence) const + { + size_t sizes[] = { static_cast(etl::distance(ETL_OR_STD::cbegin(etl::get(_r)), ETL_OR_STD::cend(etl::get(_r))))... }; + size_t product = 1; + for (size_t i = 0; i < sizeof...(Ranges); ++i) + { + product *= sizes[i]; + } + return product; + } + + mutable etl::tuple _r; + }; + + template + cartesian_product_view(Ranges&&...) -> cartesian_product_view...>; + + namespace views + { + namespace private_views + { + struct cartesian_product + { + template + constexpr auto operator()(Ranges&&... r) const + { + return cartesian_product_view(views::all(etl::forward(r))...); + } + }; + } + + inline constexpr private_views::cartesian_product cartesian_product; + } + + //************************************************************************* + /// to_input_iterator + /// An iterator wrapper that downgrades the iterator category to + /// input_iterator_tag, preserving the underlying iterator's traversal + /// behaviour but preventing algorithms from assuming stronger guarantees. + //************************************************************************* + template + class to_input_iterator + { + public: + using trait = typename etl::ranges::private_ranges::iterator_trait; + + using iterator = typename trait::iterator; + using const_iterator = typename trait::const_iterator; + using value_type = typename trait::value_type; + using difference_type = typename trait::difference_type; + using pointer = typename trait::pointer; + using reference = typename trait::reference; + + using iterator_category = ETL_OR_STD::input_iterator_tag; + + to_input_iterator() = default; + + to_input_iterator(const_iterator it) + : _it(it) + { + } + + to_input_iterator(const to_input_iterator& other) = default; + + to_input_iterator& operator=(const to_input_iterator& other) = default; + + to_input_iterator& operator++() + { + ++_it; + return *this; + } + + to_input_iterator operator++(int) + { + to_input_iterator tmp = *this; + ++(*this); + return tmp; + } + + reference operator*() const + { + return *_it; + } + + pointer operator->() const + { + return &(*_it); + } + + bool operator==(const to_input_iterator& other) const + { + return _it == other._it; + } + + bool operator!=(const to_input_iterator& other) const + { + return !(*this == other); + } + + private: + mutable const_iterator _it; + }; + + //************************************************************************* + /// to_input_view + /// A range adaptor that wraps a view and downgrades its iterator category + /// to input_iterator_tag. The view preserves all the elements and order + /// of the underlying range but prevents algorithms from relying on + /// forward, bidirectional, or random-access traversal guarantees. + //************************************************************************* + template + class to_input_view: public etl::ranges::view_interface> + { + public: + + using iterator = to_input_iterator; + using const_iterator = iterator; + + to_input_view(const to_input_view& other) = default; + + to_input_view(Range&& r): _r{etl::move(r)} + { + } + + constexpr Range& base() const + { + return _r; + } + + constexpr iterator begin() const + { + return iterator(ETL_OR_STD::begin(_r)); + } + + constexpr iterator end() const + { + return iterator(ETL_OR_STD::end(_r)); + } + + constexpr size_t size() const + { + return etl::distance(ETL_OR_STD::cbegin(_r), ETL_OR_STD::cend(_r)); + } + + private: + mutable Range _r; + }; + + template + to_input_view(Range&&) -> to_input_view>; + + struct to_input_range_adapter_closure: public range_adapter_closure + { + template + using target_view_type = to_input_view; + + to_input_range_adapter_closure() = default; + + template + constexpr auto operator()(Range&& r) + { + return to_input_view(views::all(etl::forward(r))); + } + }; + + namespace views + { + namespace private_views + { + struct to_input + { + template + constexpr auto operator()(Range&& r) const + { + return to_input_view(views::all(etl::forward(r))); + } + + constexpr auto operator()() const + { + return ranges::to_input_range_adapter_closure(); + } + }; + } + + inline constexpr private_views::to_input to_input; + } + + //************************************************************************* + /// elements_of + /// Encapsulates a range. Specializations of elements_of act as a tag in + /// overload sets to disambiguate when a range should be treated as a + /// sequence rather than a single value. + /// This is primarily used with coroutine generators to indicate that + /// elements of a range should be yielded one at a time. + //************************************************************************* + template + struct elements_of + { + R range; + }; + + template + elements_of(R&&) -> elements_of; + + namespace private_ranges + { + template + struct to_range_adapter_closure: public range_adapter_closure> + { + template + using target_view_type = C; + + to_range_adapter_closure() = default; + + template + C operator()(const Range& r) const + { + using result_type = C; + + result_type result; + + for (auto i: r) + { + result.push_back(i); + } + + return result; + } + + template + C operator()(Range&& r) + { + using result_type = C; + + result_type result; + + for (auto&& i: r) + { + result.emplace_back(etl::move(i)); + } + + return result; + } + }; + } + + template + private_ranges::to_range_adapter_closure to() + { + return private_ranges::to_range_adapter_closure(); + } + + } + + namespace views = ranges::views; +} + +template>> + +auto operator|(Range&& r, RangeAdaptorClosure rac) +{ + return rac(etl::forward(r)); +} + +#endif + +#endif diff --git a/include/etl/type_list.h b/include/etl/type_list.h index f87d97c6..66921dd0 100644 --- a/include/etl/type_list.h +++ b/include/etl/type_list.h @@ -31,7 +31,6 @@ SOFTWARE. #include "platform.h" -#include "algorithm.h" #include "index_of_type.h" #include "integral_limits.h" #include "static_assert.h" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index daa3c5c7..85e7413e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -282,6 +282,7 @@ add_executable(etl_tests test_queue_spsc_locked.cpp test_queue_spsc_locked_small.cpp test_random.cpp + test_ranges.cpp test_ratio.cpp test_reference_flat_map.cpp test_reference_flat_multimap.cpp diff --git a/test/test_algorithm.cpp b/test/test_algorithm.cpp index 66cd7caa..5e873131 100644 --- a/test/test_algorithm.cpp +++ b/test/test_algorithm.cpp @@ -4977,5 +4977,11926 @@ namespace CHECK_ARRAY_EQUAL(expected, data, 12); } + +#if ETL_USING_CPP17 + + //************************************************************************* + TEST(ranges_for_each_iterator) + { + std::vector vec{1, 2, 3, 4, 5}; + int sum = 0; + auto fun = [&sum](const int& v) { sum += v; }; + + auto result = etl::ranges::for_each(vec.begin(), vec.end(), fun); + + CHECK_EQUAL(15, sum); + CHECK(result.in == vec.end()); + } + + //************************************************************************* + TEST(ranges_for_each_range) + { + std::vector vec{1, 2, 3, 4, 5}; + int sum = 0; + auto fun = [&sum](const int& v) { sum += v; }; + + auto result = etl::ranges::for_each(vec, fun); + + CHECK_EQUAL(15, sum); + CHECK(result.in == vec.end()); // range overload returns end + } + + //************************************************************************* + TEST(ranges_for_each_empty) + { + std::vector vec_empty{}; + int sum = 0; + auto fun = [&sum](const int& v) { sum += v; }; + + auto result_it = etl::ranges::for_each(vec_empty.begin(), vec_empty.end(), fun); + CHECK_EQUAL(0, sum); + (void)result_it; + + sum = 0; + auto result_r = etl::ranges::for_each(vec_empty, fun); + CHECK_EQUAL(0, sum); + (void)result_r; + } + + //************************************************************************* + TEST(ranges_for_each_mutate) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector expected{2, 4, 6, 8, 10}; + auto doubler = [](int& v) { v *= 2; }; + + etl::ranges::for_each(vec.begin(), vec.end(), doubler); + bool is_same = std::equal(vec.begin(), vec.end(), expected.begin()); + CHECK(is_same); + + vec = {1, 2, 3, 4, 5}; + etl::ranges::for_each(vec, doubler); + is_same = std::equal(vec.begin(), vec.end(), expected.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(ranges_for_each_with_projection) + { + struct Item + { + int value; + }; + + std::vector vec{{1}, {2}, {3}, {4}, {5}}; + int sum = 0; + auto fun = [&sum](int v) { sum += v; }; + auto proj = [](const Item& item) -> int { return item.value; }; + + auto result = etl::ranges::for_each(vec.begin(), vec.end(), fun, proj); + CHECK_EQUAL(15, sum); + (void)result; + + sum = 0; + auto result_r = etl::ranges::for_each(vec, fun, proj); + CHECK_EQUAL(15, sum); + (void)result_r; + } + + //************************************************************************* + TEST(ranges_for_each_with_lambda_projection) + { + std::vector vec{1, 2, 3, 4, 5}; + int sum = 0; + auto fun = [&sum](int v) { sum += v; }; + auto proj = [](const int& v) { return v * 10; }; + + etl::ranges::for_each(vec.begin(), vec.end(), fun, proj); + CHECK_EQUAL(150, sum); + + sum = 0; + etl::ranges::for_each(vec, fun, proj); + CHECK_EQUAL(150, sum); + } + + //************************************************************************* + TEST(ranges_for_each_returns_fun) + { + std::vector vec{1, 2, 3, 4, 5}; + + struct Counter + { + int count = 0; + void operator()(const int&) { ++count; } + }; + + auto result_it = etl::ranges::for_each(vec.begin(), vec.end(), Counter{}); + CHECK_EQUAL(5, result_it.fun.count); + + auto result_r = etl::ranges::for_each(vec, Counter{}); + CHECK_EQUAL(5, result_r.fun.count); + } + + //************************************************************************* + TEST(ranges_for_each_single_element) + { + std::vector vec{42}; + int sum = 0; + auto fun = [&sum](const int& v) { sum += v; }; + + etl::ranges::for_each(vec.begin(), vec.end(), fun); + CHECK_EQUAL(42, sum); + + sum = 0; + etl::ranges::for_each(vec, fun); + CHECK_EQUAL(42, sum); + } + + //************************************************************************* + TEST(ranges_for_each_array) + { + std::array arr{1, 2, 3, 4, 5}; + int sum = 0; + auto fun = [&sum](const int& v) { sum += v; }; + + etl::ranges::for_each(arr.begin(), arr.end(), fun); + CHECK_EQUAL(15, sum); + + sum = 0; + etl::ranges::for_each(arr, fun); + CHECK_EQUAL(15, sum); + } + + //************************************************************************* + TEST(ranges_for_each_n_basic) + { + std::vector vec{1, 2, 3, 4, 5}; + int sum = 0; + auto fun = [&sum](const int& v) { sum += v; }; + + auto result = etl::ranges::for_each_n(vec.begin(), 3, fun); + + CHECK_EQUAL(6, sum); // 1+2+3 + CHECK(result.in == vec.begin() + 3); + } + + //************************************************************************* + TEST(ranges_for_each_n_all_elements) + { + std::vector vec{1, 2, 3, 4, 5}; + int sum = 0; + auto fun = [&sum](const int& v) { sum += v; }; + + auto result = etl::ranges::for_each_n(vec.begin(), 5, fun); + + CHECK_EQUAL(15, sum); + CHECK(result.in == vec.end()); + } + + //************************************************************************* + TEST(ranges_for_each_n_zero_count) + { + std::vector vec{1, 2, 3, 4, 5}; + int sum = 0; + auto fun = [&sum](const int& v) { sum += v; }; + + auto result = etl::ranges::for_each_n(vec.begin(), 0, fun); + + CHECK_EQUAL(0, sum); + CHECK(result.in == vec.begin()); + } + + //************************************************************************* + TEST(ranges_for_each_n_single_element) + { + std::vector vec{42}; + int sum = 0; + auto fun = [&sum](const int& v) { sum += v; }; + + auto result = etl::ranges::for_each_n(vec.begin(), 1, fun); + + CHECK_EQUAL(42, sum); + CHECK(result.in == vec.end()); + } + + //************************************************************************* + TEST(ranges_for_each_n_mutate) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector expected{2, 4, 6, 4, 5}; + auto doubler = [](int& v) { v *= 2; }; + + etl::ranges::for_each_n(vec.begin(), 3, doubler); + + bool is_same = std::equal(vec.begin(), vec.end(), expected.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(ranges_for_each_n_with_projection) + { + struct Item + { + int value; + }; + + std::vector vec{{1}, {2}, {3}, {4}, {5}}; + int sum = 0; + auto fun = [&sum](int v) { sum += v; }; + auto proj = [](const Item& item) -> int { return item.value; }; + + auto result = etl::ranges::for_each_n(vec.begin(), 3, fun, proj); + + CHECK_EQUAL(6, sum); // 1+2+3 + CHECK(result.in == vec.begin() + 3); + } + + //************************************************************************* + TEST(ranges_for_each_n_with_lambda_projection) + { + std::vector vec{1, 2, 3, 4, 5}; + int sum = 0; + auto fun = [&sum](int v) { sum += v; }; + auto proj = [](const int& v) { return v * 10; }; + + etl::ranges::for_each_n(vec.begin(), 4, fun, proj); + + CHECK_EQUAL(100, sum); // (1+2+3+4)*10 + } + + //************************************************************************* + TEST(ranges_for_each_n_returns_fun) + { + std::vector vec{1, 2, 3, 4, 5}; + + struct Counter + { + int count = 0; + void operator()(const int&) { ++count; } + }; + + auto result = etl::ranges::for_each_n(vec.begin(), 3, Counter{}); + CHECK_EQUAL(3, result.fun.count); + CHECK(result.in == vec.begin() + 3); + } + + //************************************************************************* + TEST(ranges_for_each_n_array) + { + std::array arr{1, 2, 3, 4, 5}; + int sum = 0; + auto fun = [&sum](const int& v) { sum += v; }; + + auto result = etl::ranges::for_each_n(arr.begin(), 4, fun); + + CHECK_EQUAL(10, sum); // 1+2+3+4 + CHECK(result.in == arr.begin() + 4); + } + + //************************************************************************* + TEST(ranges_all_of) + { + std::vector vec{1, 2, 3}; + std::vector vec_big1{11, 22, 33}; + std::vector vec_big2{1, 22, 3}; + std::vector vec_empty{}; + auto is_small = [](const int& v) -> bool { return v < 10; }; + + CHECK(etl::ranges::all_of(vec.begin(), vec.end(), is_small)); + CHECK(etl::ranges::all_of(vec, is_small)); + + CHECK(etl::ranges::all_of(vec_empty.begin(), vec_empty.end(), is_small)); + CHECK(etl::ranges::all_of(vec_empty, is_small)); + + CHECK_FALSE(etl::ranges::all_of(vec_big1.begin(), vec_big1.end(), is_small)); + CHECK_FALSE(etl::ranges::all_of(vec_big1, is_small)); + + CHECK_FALSE(etl::ranges::all_of(vec_big2.begin(), vec_big2.end(), is_small)); + CHECK_FALSE(etl::ranges::all_of(vec_big2, is_small)); + + auto proj = [](const int& v){ return v * 10; }; + + CHECK_FALSE(etl::ranges::all_of(vec.begin(), vec.end(), is_small, proj)); + CHECK_FALSE(etl::ranges::all_of(vec, is_small, proj)); + + CHECK(etl::ranges::all_of(vec_empty.begin(), vec_empty.end(), is_small, proj)); + CHECK(etl::ranges::all_of(vec_empty, is_small, proj)); + + CHECK_FALSE(etl::ranges::all_of(vec_big1.begin(), vec_big1.end(), is_small, proj)); + CHECK_FALSE(etl::ranges::all_of(vec_big1, is_small, proj)); + + CHECK_FALSE(etl::ranges::all_of(vec_big2.begin(), vec_big2.end(), is_small, proj)); + CHECK_FALSE(etl::ranges::all_of(vec_big2, is_small, proj)); + } + + //************************************************************************* + TEST(ranges_any_of) + { + std::vector vec{1, 2, 3}; + std::vector vec_big1{11, 22, 33}; + std::vector vec_big2{0, 22, 3}; + std::vector vec_empty{}; + auto is_small = [](const int& v) -> bool { return v < 10; }; + + CHECK(etl::ranges::any_of(vec.begin(), vec.end(), is_small)); + CHECK(etl::ranges::any_of(vec, is_small)); + + CHECK_FALSE(etl::ranges::any_of(vec_empty.begin(), vec_empty.end(), is_small)); + CHECK_FALSE(etl::ranges::any_of(vec_empty, is_small)); + + CHECK_FALSE(etl::ranges::any_of(vec_big1.begin(), vec_big1.end(), is_small)); + CHECK_FALSE(etl::ranges::any_of(vec_big1, is_small)); + + CHECK(etl::ranges::any_of(vec_big2.begin(), vec_big2.end(), is_small)); + CHECK(etl::ranges::any_of(vec_big2, is_small)); + + auto proj = [](const int& v){ return v * 10; }; + + CHECK_FALSE(etl::ranges::any_of(vec.begin(), vec.end(), is_small, proj)); + CHECK_FALSE(etl::ranges::any_of(vec, is_small, proj)); + + CHECK_FALSE(etl::ranges::any_of(vec_empty.begin(), vec_empty.end(), is_small, proj)); + CHECK_FALSE(etl::ranges::any_of(vec_empty, is_small, proj)); + + CHECK_FALSE(etl::ranges::any_of(vec_big1.begin(), vec_big1.end(), is_small, proj)); + CHECK_FALSE(etl::ranges::any_of(vec_big1, is_small, proj)); + + CHECK(etl::ranges::any_of(vec_big2.begin(), vec_big2.end(), is_small, proj)); + CHECK(etl::ranges::any_of(vec_big2, is_small, proj)); + } + + //************************************************************************* + TEST(ranges_none_of) + { + std::vector vec{1, 2, 3}; + std::vector vec_big1{11, 22, 33}; + std::vector vec_big2{0, 22, 3}; + std::vector vec_empty{}; + auto is_small = [](const int& v) -> bool { return v < 10; }; + + CHECK_FALSE(etl::ranges::none_of(vec.begin(), vec.end(), is_small)); + CHECK_FALSE(etl::ranges::none_of(vec, is_small)); + + CHECK(etl::ranges::none_of(vec_empty.begin(), vec_empty.end(), is_small)); + CHECK(etl::ranges::none_of(vec_empty, is_small)); + + CHECK(etl::ranges::none_of(vec_big1.begin(), vec_big1.end(), is_small)); + CHECK(etl::ranges::none_of(vec_big1, is_small)); + + CHECK_FALSE(etl::ranges::none_of(vec_big2.begin(), vec_big2.end(), is_small)); + CHECK_FALSE(etl::ranges::none_of(vec_big2, is_small)); + + auto proj = [](const int& v){ return v * 10; }; + + CHECK(etl::ranges::none_of(vec.begin(), vec.end(), is_small, proj)); + CHECK(etl::ranges::none_of(vec, is_small, proj)); + + CHECK(etl::ranges::none_of(vec_empty.begin(), vec_empty.end(), is_small, proj)); + CHECK(etl::ranges::none_of(vec_empty, is_small, proj)); + + CHECK(etl::ranges::none_of(vec_big1.begin(), vec_big1.end(), is_small, proj)); + CHECK(etl::ranges::none_of(vec_big1, is_small, proj)); + + CHECK_FALSE(etl::ranges::none_of(vec_big2.begin(), vec_big2.end(), is_small, proj)); + CHECK_FALSE(etl::ranges::none_of(vec_big2, is_small, proj)); + } + + //************************************************************************* + TEST(ranges_find) + { + auto proj = [](const int& v) { return v * 2; }; + + { + std::vector vec{7, 2, 1, 8, 1, 6}; + auto it = etl::ranges::find(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), 1); + CHECK_EQUAL(vec[2], *it); + CHECK_EQUAL(&vec[2], &(*it)); + *it = 3; + + it = etl::ranges::find(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), 1); + CHECK_EQUAL(vec[4], *it); + CHECK_EQUAL(&vec[4], &(*it)); + + it = etl::ranges::find(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), 9); + + CHECK(vec.end() == it); + + it = etl::ranges::find(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), 12, proj); + CHECK_EQUAL(vec[5], *it); + CHECK_EQUAL(&vec[5], &(*it)); + } + + { + std::vector vec{7, 2, 1, 8, 1, 6}; + auto it = etl::ranges::find(vec, 1); + CHECK_EQUAL(vec[2], *it); + CHECK_EQUAL(&vec[2], &(*it)); + + it = etl::ranges::find(vec, 16, proj); + CHECK_EQUAL(vec[3], *it); + CHECK_EQUAL(&vec[3], &(*it)); + } + + } + + //************************************************************************* + TEST(ranges_find_if) + { + auto proj = [](const int& v) { return v * 2; }; + auto pred = [](const int& v) { return v == 1; }; + + { + std::vector vec{7, 2, 1, 8, 1, 6}; + auto it = etl::ranges::find_if(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), pred); + CHECK_EQUAL(vec[2], *it); + CHECK_EQUAL(&vec[2], &(*it)); + *it = 3; + + it = etl::ranges::find_if(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), pred); + CHECK_EQUAL(vec[4], *it); + CHECK_EQUAL(&vec[4], &(*it)); + + it = etl::ranges::find_if(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), [](const int& v) { return v == 9; }); + + CHECK(vec.end() == it); + + it = etl::ranges::find_if(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), [](const int& v) { return v == 12; }, proj); + CHECK_EQUAL(vec[5], *it); + CHECK_EQUAL(&vec[5], &(*it)); + } + + { + std::vector vec{7, 2, 1, 8, 1, 6}; + auto it = etl::ranges::find_if(vec, pred); + CHECK_EQUAL(vec[2], *it); + CHECK_EQUAL(&vec[2], &(*it)); + + it = etl::ranges::find_if(vec, [](const int& v) { return v == 16; }, proj); + CHECK_EQUAL(vec[3], *it); + CHECK_EQUAL(&vec[3], &(*it)); + } + + } + + //************************************************************************* + TEST(ranges_find_if_not) + { + auto proj = [](const int& v) { return v * 2; }; + auto pred = [](const int& v) { return v != 1; }; + + { + std::vector vec{7, 2, 1, 8, 1, 6}; + auto it = etl::ranges::find_if_not(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), pred); + CHECK_EQUAL(vec[2], *it); + CHECK_EQUAL(&vec[2], &(*it)); + *it = 3; + + it = etl::ranges::find_if_not(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), pred); + CHECK_EQUAL(vec[4], *it); + CHECK_EQUAL(&vec[4], &(*it)); + + it = etl::ranges::find_if_not(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), [](const int& v) { return v != 9; }); + + CHECK(vec.end() == it); + + it = etl::ranges::find_if_not(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), [](const int& v) { return v != 12; }, proj); + CHECK_EQUAL(vec[5], *it); + CHECK_EQUAL(&vec[5], &(*it)); + } + + { + std::vector vec{7, 2, 1, 8, 1, 6}; + auto it = etl::ranges::find_if_not(vec, pred); + CHECK_EQUAL(vec[2], *it); + CHECK_EQUAL(&vec[2], &(*it)); + + it = etl::ranges::find_if_not(vec, [](const int& v) { return v != 16; }, proj); + CHECK_EQUAL(vec[3], *it); + CHECK_EQUAL(&vec[3], &(*it)); + } + + } + + //************************************************************************* + TEST(ranges_find_end) + { + auto proj1 = [](const int& v) { return v * 2; }; + auto proj2 = [](const int& v) { return v * 3; }; + auto pred = [](const int& v0, const int& v1) { return v0 == v1; }; + + { + std::vector vec{7, 2, 1, 8, 2, 6, 12, 3}; + std::vector vec2{8, 2}; + auto s = etl::ranges::find_end(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(vec2), ETL_OR_STD::end(vec2), pred); + CHECK_EQUAL(s.size(), 2); + CHECK_EQUAL(s[0], 8); + CHECK_EQUAL(s[1], 2); + vec[3] = 3; + + s = etl::ranges::find_end(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(vec2), ETL_OR_STD::end(vec2), pred); + CHECK(s.empty()); + + vec[3] = 8; + + s = etl::ranges::find_end(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(vec2), ETL_OR_STD::end(vec2), [](const int& v0, const int& v1) { return v0 == v1; }); + CHECK_EQUAL(s.size(), 2); + CHECK_EQUAL(s[0], 8); + CHECK_EQUAL(s[1], 2); + + s = etl::ranges::find_end(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(vec2), ETL_OR_STD::end(vec2), [](const int& v0, const int& v1) { return v0 == v1; }, proj1, proj2); + CHECK_EQUAL(s.size(), 2); + CHECK_EQUAL(s[0], 12); + CHECK_EQUAL(s[1], 3); + } + + { + std::vector vec{7, 2, 1, 8, 2, 6, 12, 3}; + std::vector vec2{8, 2}; + std::vector vec3{99, 2}; + auto s = etl::ranges::find_end(vec, vec2, pred); + CHECK_EQUAL(s.size(), 2); + CHECK_EQUAL(s[0], 8); + CHECK_EQUAL(s[1], 2); + + s = etl::ranges::find_end(vec, vec2, [](const int& v0, const int& v1) { return v0 == v1; }, proj1, proj2); + CHECK_EQUAL(s.size(), 2); + CHECK_EQUAL(s[0], 12); + CHECK_EQUAL(s[1], 3); + + s = etl::ranges::find_end(vec, vec3, [](const int& v0, const int& v1) { return v0 == v1; }, proj1, proj2); + CHECK(s.empty()); + } + + } + + //************************************************************************* + TEST(ranges_find_first_of_iterator) + { + auto proj1 = [](const int& v) { return v * 2; }; + auto proj2 = [](const int& v) { return v * 3; }; + auto pred = [](const int& v0, const int& v1) { return v0 == v1; }; + + // Found with predicate + { + std::vector vec{7, 2, 1, 8, 5, 6, 12, 3}; + std::vector targets{5, 8}; + auto it = etl::ranges::find_first_of(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(targets), ETL_OR_STD::end(targets), pred); + CHECK_EQUAL(8, *it); + } + + // Not found with predicate + { + std::vector vec{7, 2, 1, 8, 5, 6, 12, 3}; + std::vector targets{99, 100}; + auto it = etl::ranges::find_first_of(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(targets), ETL_OR_STD::end(targets), pred); + CHECK(it == ETL_OR_STD::end(vec)); + } + + // Found with default predicate (no predicate argument) + { + std::vector vec{7, 2, 1, 8, 5, 6, 12, 3}; + std::vector targets{6, 12}; + auto it = etl::ranges::find_first_of(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(targets), ETL_OR_STD::end(targets)); + CHECK_EQUAL(6, *it); + } + + // Found with lambda predicate + { + std::vector vec{7, 2, 1, 8, 5, 6, 12, 3}; + std::vector targets{12, 3}; + auto it = etl::ranges::find_first_of(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(targets), ETL_OR_STD::end(targets), [](const int& v0, const int& v1) { return v0 == v1; }); + CHECK_EQUAL(12, *it); + } + + // Found with projections: proj1 doubles, proj2 triples, so 6*2==4*3 + { + std::vector vec{7, 2, 1, 8, 5, 6, 12, 3}; + std::vector targets{4}; + auto it = etl::ranges::find_first_of(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(targets), ETL_OR_STD::end(targets), [](const int& v0, const int& v1) { return v0 == v1; }, proj1, proj2); + CHECK_EQUAL(6, *it); + } + + // Empty haystack + { + std::vector vec{}; + std::vector targets{1, 2}; + auto it = etl::ranges::find_first_of(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(targets), ETL_OR_STD::end(targets), pred); + CHECK(it == ETL_OR_STD::end(vec)); + } + + // Empty targets + { + std::vector vec{7, 2, 1, 8}; + std::vector targets{}; + auto it = etl::ranges::find_first_of(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(targets), ETL_OR_STD::end(targets), pred); + CHECK(it == ETL_OR_STD::end(vec)); + } + + // First element matches + { + std::vector vec{7, 2, 1, 8}; + std::vector targets{7}; + auto it = etl::ranges::find_first_of(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(targets), ETL_OR_STD::end(targets), pred); + CHECK_EQUAL(7, *it); + CHECK(it == ETL_OR_STD::begin(vec)); + } + + // Last element matches + { + std::vector vec{7, 2, 1, 8}; + std::vector targets{8}; + auto it = etl::ranges::find_first_of(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(targets), ETL_OR_STD::end(targets), pred); + CHECK_EQUAL(8, *it); + } + } + + //************************************************************************* + TEST(ranges_find_first_of_range) + { + auto proj1 = [](const int& v) { return v * 2; }; + auto proj2 = [](const int& v) { return v * 3; }; + auto pred = [](const int& v0, const int& v1) { return v0 == v1; }; + + // Found with predicate + { + std::vector vec{7, 2, 1, 8, 5, 6, 12, 3}; + std::vector targets{5, 8}; + auto it = etl::ranges::find_first_of(vec, targets, pred); + CHECK_EQUAL(8, *it); + } + + // Not found with predicate + { + std::vector vec{7, 2, 1, 8, 5, 6, 12, 3}; + std::vector targets{99, 100}; + auto it = etl::ranges::find_first_of(vec, targets, pred); + CHECK(it == ETL_OR_STD::end(vec)); + } + + // Found with default predicate + { + std::vector vec{7, 2, 1, 8, 5, 6, 12, 3}; + std::vector targets{6, 12}; + auto it = etl::ranges::find_first_of(vec, targets); + CHECK_EQUAL(6, *it); + } + + // Found with lambda predicate + { + std::vector vec{7, 2, 1, 8, 5, 6, 12, 3}; + std::vector targets{12, 3}; + auto it = etl::ranges::find_first_of(vec, targets, [](const int& v0, const int& v1) { return v0 == v1; }); + CHECK_EQUAL(12, *it); + } + + // Found with projections: proj1 doubles, proj2 triples, so 6*2==4*3 + { + std::vector vec{7, 2, 1, 8, 5, 6, 12, 3}; + std::vector targets{4}; + auto it = etl::ranges::find_first_of(vec, targets, [](const int& v0, const int& v1) { return v0 == v1; }, proj1, proj2); + CHECK_EQUAL(6, *it); + } + + // Not found with projections + { + std::vector vec{7, 2, 1, 8, 5, 6, 12, 3}; + std::vector targets{99}; + auto it = etl::ranges::find_first_of(vec, targets, [](const int& v0, const int& v1) { return v0 == v1; }, proj1, proj2); + CHECK(it == ETL_OR_STD::end(vec)); + } + + // Empty haystack + { + std::vector vec{}; + std::vector targets{1, 2}; + auto it = etl::ranges::find_first_of(vec, targets, pred); + CHECK(it == ETL_OR_STD::end(vec)); + } + + // Empty targets + { + std::vector vec{7, 2, 1, 8}; + std::vector targets{}; + auto it = etl::ranges::find_first_of(vec, targets, pred); + CHECK(it == ETL_OR_STD::end(vec)); + } + + // First element matches + { + std::vector vec{7, 2, 1, 8}; + std::vector targets{7}; + auto it = etl::ranges::find_first_of(vec, targets, pred); + CHECK_EQUAL(7, *it); + CHECK(it == ETL_OR_STD::begin(vec)); + } + + // Last element matches + { + std::vector vec{7, 2, 1, 8}; + std::vector targets{8}; + auto it = etl::ranges::find_first_of(vec, targets, pred); + CHECK_EQUAL(8, *it); + } + } + + //************************************************************************* + TEST(ranges_search_iterator) + { + auto pred = [](const int& v0, const int& v1) { return v0 == v1; }; + + // Search with predicate - found + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8}; + std::vector needle{3, 4, 5}; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle), pred); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(3, s[0]); + CHECK_EQUAL(4, s[1]); + CHECK_EQUAL(5, s[2]); + } + + // Search with predicate - not found + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8}; + std::vector needle{3, 5, 4}; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle), pred); + CHECK(s.empty()); + CHECK(s.begin() == ETL_OR_STD::end(vec)); + } + + // Search with default predicate (no predicate argument) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8}; + std::vector needle{6, 7, 8}; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle)); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(6, s[0]); + CHECK_EQUAL(7, s[1]); + CHECK_EQUAL(8, s[2]); + } + + // Search with lambda predicate + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8}; + std::vector needle{4, 5}; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle), [](const int& v0, const int& v1) { return v0 == v1; }); + CHECK_EQUAL(2, s.size()); + CHECK_EQUAL(4, s[0]); + CHECK_EQUAL(5, s[1]); + } + + // Search with projections + { + std::vector vec{2, 4, 6, 8, 10, 12, 14, 16}; + std::vector needle{3, 4}; + auto proj1 = [](const int& v) { return v / 2; }; + auto proj2 = [](const int& v) { return v; }; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle), [](const int& v0, const int& v1) { return v0 == v1; }, proj1, proj2); + CHECK_EQUAL(2, s.size()); + CHECK_EQUAL(6, s[0]); + CHECK_EQUAL(8, s[1]); + } + + // Search at beginning + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector needle{1, 2}; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle), pred); + CHECK_EQUAL(2, s.size()); + CHECK_EQUAL(1, s[0]); + CHECK_EQUAL(2, s[1]); + } + + // Search at end + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector needle{4, 5}; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle), pred); + CHECK_EQUAL(2, s.size()); + CHECK_EQUAL(4, s[0]); + CHECK_EQUAL(5, s[1]); + } + + // Search with empty needle + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector needle{}; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle), pred); + CHECK(s.empty()); + CHECK(s.begin() == ETL_OR_STD::begin(vec)); + } + + // Search with single element needle - found + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector needle{3}; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle), pred); + CHECK_EQUAL(1, s.size()); + CHECK_EQUAL(3, s[0]); + } + + // Search with needle same size as haystack - found + { + std::vector vec{1, 2, 3}; + std::vector needle{1, 2, 3}; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle), pred); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(1, s[0]); + CHECK_EQUAL(2, s[1]); + CHECK_EQUAL(3, s[2]); + } + + // Search with needle larger than haystack - not found + { + std::vector vec{1, 2}; + std::vector needle{1, 2, 3}; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle), pred); + CHECK(s.empty()); + CHECK(s.begin() == ETL_OR_STD::end(vec)); + } + + // Search finds first occurrence + { + std::vector vec{1, 2, 3, 1, 2, 3}; + std::vector needle{1, 2, 3}; + auto s = etl::ranges::search(ETL_OR_STD::begin(vec), ETL_OR_STD::end(vec), ETL_OR_STD::begin(needle), ETL_OR_STD::end(needle), pred); + CHECK_EQUAL(3, s.size()); + CHECK(s.begin() == vec.begin()); + } + } + + //************************************************************************* + TEST(ranges_search_range) + { + auto pred = [](const int& v0, const int& v1) { return v0 == v1; }; + + // Search with predicate - found + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8}; + std::vector needle{3, 4, 5}; + auto s = etl::ranges::search(vec, needle, pred); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(3, s[0]); + CHECK_EQUAL(4, s[1]); + CHECK_EQUAL(5, s[2]); + } + + // Search with predicate - not found + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8}; + std::vector needle{3, 5, 4}; + auto s = etl::ranges::search(vec, needle, pred); + CHECK(s.empty()); + CHECK(s.begin() == ETL_OR_STD::end(vec)); + } + + // Search with default predicate + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8}; + std::vector needle{5, 6, 7}; + auto s = etl::ranges::search(vec, needle); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(5, s[0]); + CHECK_EQUAL(6, s[1]); + CHECK_EQUAL(7, s[2]); + } + + // Search with projections + { + std::vector vec{2, 4, 6, 8, 10, 12, 14, 16}; + std::vector needle{3, 4}; + auto proj1 = [](const int& v) { return v / 2; }; + auto proj2 = [](const int& v) { return v; }; + auto s = etl::ranges::search(vec, needle, [](const int& v0, const int& v1) { return v0 == v1; }, proj1, proj2); + CHECK_EQUAL(2, s.size()); + CHECK_EQUAL(6, s[0]); + CHECK_EQUAL(8, s[1]); + } + + // Search with empty needle + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector needle{}; + auto s = etl::ranges::search(vec, needle, pred); + CHECK(s.empty()); + CHECK(s.begin() == ETL_OR_STD::begin(vec)); + } + + // Search finds first occurrence + { + std::vector vec{1, 2, 3, 1, 2, 3}; + std::vector needle{1, 2, 3}; + auto s = etl::ranges::search(vec, needle, pred); + CHECK_EQUAL(3, s.size()); + CHECK(s.begin() == vec.begin()); + } + + // Search not found with projections + { + std::vector vec{2, 4, 6, 8, 10}; + std::vector needle{99, 100}; + auto proj1 = [](const int& v) { return v / 2; }; + auto proj2 = [](const int& v) { return v; }; + auto s = etl::ranges::search(vec, needle, [](const int& v0, const int& v1) { return v0 == v1; }, proj1, proj2); + CHECK(s.empty()); + CHECK(s.begin() == ETL_OR_STD::end(vec)); + } + } + + //************************************************************************* + TEST(ranges_search_n_iterator) + { + auto pred = [](const int& v0, const int& v1) { return v0 == v1; }; + + // Search_n with predicate - found + { + std::vector vec{1, 2, 3, 3, 3, 4, 5}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 3, 3, pred); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(3, s[0]); + CHECK_EQUAL(3, s[1]); + CHECK_EQUAL(3, s[2]); + CHECK(s.begin() == vec.begin() + 2); + } + + // Search_n with predicate - not found + { + std::vector vec{1, 2, 3, 3, 4, 5}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 3, 3, pred); + CHECK(s.empty()); + CHECK(s.begin() == vec.end()); + } + + // Search_n with default predicate (no predicate argument) + { + std::vector vec{1, 2, 2, 2, 3, 4}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 3, 2); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(2, s[0]); + CHECK_EQUAL(2, s[1]); + CHECK_EQUAL(2, s[2]); + } + + // Search_n with lambda predicate + { + std::vector vec{1, 2, 3, 3, 3, 4}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 2, 3, [](const int& v0, const int& v1) { return v0 == v1; }); + CHECK_EQUAL(2, s.size()); + CHECK_EQUAL(3, s[0]); + CHECK_EQUAL(3, s[1]); + } + + // Search_n with projection + { + std::vector vec{2, 4, 6, 6, 6, 8}; + auto proj = [](const int& v) { return v / 2; }; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 3, 3, [](const int& v0, const int& v1) { return v0 == v1; }, proj); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(6, s[0]); + CHECK_EQUAL(6, s[1]); + CHECK_EQUAL(6, s[2]); + } + + // Search_n at beginning + { + std::vector vec{5, 5, 5, 1, 2, 3}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 3, 5, pred); + CHECK_EQUAL(3, s.size()); + CHECK(s.begin() == vec.begin()); + CHECK_EQUAL(5, s[0]); + CHECK_EQUAL(5, s[1]); + CHECK_EQUAL(5, s[2]); + } + + // Search_n at end + { + std::vector vec{1, 2, 3, 5, 5, 5}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 3, 5, pred); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(5, s[0]); + CHECK_EQUAL(5, s[1]); + CHECK_EQUAL(5, s[2]); + } + + // Search_n with count 0 + { + std::vector vec{1, 2, 3}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 0, 1, pred); + CHECK(s.empty()); + CHECK(s.begin() == vec.begin()); + } + + // Search_n with count 1 - found + { + std::vector vec{1, 2, 3, 4, 5}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 1, 3, pred); + CHECK_EQUAL(1, s.size()); + CHECK_EQUAL(3, s[0]); + } + + // Search_n with empty range + { + std::vector vec{}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 1, 3, pred); + CHECK(s.empty()); + } + + // Search_n entire range matches + { + std::vector vec{7, 7, 7, 7}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 4, 7, pred); + CHECK_EQUAL(4, s.size()); + CHECK(s.begin() == vec.begin()); + } + + // Search_n count larger than range + { + std::vector vec{3, 3}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 5, 3, pred); + CHECK(s.empty()); + } + + // Search_n finds first occurrence + { + std::vector vec{1, 2, 2, 1, 2, 2, 3}; + auto s = etl::ranges::search_n(vec.begin(), vec.end(), 2, 2, pred); + CHECK_EQUAL(2, s.size()); + CHECK(s.begin() == vec.begin() + 1); + } + } + + //************************************************************************* + TEST(ranges_search_n_range) + { + auto pred = [](const int& v0, const int& v1) { return v0 == v1; }; + + // Search_n with predicate - found + { + std::vector vec{1, 2, 3, 3, 3, 4, 5}; + auto s = etl::ranges::search_n(vec, 3, 3, pred); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(3, s[0]); + CHECK_EQUAL(3, s[1]); + CHECK_EQUAL(3, s[2]); + CHECK(s.begin() == vec.begin() + 2); + } + + // Search_n with predicate - not found + { + std::vector vec{1, 2, 3, 3, 4, 5}; + auto s = etl::ranges::search_n(vec, 3, 3, pred); + CHECK(s.empty()); + CHECK(s.begin() == vec.end()); + } + + // Search_n with default predicate + { + std::vector vec{1, 4, 4, 4, 5, 6}; + auto s = etl::ranges::search_n(vec, 3, 4); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(4, s[0]); + CHECK_EQUAL(4, s[1]); + CHECK_EQUAL(4, s[2]); + } + + // Search_n with projection + { + std::vector vec{2, 4, 6, 6, 6, 8}; + auto proj = [](const int& v) { return v / 2; }; + auto s = etl::ranges::search_n(vec, 3, 3, [](const int& v0, const int& v1) { return v0 == v1; }, proj); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(6, s[0]); + CHECK_EQUAL(6, s[1]); + CHECK_EQUAL(6, s[2]); + } + + // Search_n with count 0 + { + std::vector vec{1, 2, 3}; + auto s = etl::ranges::search_n(vec, 0, 1, pred); + CHECK(s.empty()); + CHECK(s.begin() == vec.begin()); + } + + // Search_n with empty range + { + std::vector vec{}; + auto s = etl::ranges::search_n(vec, 1, 3, pred); + CHECK(s.empty()); + } + + // Search_n finds first occurrence + { + std::vector vec{1, 2, 2, 1, 2, 2, 3}; + auto s = etl::ranges::search_n(vec, 2, 2, pred); + CHECK_EQUAL(2, s.size()); + CHECK(s.begin() == vec.begin() + 1); + } + + // Search_n entire range matches + { + std::vector vec{7, 7, 7, 7}; + auto s = etl::ranges::search_n(vec, 4, 7, pred); + CHECK_EQUAL(4, s.size()); + CHECK(s.begin() == vec.begin()); + } + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_found) + { + // Adjacent duplicates exist + std::vector vec{1, 2, 3, 3, 4, 5}; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end()); + CHECK_EQUAL(3, *it); + CHECK(it == vec.begin() + 2); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_not_found) + { + // No adjacent duplicates + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end()); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_empty) + { + // Empty range + std::vector vec{}; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end()); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_single_element) + { + // Single element - no adjacent pair possible + std::vector vec{42}; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end()); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_first_pair) + { + // Adjacent duplicates at the very beginning + std::vector vec{5, 5, 1, 2, 3}; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end()); + CHECK_EQUAL(5, *it); + CHECK(it == vec.begin()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_last_pair) + { + // Adjacent duplicates at the very end + std::vector vec{1, 2, 3, 7, 7}; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end()); + CHECK_EQUAL(7, *it); + CHECK(it == vec.begin() + 3); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_multiple_pairs) + { + // Multiple adjacent duplicate pairs - should find the first one + std::vector vec{1, 2, 2, 3, 3, 4}; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end()); + CHECK_EQUAL(2, *it); + CHECK(it == vec.begin() + 1); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_all_same) + { + // All elements are the same + std::vector vec{9, 9, 9, 9}; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end()); + CHECK_EQUAL(9, *it); + CHECK(it == vec.begin()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_custom_predicate) + { + // Custom predicate: find adjacent pair where second is greater + std::vector vec{5, 3, 1, 4, 2}; + auto pred = [](const int& a, const int& b) { return a < b; }; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end(), pred); + CHECK_EQUAL(1, *it); + CHECK(it == vec.begin() + 2); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_custom_predicate_not_found) + { + // Custom predicate not satisfied by any adjacent pair + std::vector vec{5, 4, 3, 2, 1}; + auto pred = [](const int& a, const int& b) { return a < b; }; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end(), pred); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_with_projection) + { + // Projection: compare absolute values for adjacency + std::vector vec{1, -2, 3, -3, 5}; + auto proj = [](const int& v) { return v < 0 ? -v : v; }; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end(), etl::ranges::equal_to{}, proj); + CHECK_EQUAL(3, *it); + CHECK(it == vec.begin() + 2); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_with_projection_not_found) + { + // Projection: no adjacent pair matches after projection + std::vector vec{1, -2, 3, -4, 5}; + auto proj = [](const int& v) { return v < 0 ? -v : v; }; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end(), etl::ranges::equal_to{}, proj); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_iterator_predicate_and_projection) + { + // Predicate: equality after projection (mod 10) + // {11, 25, 32, 43, 53} -> mod 10 -> {1, 5, 2, 3, 3}, so 43 and 53 match + std::vector vec{11, 25, 32, 43, 53}; + auto proj = [](const int& v) { return v % 10; }; + auto pred = [](const int& a, const int& b) { return a == b; }; + auto it = etl::ranges::adjacent_find(vec.begin(), vec.end(), pred, proj); + CHECK_EQUAL(43, *it); + CHECK(it == vec.begin() + 3); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_found) + { + // Adjacent duplicates exist - range overload + std::vector vec{1, 2, 3, 3, 4, 5}; + auto it = etl::ranges::adjacent_find(vec); + CHECK_EQUAL(3, *it); + CHECK(it == vec.begin() + 2); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_not_found) + { + // No adjacent duplicates - range overload + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::adjacent_find(vec); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_empty) + { + // Empty range - range overload + std::vector vec{}; + auto it = etl::ranges::adjacent_find(vec); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_single_element) + { + // Single element - range overload + std::vector vec{42}; + auto it = etl::ranges::adjacent_find(vec); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_first_pair) + { + // Adjacent duplicates at beginning - range overload + std::vector vec{5, 5, 1, 2, 3}; + auto it = etl::ranges::adjacent_find(vec); + CHECK_EQUAL(5, *it); + CHECK(it == vec.begin()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_last_pair) + { + // Adjacent duplicates at end - range overload + std::vector vec{1, 2, 3, 7, 7}; + auto it = etl::ranges::adjacent_find(vec); + CHECK_EQUAL(7, *it); + CHECK(it == vec.begin() + 3); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_multiple_pairs) + { + // Multiple adjacent pairs - range overload, finds first + std::vector vec{1, 2, 2, 3, 3, 4}; + auto it = etl::ranges::adjacent_find(vec); + CHECK_EQUAL(2, *it); + CHECK(it == vec.begin() + 1); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_all_same) + { + // All same - range overload + std::vector vec{9, 9, 9, 9}; + auto it = etl::ranges::adjacent_find(vec); + CHECK_EQUAL(9, *it); + CHECK(it == vec.begin()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_custom_predicate) + { + // Custom predicate with range overload + std::vector vec{5, 3, 1, 4, 2}; + auto pred = [](const int& a, const int& b) { return a < b; }; + auto it = etl::ranges::adjacent_find(vec, pred); + CHECK_EQUAL(1, *it); + CHECK(it == vec.begin() + 2); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_custom_predicate_not_found) + { + // Custom predicate not satisfied - range overload + std::vector vec{5, 4, 3, 2, 1}; + auto pred = [](const int& a, const int& b) { return a < b; }; + auto it = etl::ranges::adjacent_find(vec, pred); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_with_projection) + { + // Projection with range overload + std::vector vec{1, -2, 3, -3, 5}; + auto proj = [](const int& v) { return v < 0 ? -v : v; }; + auto it = etl::ranges::adjacent_find(vec, etl::ranges::equal_to{}, proj); + CHECK_EQUAL(3, *it); + CHECK(it == vec.begin() + 2); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_with_projection_not_found) + { + // Projection with range overload - not found + std::vector vec{1, -2, 3, -4, 5}; + auto proj = [](const int& v) { return v < 0 ? -v : v; }; + auto it = etl::ranges::adjacent_find(vec, etl::ranges::equal_to{}, proj); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_range_predicate_and_projection) + { + // Predicate and projection with range overload + // {11, 25, 32, 43, 53} -> mod 10 -> {1, 5, 2, 3, 3}, so 43 and 53 match + std::vector vec{11, 25, 32, 43, 53}; + auto proj = [](const int& v) { return v % 10; }; + auto pred = [](const int& a, const int& b) { return a == b; }; + auto it = etl::ranges::adjacent_find(vec, pred, proj); + CHECK_EQUAL(43, *it); + CHECK(it == vec.begin() + 3); + } + + //************************************************************************* + TEST(ranges_adjacent_find_two_elements_equal) + { + // Exactly two elements that are equal + std::vector vec{3, 3}; + auto it = etl::ranges::adjacent_find(vec); + CHECK_EQUAL(3, *it); + CHECK(it == vec.begin()); + } + + //************************************************************************* + TEST(ranges_adjacent_find_two_elements_not_equal) + { + // Exactly two elements that are not equal + std::vector vec{3, 4}; + auto it = etl::ranges::adjacent_find(vec); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_count_iterator_basic) + { + std::vector vec{1, 2, 3, 1, 4, 1, 5}; + + auto result = etl::ranges::count(vec.begin(), vec.end(), 1); + CHECK_EQUAL(3, result); + + result = etl::ranges::count(vec.begin(), vec.end(), 4); + CHECK_EQUAL(1, result); + } + + //************************************************************************* + TEST(ranges_count_range_basic) + { + std::vector vec{1, 2, 3, 1, 4, 1, 5}; + + auto result = etl::ranges::count(vec, 1); + CHECK_EQUAL(3, result); + + result = etl::ranges::count(vec, 4); + CHECK_EQUAL(1, result); + } + + //************************************************************************* + TEST(ranges_count_not_found) + { + std::vector vec{1, 2, 3, 4, 5}; + + auto result_it = etl::ranges::count(vec.begin(), vec.end(), 99); + CHECK_EQUAL(0, result_it); + + auto result_r = etl::ranges::count(vec, 99); + CHECK_EQUAL(0, result_r); + } + + //************************************************************************* + TEST(ranges_count_empty) + { + std::vector vec{}; + + auto result_it = etl::ranges::count(vec.begin(), vec.end(), 1); + CHECK_EQUAL(0, result_it); + + auto result_r = etl::ranges::count(vec, 1); + CHECK_EQUAL(0, result_r); + } + + //************************************************************************* + TEST(ranges_count_all_match) + { + std::vector vec{7, 7, 7, 7}; + + auto result_it = etl::ranges::count(vec.begin(), vec.end(), 7); + CHECK_EQUAL(4, result_it); + + auto result_r = etl::ranges::count(vec, 7); + CHECK_EQUAL(4, result_r); + } + + //************************************************************************* + TEST(ranges_count_single_element_match) + { + std::vector vec{42}; + + auto result_it = etl::ranges::count(vec.begin(), vec.end(), 42); + CHECK_EQUAL(1, result_it); + + auto result_r = etl::ranges::count(vec, 42); + CHECK_EQUAL(1, result_r); + } + + //************************************************************************* + TEST(ranges_count_single_element_no_match) + { + std::vector vec{42}; + + auto result_it = etl::ranges::count(vec.begin(), vec.end(), 99); + CHECK_EQUAL(0, result_it); + + auto result_r = etl::ranges::count(vec, 99); + CHECK_EQUAL(0, result_r); + } + + //************************************************************************* + TEST(ranges_count_with_projection) + { + std::vector vec{1, -2, 3, -2, 5}; + auto proj = [](const int& v) { return v < 0 ? -v : v; }; + + auto result_it = etl::ranges::count(vec.begin(), vec.end(), 2, proj); + CHECK_EQUAL(2, result_it); + + auto result_r = etl::ranges::count(vec, 2, proj); + CHECK_EQUAL(2, result_r); + } + + //************************************************************************* + TEST(ranges_count_with_projection_no_match) + { + std::vector vec{1, -2, 3, -4, 5}; + auto proj = [](const int& v) { return v < 0 ? -v : v; }; + + auto result_it = etl::ranges::count(vec.begin(), vec.end(), 99, proj); + CHECK_EQUAL(0, result_it); + + auto result_r = etl::ranges::count(vec, 99, proj); + CHECK_EQUAL(0, result_r); + } + + //************************************************************************* + TEST(ranges_count_with_member_projection) + { + struct Item + { + int id; + int category; + }; + + std::vector vec{{1, 10}, {2, 20}, {3, 10}, {4, 10}, {5, 30}}; + + auto result_it = etl::ranges::count(vec.begin(), vec.end(), 10, &Item::category); + CHECK_EQUAL(3, result_it); + + auto result_r = etl::ranges::count(vec, 20, &Item::category); + CHECK_EQUAL(1, result_r); + + auto result_none = etl::ranges::count(vec, 99, &Item::category); + CHECK_EQUAL(0, result_none); + } + + //************************************************************************* + TEST(ranges_count_with_lambda_projection) + { + std::vector vec{10, 21, 30, 41, 50}; + auto proj = [](const int& v) { return v % 10; }; + + // Count elements whose last digit is 0 + auto result_it = etl::ranges::count(vec.begin(), vec.end(), 0, proj); + CHECK_EQUAL(3, result_it); + + auto result_r = etl::ranges::count(vec, 1, proj); + CHECK_EQUAL(2, result_r); + } + + //************************************************************************* + TEST(ranges_count_array) + { + int arr[] = {1, 2, 1, 3, 1, 4}; + + auto result = etl::ranges::count(arr, 1); + CHECK_EQUAL(3, result); + + result = etl::ranges::count(arr, 3); + CHECK_EQUAL(1, result); + + result = etl::ranges::count(arr, 99); + CHECK_EQUAL(0, result); + } + + //************************************************************************* + TEST(ranges_count_if_iterator_basic) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8}; + auto is_even = [](int v) { return v % 2 == 0; }; + + auto result = etl::ranges::count_if(vec.begin(), vec.end(), is_even); + CHECK_EQUAL(4, result); + + auto is_greater_than_5 = [](int v) { return v > 5; }; + result = etl::ranges::count_if(vec.begin(), vec.end(), is_greater_than_5); + CHECK_EQUAL(3, result); + } + + //************************************************************************* + TEST(ranges_count_if_range_basic) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8}; + auto is_even = [](int v) { return v % 2 == 0; }; + + auto result = etl::ranges::count_if(vec, is_even); + CHECK_EQUAL(4, result); + + auto is_greater_than_5 = [](int v) { return v > 5; }; + result = etl::ranges::count_if(vec, is_greater_than_5); + CHECK_EQUAL(3, result); + } + + //************************************************************************* + TEST(ranges_count_if_not_found) + { + std::vector vec{1, 2, 3, 4, 5}; + auto is_negative = [](int v) { return v < 0; }; + + auto result_it = etl::ranges::count_if(vec.begin(), vec.end(), is_negative); + CHECK_EQUAL(0, result_it); + + auto result_r = etl::ranges::count_if(vec, is_negative); + CHECK_EQUAL(0, result_r); + } + + //************************************************************************* + TEST(ranges_count_if_empty) + { + std::vector vec{}; + auto is_even = [](int v) { return v % 2 == 0; }; + + auto result_it = etl::ranges::count_if(vec.begin(), vec.end(), is_even); + CHECK_EQUAL(0, result_it); + + auto result_r = etl::ranges::count_if(vec, is_even); + CHECK_EQUAL(0, result_r); + } + + //************************************************************************* + TEST(ranges_count_if_all_match) + { + std::vector vec{2, 4, 6, 8}; + auto is_even = [](int v) { return v % 2 == 0; }; + + auto result_it = etl::ranges::count_if(vec.begin(), vec.end(), is_even); + CHECK_EQUAL(4, result_it); + + auto result_r = etl::ranges::count_if(vec, is_even); + CHECK_EQUAL(4, result_r); + } + + //************************************************************************* + TEST(ranges_count_if_single_element_match) + { + std::vector vec{42}; + auto is_even = [](int v) { return v % 2 == 0; }; + + auto result_it = etl::ranges::count_if(vec.begin(), vec.end(), is_even); + CHECK_EQUAL(1, result_it); + + auto result_r = etl::ranges::count_if(vec, is_even); + CHECK_EQUAL(1, result_r); + } + + //************************************************************************* + TEST(ranges_count_if_single_element_no_match) + { + std::vector vec{41}; + auto is_even = [](int v) { return v % 2 == 0; }; + + auto result_it = etl::ranges::count_if(vec.begin(), vec.end(), is_even); + CHECK_EQUAL(0, result_it); + + auto result_r = etl::ranges::count_if(vec, is_even); + CHECK_EQUAL(0, result_r); + } + + //************************************************************************* + TEST(ranges_count_if_with_projection) + { + std::vector vec{1, -2, 3, -4, 5}; + auto proj = [](const int& v) { return v < 0 ? -v : v; }; + auto is_even = [](int v) { return v % 2 == 0; }; + + auto result_it = etl::ranges::count_if(vec.begin(), vec.end(), is_even, proj); + CHECK_EQUAL(2, result_it); + + auto result_r = etl::ranges::count_if(vec, is_even, proj); + CHECK_EQUAL(2, result_r); + } + + //************************************************************************* + TEST(ranges_count_if_with_projection_no_match) + { + std::vector vec{1, -2, 3, -4, 5}; + auto proj = [](const int& v) { return v < 0 ? -v : v; }; + auto greater_than_10 = [](int v) { return v > 10; }; + + auto result_it = etl::ranges::count_if(vec.begin(), vec.end(), greater_than_10, proj); + CHECK_EQUAL(0, result_it); + + auto result_r = etl::ranges::count_if(vec, greater_than_10, proj); + CHECK_EQUAL(0, result_r); + } + + //************************************************************************* + TEST(ranges_count_if_with_member_projection) + { + struct Item + { + int id; + int category; + }; + + std::vector vec{{1, 10}, {2, 20}, {3, 10}, {4, 10}, {5, 30}}; + auto is_ten = [](int v) { return v == 10; }; + + auto result_it = etl::ranges::count_if(vec.begin(), vec.end(), is_ten, &Item::category); + CHECK_EQUAL(3, result_it); + + auto result_r = etl::ranges::count_if(vec, is_ten, &Item::category); + CHECK_EQUAL(3, result_r); + + auto is_greater_than_15 = [](int v) { return v > 15; }; + auto result_gt = etl::ranges::count_if(vec, is_greater_than_15, &Item::category); + CHECK_EQUAL(2, result_gt); + } + + //************************************************************************* + TEST(ranges_count_if_with_lambda_projection) + { + std::vector vec{10, 21, 30, 41, 50}; + auto proj = [](const int& v) { return v % 10; }; + auto is_zero = [](int v) { return v == 0; }; + + // Count elements whose last digit is 0 + auto result_it = etl::ranges::count_if(vec.begin(), vec.end(), is_zero, proj); + CHECK_EQUAL(3, result_it); + + auto result_r = etl::ranges::count_if(vec, is_zero, proj); + CHECK_EQUAL(3, result_r); + } + + //************************************************************************* + TEST(ranges_count_if_array) + { + int arr[] = {1, 2, 3, 4, 5, 6}; + auto is_odd = [](int v) { return v % 2 != 0; }; + + auto result = etl::ranges::count_if(arr, is_odd); + CHECK_EQUAL(3, result); + + auto is_greater_than_4 = [](int v) { return v > 4; }; + result = etl::ranges::count_if(arr, is_greater_than_4); + CHECK_EQUAL(2, result); + + auto is_negative = [](int v) { return v < 0; }; + result = etl::ranges::count_if(arr, is_negative); + CHECK_EQUAL(0, result); + } + + //************************************************************************* + TEST(ranges_mismatch_iterator_basic) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 9, 4, 5}; + + auto result = etl::ranges::mismatch(v1.begin(), v1.end(), v2.begin(), v2.end()); + + CHECK(result.in1 == v1.begin() + 2); + CHECK(result.in2 == v2.begin() + 2); + CHECK_EQUAL(3, *result.in1); + CHECK_EQUAL(9, *result.in2); + } + + //************************************************************************* + TEST(ranges_mismatch_range_basic) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 9, 4, 5}; + + auto result = etl::ranges::mismatch(v1, v2); + + CHECK(result.in1 == v1.begin() + 2); + CHECK(result.in2 == v2.begin() + 2); + CHECK_EQUAL(3, *result.in1); + CHECK_EQUAL(9, *result.in2); + } + + //************************************************************************* + TEST(ranges_mismatch_all_equal) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3, 4, 5}; + + auto result_it = etl::ranges::mismatch(v1.begin(), v1.end(), v2.begin(), v2.end()); + CHECK(result_it.in1 == v1.end()); + CHECK(result_it.in2 == v2.end()); + + auto result_r = etl::ranges::mismatch(v1, v2); + CHECK(result_r.in1 == v1.end()); + CHECK(result_r.in2 == v2.end()); + } + + //************************************************************************* + TEST(ranges_mismatch_first_element_differs) + { + std::vector v1{9, 2, 3}; + std::vector v2{1, 2, 3}; + + auto result_it = etl::ranges::mismatch(v1.begin(), v1.end(), v2.begin(), v2.end()); + CHECK(result_it.in1 == v1.begin()); + CHECK(result_it.in2 == v2.begin()); + + auto result_r = etl::ranges::mismatch(v1, v2); + CHECK(result_r.in1 == v1.begin()); + CHECK(result_r.in2 == v2.begin()); + } + + //************************************************************************* + TEST(ranges_mismatch_different_lengths) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3}; + + auto result_it = etl::ranges::mismatch(v1.begin(), v1.end(), v2.begin(), v2.end()); + CHECK(result_it.in1 == v1.begin() + 3); + CHECK(result_it.in2 == v2.end()); + + auto result_r = etl::ranges::mismatch(v1, v2); + CHECK(result_r.in1 == v1.begin() + 3); + CHECK(result_r.in2 == v2.end()); + } + + //************************************************************************* + TEST(ranges_mismatch_empty_ranges) + { + std::vector v1{}; + std::vector v2{}; + + auto result_it = etl::ranges::mismatch(v1.begin(), v1.end(), v2.begin(), v2.end()); + CHECK(result_it.in1 == v1.end()); + CHECK(result_it.in2 == v2.end()); + + auto result_r = etl::ranges::mismatch(v1, v2); + CHECK(result_r.in1 == v1.end()); + CHECK(result_r.in2 == v2.end()); + } + + //************************************************************************* + TEST(ranges_mismatch_first_empty) + { + std::vector v1{}; + std::vector v2{1, 2, 3}; + + auto result_it = etl::ranges::mismatch(v1.begin(), v1.end(), v2.begin(), v2.end()); + CHECK(result_it.in1 == v1.end()); + CHECK(result_it.in2 == v2.begin()); + + auto result_r = etl::ranges::mismatch(v1, v2); + CHECK(result_r.in1 == v1.end()); + CHECK(result_r.in2 == v2.begin()); + } + + //************************************************************************* + TEST(ranges_mismatch_with_predicate) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{2, 4, 6, 9, 10}; + auto pred = [](int a, int b) { return b == a * 2; }; + + auto result_it = etl::ranges::mismatch(v1.begin(), v1.end(), v2.begin(), v2.end(), pred); + CHECK(result_it.in1 == v1.begin() + 3); + CHECK(result_it.in2 == v2.begin() + 3); + CHECK_EQUAL(4, *result_it.in1); + CHECK_EQUAL(9, *result_it.in2); + + auto result_r = etl::ranges::mismatch(v1, v2, pred); + CHECK(result_r.in1 == v1.begin() + 3); + CHECK(result_r.in2 == v2.begin() + 3); + } + + //************************************************************************* + TEST(ranges_mismatch_with_projection) + { + struct Item + { + int id; + int value; + }; + + std::vector v1{{1, 10}, {2, 20}, {3, 30}}; + std::vector v2{{9, 10}, {8, 20}, {7, 99}}; + + auto result_it = etl::ranges::mismatch(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, &Item::value, &Item::value); + CHECK(result_it.in1 == v1.begin() + 2); + CHECK(result_it.in2 == v2.begin() + 2); + + auto result_r = etl::ranges::mismatch(v1, v2, etl::ranges::equal_to{}, &Item::value, &Item::value); + CHECK(result_r.in1 == v1.begin() + 2); + CHECK(result_r.in2 == v2.begin() + 2); + } + + //************************************************************************* + TEST(ranges_mismatch_with_lambda_projection) + { + std::vector v1{-1, -2, -3}; + std::vector v2{1, 2, 9}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + + auto result_it = etl::ranges::mismatch(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, abs_proj, etl::identity{}); + CHECK(result_it.in1 == v1.begin() + 2); + CHECK(result_it.in2 == v2.begin() + 2); + + auto result_r = etl::ranges::mismatch(v1, v2, etl::ranges::equal_to{}, abs_proj, etl::identity{}); + CHECK(result_r.in1 == v1.begin() + 2); + CHECK(result_r.in2 == v2.begin() + 2); + } + + //************************************************************************* + TEST(ranges_mismatch_array) + { + int a1[] = {1, 2, 3, 4, 5}; + int a2[] = {1, 2, 3, 9, 5}; + + auto result = etl::ranges::mismatch(a1, a2); + CHECK_EQUAL(4, *result.in1); + CHECK_EQUAL(9, *result.in2); + } + + //************************************************************************* + TEST(ranges_mismatch_single_element_match) + { + std::vector v1{42}; + std::vector v2{42}; + + auto result = etl::ranges::mismatch(v1, v2); + CHECK(result.in1 == v1.end()); + CHECK(result.in2 == v2.end()); + } + + //************************************************************************* + TEST(ranges_mismatch_single_element_no_match) + { + std::vector v1{42}; + std::vector v2{99}; + + auto result = etl::ranges::mismatch(v1, v2); + CHECK(result.in1 == v1.begin()); + CHECK(result.in2 == v2.begin()); + } + + //************************************************************************* + TEST(ranges_equal_iterator_basic) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3, 4, 5}; + + bool result = etl::ranges::equal(v1.begin(), v1.end(), v2.begin(), v2.end()); + CHECK(result); + } + + //************************************************************************* + TEST(ranges_equal_range_basic) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3, 4, 5}; + + bool result = etl::ranges::equal(v1, v2); + CHECK(result); + } + + //************************************************************************* + TEST(ranges_equal_not_equal) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 9, 4, 5}; + + CHECK(!etl::ranges::equal(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::equal(v1, v2)); + } + + //************************************************************************* + TEST(ranges_equal_different_lengths) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3}; + + CHECK(!etl::ranges::equal(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::equal(v1, v2)); + } + + //************************************************************************* + TEST(ranges_equal_empty_ranges) + { + std::vector v1{}; + std::vector v2{}; + + CHECK(etl::ranges::equal(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::equal(v1, v2)); + } + + //************************************************************************* + TEST(ranges_equal_first_empty) + { + std::vector v1{}; + std::vector v2{1, 2, 3}; + + CHECK(!etl::ranges::equal(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::equal(v1, v2)); + } + + //************************************************************************* + TEST(ranges_equal_with_predicate) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{2, 4, 6, 8, 10}; + auto pred = [](int a, int b) { return b == a * 2; }; + + CHECK(etl::ranges::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), pred)); + CHECK(etl::ranges::equal(v1, v2, pred)); + } + + //************************************************************************* + TEST(ranges_equal_with_predicate_not_equal) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{2, 4, 6, 9, 10}; + auto pred = [](int a, int b) { return b == a * 2; }; + + CHECK(!etl::ranges::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), pred)); + CHECK(!etl::ranges::equal(v1, v2, pred)); + } + + //************************************************************************* + TEST(ranges_equal_with_projection) + { + struct Item + { + int id; + int value; + }; + + std::vector v1{{1, 10}, {2, 20}, {3, 30}}; + std::vector v2{{9, 10}, {8, 20}, {7, 30}}; + + CHECK(etl::ranges::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, &Item::value, &Item::value)); + CHECK(etl::ranges::equal(v1, v2, etl::ranges::equal_to{}, &Item::value, &Item::value)); + } + + //************************************************************************* + TEST(ranges_equal_with_projection_not_equal) + { + struct Item + { + int id; + int value; + }; + + std::vector v1{{1, 10}, {2, 20}, {3, 30}}; + std::vector v2{{9, 10}, {8, 20}, {7, 99}}; + + CHECK(!etl::ranges::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, &Item::value, &Item::value)); + CHECK(!etl::ranges::equal(v1, v2, etl::ranges::equal_to{}, &Item::value, &Item::value)); + } + + //************************************************************************* + TEST(ranges_equal_with_lambda_projection) + { + std::vector v1{-1, -2, -3}; + std::vector v2{1, 2, 3}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + + CHECK(etl::ranges::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, abs_proj, etl::identity{})); + CHECK(etl::ranges::equal(v1, v2, etl::ranges::equal_to{}, abs_proj, etl::identity{})); + } + + //************************************************************************* + TEST(ranges_equal_array) + { + int a1[] = {1, 2, 3, 4, 5}; + int a2[] = {1, 2, 3, 4, 5}; + + CHECK(etl::ranges::equal(a1, a2)); + } + + //************************************************************************* + TEST(ranges_equal_array_not_equal) + { + int a1[] = {1, 2, 3, 4, 5}; + int a2[] = {1, 2, 3, 9, 5}; + + CHECK(!etl::ranges::equal(a1, a2)); + } + + //************************************************************************* + TEST(ranges_equal_single_element_match) + { + std::vector v1{42}; + std::vector v2{42}; + + CHECK(etl::ranges::equal(v1, v2)); + } + + //************************************************************************* + TEST(ranges_equal_single_element_no_match) + { + std::vector v1{42}; + std::vector v2{99}; + + CHECK(!etl::ranges::equal(v1, v2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_iterator_basic) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{3, 1, 5, 2, 4}; + + CHECK(etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end())); + } + + //************************************************************************* + TEST(ranges_is_permutation_range_basic) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{3, 1, 5, 2, 4}; + + CHECK(etl::ranges::is_permutation(v1, v2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_not_permutation) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3, 4, 4}; + + CHECK(!etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::is_permutation(v1, v2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_different_lengths) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3}; + + CHECK(!etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::is_permutation(v1, v2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_empty_ranges) + { + std::vector v1{}; + std::vector v2{}; + + CHECK(etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::is_permutation(v1, v2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_first_empty) + { + std::vector v1{}; + std::vector v2{1, 2, 3}; + + CHECK(!etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::is_permutation(v1, v2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_with_duplicates) + { + std::vector v1{1, 2, 2, 3, 3, 3}; + std::vector v2{3, 2, 3, 1, 3, 2}; + + CHECK(etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::is_permutation(v1, v2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_with_duplicates_mismatch) + { + std::vector v1{1, 2, 2, 3, 3, 3}; + std::vector v2{3, 2, 3, 1, 2, 2}; + + CHECK(!etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::is_permutation(v1, v2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_single_element_match) + { + std::vector v1{42}; + std::vector v2{42}; + + CHECK(etl::ranges::is_permutation(v1, v2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_single_element_no_match) + { + std::vector v1{42}; + std::vector v2{99}; + + CHECK(!etl::ranges::is_permutation(v1, v2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_with_predicate) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{5, 3, 1, 4, 2}; + auto pred = [](int a, int b) { return a == b; }; + + CHECK(etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end(), pred)); + CHECK(etl::ranges::is_permutation(v1, v2, pred)); + } + + //************************************************************************* + TEST(ranges_is_permutation_with_predicate_not_permutation) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3, 4, 4}; + auto pred = [](int a, int b) { return a == b; }; + + CHECK(!etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end(), pred)); + CHECK(!etl::ranges::is_permutation(v1, v2, pred)); + } + + //************************************************************************* + TEST(ranges_is_permutation_with_projection) + { + struct Item + { + int id; + int value; + }; + + std::vector v1{{1, 10}, {2, 20}, {3, 30}}; + std::vector v2{{9, 30}, {8, 10}, {7, 20}}; + + CHECK(etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, &Item::value, &Item::value)); + CHECK(etl::ranges::is_permutation(v1, v2, etl::ranges::equal_to{}, &Item::value, &Item::value)); + } + + //************************************************************************* + TEST(ranges_is_permutation_with_projection_not_permutation) + { + struct Item + { + int id; + int value; + }; + + std::vector v1{{1, 10}, {2, 20}, {3, 30}}; + std::vector v2{{9, 10}, {8, 20}, {7, 99}}; + + CHECK(!etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, &Item::value, &Item::value)); + CHECK(!etl::ranges::is_permutation(v1, v2, etl::ranges::equal_to{}, &Item::value, &Item::value)); + } + + //************************************************************************* + TEST(ranges_is_permutation_with_lambda_projection) + { + std::vector v1{-1, -2, -3}; + std::vector v2{3, 1, 2}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + + CHECK(etl::ranges::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, abs_proj, etl::identity{})); + CHECK(etl::ranges::is_permutation(v1, v2, etl::ranges::equal_to{}, abs_proj, etl::identity{})); + } + + //************************************************************************* + TEST(ranges_is_permutation_array) + { + int a1[] = {1, 2, 3, 4, 5}; + int a2[] = {5, 4, 3, 2, 1}; + + CHECK(etl::ranges::is_permutation(a1, a2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_array_not_permutation) + { + int a1[] = {1, 2, 3, 4, 5}; + int a2[] = {1, 2, 3, 9, 5}; + + CHECK(!etl::ranges::is_permutation(a1, a2)); + } + + //************************************************************************* + TEST(ranges_is_permutation_identical) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3, 4, 5}; + + CHECK(etl::ranges::is_permutation(v1, v2)); + } + + //************************************************************************* + TEST(ranges_starts_with_iterator_basic) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3}; + + CHECK(etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + } + + //************************************************************************* + TEST(ranges_starts_with_range_basic) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3}; + + CHECK(etl::ranges::starts_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_starts_with_not_prefix) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 9, 3}; + + CHECK(!etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::starts_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_starts_with_prefix_longer_than_range) + { + std::vector v1{1, 2, 3}; + std::vector v2{1, 2, 3, 4, 5}; + + CHECK(!etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::starts_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_starts_with_equal_ranges) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3, 4, 5}; + + CHECK(etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::starts_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_starts_with_empty_prefix) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{}; + + CHECK(etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::starts_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_starts_with_both_empty) + { + std::vector v1{}; + std::vector v2{}; + + CHECK(etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::starts_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_starts_with_empty_range_nonempty_prefix) + { + std::vector v1{}; + std::vector v2{1, 2, 3}; + + CHECK(!etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::starts_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_starts_with_single_element_match) + { + std::vector v1{42, 1, 2}; + std::vector v2{42}; + + CHECK(etl::ranges::starts_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_starts_with_single_element_no_match) + { + std::vector v1{42, 1, 2}; + std::vector v2{99}; + + CHECK(!etl::ranges::starts_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_starts_with_with_predicate) + { + std::vector v1{2, 4, 6, 8, 10}; + std::vector v2{1, 2, 3}; + auto pred = [](int a, int b) { return a == b * 2; }; + + CHECK(etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end(), pred)); + CHECK(etl::ranges::starts_with(v1, v2, pred)); + } + + //************************************************************************* + TEST(ranges_starts_with_with_predicate_no_match) + { + std::vector v1{2, 4, 7, 8, 10}; + std::vector v2{1, 2, 3}; + auto pred = [](int a, int b) { return a == b * 2; }; + + CHECK(!etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end(), pred)); + CHECK(!etl::ranges::starts_with(v1, v2, pred)); + } + + //************************************************************************* + TEST(ranges_starts_with_with_projection) + { + struct Item + { + int id; + int value; + }; + + std::vector v1{{1, 10}, {2, 20}, {3, 30}, {4, 40}}; + std::vector v2{{9, 10}, {8, 20}}; + + CHECK(etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, &Item::value, &Item::value)); + CHECK(etl::ranges::starts_with(v1, v2, etl::ranges::equal_to{}, &Item::value, &Item::value)); + } + + //************************************************************************* + TEST(ranges_starts_with_with_projection_no_match) + { + struct Item + { + int id; + int value; + }; + + std::vector v1{{1, 10}, {2, 20}, {3, 30}, {4, 40}}; + std::vector v2{{9, 10}, {8, 99}}; + + CHECK(!etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, &Item::value, &Item::value)); + CHECK(!etl::ranges::starts_with(v1, v2, etl::ranges::equal_to{}, &Item::value, &Item::value)); + } + + //************************************************************************* + TEST(ranges_starts_with_with_lambda_projection) + { + std::vector v1{-1, -2, -3, -4, -5}; + std::vector v2{1, 2, 3}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + + CHECK(etl::ranges::starts_with(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, abs_proj, etl::identity{})); + CHECK(etl::ranges::starts_with(v1, v2, etl::ranges::equal_to{}, abs_proj, etl::identity{})); + } + + //************************************************************************* + TEST(ranges_starts_with_array) + { + int a1[] = {1, 2, 3, 4, 5}; + int a2[] = {1, 2, 3}; + + CHECK(etl::ranges::starts_with(a1, a2)); + } + + //************************************************************************* + TEST(ranges_starts_with_array_not_prefix) + { + int a1[] = {1, 2, 3, 4, 5}; + int a2[] = {1, 9, 3}; + + CHECK(!etl::ranges::starts_with(a1, a2)); + } + + //************************************************************************* + TEST(ranges_ends_with_match) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{3, 4, 5}; + + CHECK(etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::ends_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_ends_with_no_match) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{3, 4, 6}; + + CHECK(!etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::ends_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_ends_with_suffix_longer_than_range) + { + std::vector v1{1, 2, 3}; + std::vector v2{1, 2, 3, 4, 5}; + + CHECK(!etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::ends_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_ends_with_equal_ranges) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{1, 2, 3, 4, 5}; + + CHECK(etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::ends_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_ends_with_empty_suffix) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{}; + + CHECK(etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::ends_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_ends_with_both_empty) + { + std::vector v1{}; + std::vector v2{}; + + CHECK(etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::ends_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_ends_with_empty_range_nonempty_suffix) + { + std::vector v1{}; + std::vector v2{1, 2, 3}; + + CHECK(!etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::ends_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_ends_with_single_element_match) + { + std::vector v1{1, 2, 42}; + std::vector v2{42}; + + CHECK(etl::ranges::ends_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_ends_with_single_element_no_match) + { + std::vector v1{1, 2, 42}; + std::vector v2{99}; + + CHECK(!etl::ranges::ends_with(v1, v2)); + } + + //************************************************************************* + TEST(ranges_ends_with_with_predicate) + { + std::vector v1{2, 4, 6, 8, 10}; + std::vector v2{4, 5}; + auto pred = [](int a, int b) { return a == b * 2; }; + + CHECK(etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end(), pred)); + CHECK(etl::ranges::ends_with(v1, v2, pred)); + } + + //************************************************************************* + TEST(ranges_ends_with_with_predicate_no_match) + { + std::vector v1{2, 4, 7, 8, 10}; + std::vector v2{4, 6}; + auto pred = [](int a, int b) { return a == b * 2; }; + + CHECK(!etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end(), pred)); + CHECK(!etl::ranges::ends_with(v1, v2, pred)); + } + + //************************************************************************* + TEST(ranges_ends_with_with_projection) + { + struct Item + { + int id; + int value; + }; + + std::vector v1{{1, 10}, {2, 20}, {3, 30}, {4, 40}}; + std::vector v2{{9, 30}, {8, 40}}; + + CHECK(etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, &Item::value, &Item::value)); + CHECK(etl::ranges::ends_with(v1, v2, etl::ranges::equal_to{}, &Item::value, &Item::value)); + } + + //************************************************************************* + TEST(ranges_ends_with_with_projection_no_match) + { + struct Item + { + int id; + int value; + }; + + std::vector v1{{1, 10}, {2, 20}, {3, 30}, {4, 40}}; + std::vector v2{{9, 30}, {8, 99}}; + + CHECK(!etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, &Item::value, &Item::value)); + CHECK(!etl::ranges::ends_with(v1, v2, etl::ranges::equal_to{}, &Item::value, &Item::value)); + } + + //************************************************************************* + TEST(ranges_ends_with_with_lambda_projection) + { + std::vector v1{-1, -2, -3, -4, -5}; + std::vector v2{4, 5}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + + CHECK(etl::ranges::ends_with(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::equal_to{}, abs_proj, etl::identity{})); + CHECK(etl::ranges::ends_with(v1, v2, etl::ranges::equal_to{}, abs_proj, etl::identity{})); + } + + //************************************************************************* + TEST(ranges_ends_with_array) + { + int a1[] = {1, 2, 3, 4, 5}; + int a2[] = {3, 4, 5}; + + CHECK(etl::ranges::ends_with(a1, a2)); + } + + //************************************************************************* + TEST(ranges_ends_with_array_not_suffix) + { + int a1[] = {1, 2, 3, 4, 5}; + int a2[] = {3, 9, 5}; + + CHECK(!etl::ranges::ends_with(a1, a2)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_iterator_less) + { + std::vector v1{1, 2, 3}; + std::vector v2{1, 2, 4}; + + CHECK(etl::ranges::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::lexicographical_compare(v2.begin(), v2.end(), v1.begin(), v1.end())); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_range_less) + { + std::vector v1{1, 2, 3}; + std::vector v2{1, 2, 4}; + + CHECK(etl::ranges::lexicographical_compare(v1, v2)); + CHECK(!etl::ranges::lexicographical_compare(v2, v1)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_equal_ranges) + { + std::vector v1{1, 2, 3}; + std::vector v2{1, 2, 3}; + + CHECK(!etl::ranges::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::lexicographical_compare(v1, v2)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_first_shorter) + { + std::vector v1{1, 2}; + std::vector v2{1, 2, 3}; + + CHECK(etl::ranges::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::lexicographical_compare(v1, v2)); + CHECK(!etl::ranges::lexicographical_compare(v2, v1)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_second_shorter) + { + std::vector v1{1, 2, 3}; + std::vector v2{1, 2}; + + CHECK(!etl::ranges::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::lexicographical_compare(v1, v2)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_both_empty) + { + std::vector v1{}; + std::vector v2{}; + + CHECK(!etl::ranges::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::lexicographical_compare(v1, v2)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_first_empty) + { + std::vector v1{}; + std::vector v2{1, 2, 3}; + + CHECK(etl::ranges::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(etl::ranges::lexicographical_compare(v1, v2)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_second_empty) + { + std::vector v1{1, 2, 3}; + std::vector v2{}; + + CHECK(!etl::ranges::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end())); + CHECK(!etl::ranges::lexicographical_compare(v1, v2)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_with_comparator) + { + std::vector v1{3, 2, 1}; + std::vector v2{3, 2, 0}; + auto comp = [](int a, int b) { return a > b; }; + + CHECK(etl::ranges::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end(), comp)); + CHECK(etl::ranges::lexicographical_compare(v1, v2, comp)); + CHECK(!etl::ranges::lexicographical_compare(v2, v1, comp)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_with_projection) + { + struct Item + { + int id; + int value; + }; + + std::vector v1{{1, 10}, {2, 20}, {3, 30}}; + std::vector v2{{9, 10}, {8, 20}, {7, 40}}; + + CHECK(etl::ranges::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::less{}, &Item::value, &Item::value)); + CHECK(etl::ranges::lexicographical_compare(v1, v2, etl::ranges::less{}, &Item::value, &Item::value)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_with_projection_not_less) + { + struct Item + { + int id; + int value; + }; + + std::vector v1{{1, 10}, {2, 20}, {3, 40}}; + std::vector v2{{9, 10}, {8, 20}, {7, 30}}; + + CHECK(!etl::ranges::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::less{}, &Item::value, &Item::value)); + CHECK(!etl::ranges::lexicographical_compare(v1, v2, etl::ranges::less{}, &Item::value, &Item::value)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_with_lambda_projection) + { + std::vector v1{-1, -2, -3}; + std::vector v2{1, 2, 4}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + + CHECK(etl::ranges::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end(), + etl::ranges::less{}, abs_proj, etl::identity{})); + CHECK(etl::ranges::lexicographical_compare(v1, v2, etl::ranges::less{}, abs_proj, etl::identity{})); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_array) + { + int a1[] = {1, 2, 3}; + int a2[] = {1, 2, 4}; + + CHECK(etl::ranges::lexicographical_compare(a1, a2)); + CHECK(!etl::ranges::lexicographical_compare(a2, a1)); + } + + //************************************************************************* + TEST(ranges_lexicographical_compare_single_element) + { + std::vector v1{1}; + std::vector v2{2}; + + CHECK(etl::ranges::lexicographical_compare(v1, v2)); + CHECK(!etl::ranges::lexicographical_compare(v2, v1)); + CHECK(!etl::ranges::lexicographical_compare(v1, v1)); + } + + //************************************************************************* + TEST(ranges_fold_left_sum_iterators) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_left(v.begin(), v.end(), 0, std::plus{}); + CHECK_EQUAL(15, result); + } + + //************************************************************************* + TEST(ranges_fold_left_sum_range) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_left(v, 0, std::plus{}); + CHECK_EQUAL(15, result); + } + + //************************************************************************* + TEST(ranges_fold_left_empty_range) + { + std::vector v{}; + auto result = etl::ranges::fold_left(v.begin(), v.end(), 42, std::plus{}); + CHECK_EQUAL(42, result); + } + + //************************************************************************* + TEST(ranges_fold_left_product) + { + std::vector v{1, 2, 3, 4}; + auto result = etl::ranges::fold_left(v, 1, std::multiplies{}); + CHECK_EQUAL(24, result); + } + + //************************************************************************* + TEST(ranges_fold_left_string_concat) + { + std::vector v{"a", "b", "c"}; + auto result = etl::ranges::fold_left(v, std::string{}, std::plus{}); + CHECK_EQUAL(std::string("abc"), result); + } + + //************************************************************************* + TEST(ranges_fold_left_different_init_type) + { + std::vector v{1, 2, 3}; + auto result = etl::ranges::fold_left(v, 0.5, [](double acc, int x) { return acc + x; }); + CHECK_CLOSE(6.5, result, 0.001); + } + + //************************************************************************* + TEST(ranges_fold_left_array) + { + int a[] = {10, 20, 30}; + auto result = etl::ranges::fold_left(a, 0, std::plus{}); + CHECK_EQUAL(60, result); + } + + //************************************************************************* + TEST(ranges_fold_left_with_iter_sum) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_left_with_iter(v.begin(), v.end(), 0, std::plus{}); + CHECK_EQUAL(15, result.value); + CHECK(result.in == v.end()); + } + + //************************************************************************* + TEST(ranges_fold_left_with_iter_range) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_left_with_iter(v, 0, std::plus{}); + CHECK_EQUAL(15, result.value); + } + + //************************************************************************* + TEST(ranges_fold_left_with_iter_empty) + { + std::vector v{}; + auto result = etl::ranges::fold_left_with_iter(v.begin(), v.end(), 99, std::plus{}); + CHECK_EQUAL(99, result.value); + CHECK(result.in == v.end()); + } + + //************************************************************************* + TEST(ranges_fold_left_first_sum) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_left_first(v.begin(), v.end(), std::plus{}); + CHECK_EQUAL(15, result); + } + + //************************************************************************* + TEST(ranges_fold_left_first_range) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_left_first(v, std::plus{}); + CHECK_EQUAL(15, result); + } + + //************************************************************************* + TEST(ranges_fold_left_first_single_element) + { + std::vector v{42}; + auto result = etl::ranges::fold_left_first(v, std::plus{}); + CHECK_EQUAL(42, result); + } + + //************************************************************************* + TEST(ranges_fold_left_first_with_iter_sum) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_left_first_with_iter(v.begin(), v.end(), std::plus{}); + CHECK_EQUAL(15, result.value); + CHECK(result.in == v.end()); + } + + //************************************************************************* + TEST(ranges_fold_left_first_with_iter_range) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_left_first_with_iter(v, std::plus{}); + CHECK_EQUAL(15, result.value); + } + + //************************************************************************* + TEST(ranges_fold_left_lambda) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_left(v, 0, [](int acc, int x) { return acc + x * x; }); + CHECK_EQUAL(55, result); + } + + //************************************************************************* + TEST(ranges_fold_right_sum_iterators) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_right(v.begin(), v.end(), 0, std::plus{}); + CHECK_EQUAL(15, result); + } + + //************************************************************************* + TEST(ranges_fold_right_sum_range) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_right(v, 0, std::plus{}); + CHECK_EQUAL(15, result); + } + + //************************************************************************* + TEST(ranges_fold_right_empty_range) + { + std::vector v{}; + auto result = etl::ranges::fold_right(v.begin(), v.end(), 42, std::plus{}); + CHECK_EQUAL(42, result); + } + + //************************************************************************* + TEST(ranges_fold_right_product) + { + std::vector v{1, 2, 3, 4}; + auto result = etl::ranges::fold_right(v, 1, std::multiplies{}); + CHECK_EQUAL(24, result); + } + + //************************************************************************* + TEST(ranges_fold_right_string_concat) + { + std::vector v{"a", "b", "c"}; + auto result = etl::ranges::fold_right(v, std::string{}, std::plus{}); + CHECK_EQUAL(std::string("abc"), result); + } + + //************************************************************************* + TEST(ranges_fold_right_different_init_type) + { + std::vector v{1, 2, 3}; + auto result = etl::ranges::fold_right(v, 0.5, [](int x, double acc) { return acc + x; }); + CHECK_CLOSE(6.5, result, 0.001); + } + + //************************************************************************* + TEST(ranges_fold_right_array) + { + int a[] = {10, 20, 30}; + auto result = etl::ranges::fold_right(a, 0, std::plus{}); + CHECK_EQUAL(60, result); + } + + //************************************************************************* + TEST(ranges_fold_right_subtraction_order) + { + // fold_right({1,2,3}, 0, minus) = 1 - (2 - (3 - 0)) = 1 - (2 - 3) = 1 - (-1) = 2 + std::vector v{1, 2, 3}; + auto result = etl::ranges::fold_right(v, 0, std::minus{}); + CHECK_EQUAL(2, result); + } + + //************************************************************************* + TEST(ranges_fold_right_lambda) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_right(v, 0, [](int x, int acc) { return acc + x * x; }); + CHECK_EQUAL(55, result); + } + + //************************************************************************* + TEST(ranges_fold_right_last_sum) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_right_last(v.begin(), v.end(), std::plus{}); + CHECK_EQUAL(15, result); + } + + //************************************************************************* + TEST(ranges_fold_right_last_range) + { + std::vector v{1, 2, 3, 4, 5}; + auto result = etl::ranges::fold_right_last(v, std::plus{}); + CHECK_EQUAL(15, result); + } + + //************************************************************************* + TEST(ranges_fold_right_last_single_element) + { + std::vector v{42}; + auto result = etl::ranges::fold_right_last(v, std::plus{}); + CHECK_EQUAL(42, result); + } + + //************************************************************************* + TEST(ranges_fold_right_last_subtraction_order) + { + // fold_right_last({1,2,3,4}, minus) = 1 - (2 - (3 - 4)) = 1 - (2 - (-1)) = 1 - 3 = -2 + std::vector v{1, 2, 3, 4}; + auto result = etl::ranges::fold_right_last(v, std::minus{}); + CHECK_EQUAL(-2, result); + } + + //************************************************************************* + TEST(ranges_copy_iterator) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dst(5); + + auto result = etl::ranges::copy(src.begin(), src.end(), dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + CHECK_EQUAL(4, dst[3]); + CHECK_EQUAL(5, dst[4]); + } + + //************************************************************************* + TEST(ranges_copy_range) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dst(5); + + auto result = etl::ranges::copy(src, dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + CHECK_EQUAL(4, dst[3]); + CHECK_EQUAL(5, dst[4]); + } + + //************************************************************************* + TEST(ranges_copy_empty) + { + std::vector src{}; + std::vector dst{}; + + auto result = etl::ranges::copy(src.begin(), src.end(), dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + } + + //************************************************************************* + TEST(ranges_copy_empty_range) + { + std::vector src{}; + std::vector dst{}; + + auto result = etl::ranges::copy(src, dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + } + + //************************************************************************* + TEST(ranges_copy_single_element) + { + std::vector src{42}; + std::vector dst(1); + + auto result = etl::ranges::copy(src.begin(), src.end(), dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(42, dst[0]); + } + + //************************************************************************* + TEST(ranges_copy_array) + { + int src[] = {10, 20, 30}; + int dst[3] = {}; + + auto result = etl::ranges::copy(src, dst); + (void)result; + + CHECK_EQUAL(10, dst[0]); + CHECK_EQUAL(20, dst[1]); + CHECK_EQUAL(30, dst[2]); + } + + //************************************************************************* + TEST(ranges_copy_to_different_container) + { + std::vector src{1, 2, 3}; + std::array dst{}; + + auto result = etl::ranges::copy(src, dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + } + + //************************************************************************* + TEST(ranges_copy_if_iterator) + { + std::vector src{1, 2, 3, 4, 5, 6}; + std::vector dst(3); + + auto result = etl::ranges::copy_if(src.begin(), src.end(), dst.begin(), [](int x) { return x % 2 == 0; }); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin() + 3); + CHECK_EQUAL(2, dst[0]); + CHECK_EQUAL(4, dst[1]); + CHECK_EQUAL(6, dst[2]); + } + + //************************************************************************* + TEST(ranges_copy_if_range) + { + std::vector src{1, 2, 3, 4, 5, 6}; + std::vector dst(3); + + auto result = etl::ranges::copy_if(src, dst.begin(), [](int x) { return x % 2 == 0; }); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin() + 3); + CHECK_EQUAL(2, dst[0]); + CHECK_EQUAL(4, dst[1]); + CHECK_EQUAL(6, dst[2]); + } + + //************************************************************************* + TEST(ranges_copy_if_none_match) + { + std::vector src{1, 3, 5}; + std::vector dst(3, 0); + + auto result = etl::ranges::copy_if(src.begin(), src.end(), dst.begin(), [](int x) { return x % 2 == 0; }); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + CHECK_EQUAL(0, dst[0]); + CHECK_EQUAL(0, dst[1]); + CHECK_EQUAL(0, dst[2]); + } + + //************************************************************************* + TEST(ranges_copy_if_all_match) + { + std::vector src{2, 4, 6}; + std::vector dst(3); + + auto result = etl::ranges::copy_if(src, dst.begin(), [](int x) { return x % 2 == 0; }); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(2, dst[0]); + CHECK_EQUAL(4, dst[1]); + CHECK_EQUAL(6, dst[2]); + } + + //************************************************************************* + TEST(ranges_copy_if_empty) + { + std::vector src{}; + std::vector dst{}; + + auto result = etl::ranges::copy_if(src.begin(), src.end(), dst.begin(), [](int) { return true; }); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + } + + //************************************************************************* + TEST(ranges_copy_if_with_projection) + { + struct Item { int value; }; + std::vector src{{1}, {2}, {3}, {4}, {5}, {6}}; + std::vector dst(3); + + auto result = etl::ranges::copy_if(src.begin(), src.end(), dst.begin(), + [](int v) { return v % 2 == 0; }, + &Item::value); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin() + 3); + CHECK_EQUAL(2, dst[0].value); + CHECK_EQUAL(4, dst[1].value); + CHECK_EQUAL(6, dst[2].value); + } + + //************************************************************************* + TEST(ranges_copy_if_range_with_projection) + { + struct Item { int value; }; + std::vector src{{1}, {2}, {3}, {4}, {5}, {6}}; + std::vector dst(3); + + auto result = etl::ranges::copy_if(src, dst.begin(), + [](int v) { return v % 2 == 0; }, + &Item::value); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin() + 3); + CHECK_EQUAL(2, dst[0].value); + CHECK_EQUAL(4, dst[1].value); + CHECK_EQUAL(6, dst[2].value); + } + + //************************************************************************* + TEST(ranges_copy_n_basic) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dst(3); + + auto result = etl::ranges::copy_n(src.begin(), 3, dst.begin()); + + CHECK(result.in == src.begin() + 3); + CHECK(result.out == dst.end()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + } + + //************************************************************************* + TEST(ranges_copy_n_zero) + { + std::vector src{1, 2, 3}; + std::vector dst(3, 0); + + auto result = etl::ranges::copy_n(src.begin(), 0, dst.begin()); + + CHECK(result.in == src.begin()); + CHECK(result.out == dst.begin()); + CHECK_EQUAL(0, dst[0]); + CHECK_EQUAL(0, dst[1]); + CHECK_EQUAL(0, dst[2]); + } + + //************************************************************************* + TEST(ranges_copy_n_all_elements) + { + std::vector src{10, 20, 30, 40, 50}; + std::vector dst(5); + + auto result = etl::ranges::copy_n(src.begin(), 5, dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(10, dst[0]); + CHECK_EQUAL(20, dst[1]); + CHECK_EQUAL(30, dst[2]); + CHECK_EQUAL(40, dst[3]); + CHECK_EQUAL(50, dst[4]); + } + + //************************************************************************* + TEST(ranges_copy_n_single_element) + { + std::vector src{42, 99}; + std::vector dst(1); + + auto result = etl::ranges::copy_n(src.begin(), 1, dst.begin()); + + CHECK(result.in == src.begin() + 1); + CHECK(result.out == dst.end()); + CHECK_EQUAL(42, dst[0]); + } + + //************************************************************************* + TEST(ranges_copy_n_to_different_container) + { + std::vector src{1, 2, 3, 4, 5}; + std::array dst{}; + + auto result = etl::ranges::copy_n(src.begin(), 3, dst.begin()); + + CHECK(result.in == src.begin() + 3); + CHECK(result.out == dst.end()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + } + + //************************************************************************* + TEST(ranges_copy_backward_iterator) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dst(5); + + auto result = etl::ranges::copy_backward(src.begin(), src.end(), dst.end()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + CHECK_EQUAL(4, dst[3]); + CHECK_EQUAL(5, dst[4]); + } + + //************************************************************************* + TEST(ranges_copy_backward_range) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dst(5); + + auto result = etl::ranges::copy_backward(src, dst.end()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + CHECK_EQUAL(4, dst[3]); + CHECK_EQUAL(5, dst[4]); + } + + //************************************************************************* + TEST(ranges_copy_backward_empty) + { + std::vector src{}; + std::vector dst{}; + + auto result = etl::ranges::copy_backward(src.begin(), src.end(), dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + } + + //************************************************************************* + TEST(ranges_copy_backward_empty_range) + { + std::vector src{}; + std::vector dst{}; + + auto result = etl::ranges::copy_backward(src, dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + } + + //************************************************************************* + TEST(ranges_copy_backward_single_element) + { + std::vector src{42}; + std::vector dst(1); + + auto result = etl::ranges::copy_backward(src.begin(), src.end(), dst.end()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + CHECK_EQUAL(42, dst[0]); + } + + //************************************************************************* + TEST(ranges_copy_backward_to_different_container) + { + std::vector src{1, 2, 3}; + std::array dst{}; + + auto result = etl::ranges::copy_backward(src, dst.end()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + } + + //************************************************************************* + TEST(ranges_copy_backward_overlapping) + { + std::vector v{1, 2, 3, 4, 5, 0, 0}; + + auto result = etl::ranges::copy_backward(v.begin(), v.begin() + 5, v.end()); + + CHECK(result.in == v.begin() + 5); + CHECK(result.out == v.begin() + 2); + CHECK_EQUAL(1, v[0]); + CHECK_EQUAL(2, v[1]); + CHECK_EQUAL(1, v[2]); + CHECK_EQUAL(2, v[3]); + CHECK_EQUAL(3, v[4]); + CHECK_EQUAL(4, v[5]); + CHECK_EQUAL(5, v[6]); + } + + //************************************************************************* + TEST(ranges_move_iterator) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dst(5); + + auto result = etl::ranges::move(src.begin(), src.end(), dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + CHECK_EQUAL(4, dst[3]); + CHECK_EQUAL(5, dst[4]); + } + + //************************************************************************* + TEST(ranges_move_range) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dst(5); + + auto result = etl::ranges::move(src, dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + CHECK_EQUAL(4, dst[3]); + CHECK_EQUAL(5, dst[4]); + } + + //************************************************************************* + TEST(ranges_move_empty) + { + std::vector src{}; + std::vector dst{}; + + auto result = etl::ranges::move(src.begin(), src.end(), dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + } + + //************************************************************************* + TEST(ranges_move_empty_range) + { + std::vector src{}; + std::vector dst{}; + + auto result = etl::ranges::move(src, dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + } + + //************************************************************************* + TEST(ranges_move_single_element) + { + std::vector src{42}; + std::vector dst(1); + + auto result = etl::ranges::move(src.begin(), src.end(), dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(42, dst[0]); + } + + //************************************************************************* + TEST(ranges_move_unique_ptr) + { + std::vector> src; + src.push_back(std::make_unique(10)); + src.push_back(std::make_unique(20)); + src.push_back(std::make_unique(30)); + + std::vector> dst(3); + + auto result = etl::ranges::move(src.begin(), src.end(), dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(10, *dst[0]); + CHECK_EQUAL(20, *dst[1]); + CHECK_EQUAL(30, *dst[2]); + CHECK(src[0] == nullptr); + CHECK(src[1] == nullptr); + CHECK(src[2] == nullptr); + } + + //************************************************************************* + TEST(ranges_move_to_different_container) + { + std::vector src{1, 2, 3}; + std::array dst{}; + + auto result = etl::ranges::move(src, dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + } + + //************************************************************************* + TEST(ranges_move_backward_iterator) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dst(5); + + auto result = etl::ranges::move_backward(src.begin(), src.end(), dst.end()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + CHECK_EQUAL(4, dst[3]); + CHECK_EQUAL(5, dst[4]); + } + + //************************************************************************* + TEST(ranges_move_backward_range) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dst(5); + + auto result = etl::ranges::move_backward(src, dst.end()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + CHECK_EQUAL(4, dst[3]); + CHECK_EQUAL(5, dst[4]); + } + + //************************************************************************* + TEST(ranges_move_backward_empty) + { + std::vector src{}; + std::vector dst{}; + + auto result = etl::ranges::move_backward(src.begin(), src.end(), dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + } + + //************************************************************************* + TEST(ranges_move_backward_empty_range) + { + std::vector src{}; + std::vector dst{}; + + auto result = etl::ranges::move_backward(src, dst.begin()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + } + + //************************************************************************* + TEST(ranges_move_backward_single_element) + { + std::vector src{42}; + std::vector dst(1); + + auto result = etl::ranges::move_backward(src.begin(), src.end(), dst.end()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + CHECK_EQUAL(42, dst[0]); + } + + //************************************************************************* + TEST(ranges_move_backward_unique_ptr) + { + std::vector> src; + src.push_back(std::make_unique(10)); + src.push_back(std::make_unique(20)); + src.push_back(std::make_unique(30)); + + std::vector> dst(3); + + auto result = etl::ranges::move_backward(src.begin(), src.end(), dst.end()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + CHECK_EQUAL(10, *dst[0]); + CHECK_EQUAL(20, *dst[1]); + CHECK_EQUAL(30, *dst[2]); + CHECK(src[0] == nullptr); + CHECK(src[1] == nullptr); + CHECK(src[2] == nullptr); + } + + //************************************************************************* + TEST(ranges_move_backward_to_different_container) + { + std::vector src{1, 2, 3}; + std::array dst{}; + + auto result = etl::ranges::move_backward(src, dst.end()); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + CHECK_EQUAL(1, dst[0]); + CHECK_EQUAL(2, dst[1]); + CHECK_EQUAL(3, dst[2]); + } + + //************************************************************************* + TEST(ranges_move_backward_overlapping) + { + std::vector v{1, 2, 3, 4, 5, 0, 0}; + + auto result = etl::ranges::move_backward(v.begin(), v.begin() + 5, v.end()); + + CHECK(result.in == v.begin() + 5); + CHECK(result.out == v.begin() + 2); + CHECK_EQUAL(1, v[0]); + CHECK_EQUAL(2, v[1]); + CHECK_EQUAL(1, v[2]); + CHECK_EQUAL(2, v[3]); + CHECK_EQUAL(3, v[4]); + CHECK_EQUAL(4, v[5]); + CHECK_EQUAL(5, v[6]); + } + + //************************************************************************* + TEST(ranges_swap_ranges_iterator) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{6, 7, 8, 9, 10}; + + auto result = etl::ranges::swap_ranges(v1.begin(), v1.end(), v2.begin(), v2.end()); + + CHECK(result.in1 == v1.end()); + CHECK(result.in2 == v2.end()); + CHECK_EQUAL(6, v1[0]); + CHECK_EQUAL(7, v1[1]); + CHECK_EQUAL(8, v1[2]); + CHECK_EQUAL(9, v1[3]); + CHECK_EQUAL(10, v1[4]); + CHECK_EQUAL(1, v2[0]); + CHECK_EQUAL(2, v2[1]); + CHECK_EQUAL(3, v2[2]); + CHECK_EQUAL(4, v2[3]); + CHECK_EQUAL(5, v2[4]); + } + + //************************************************************************* + TEST(ranges_swap_ranges_range) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{6, 7, 8, 9, 10}; + + auto result = etl::ranges::swap_ranges(v1, v2); + + CHECK(result.in1 == v1.end()); + CHECK(result.in2 == v2.end()); + CHECK_EQUAL(6, v1[0]); + CHECK_EQUAL(7, v1[1]); + CHECK_EQUAL(8, v1[2]); + CHECK_EQUAL(9, v1[3]); + CHECK_EQUAL(10, v1[4]); + CHECK_EQUAL(1, v2[0]); + CHECK_EQUAL(2, v2[1]); + CHECK_EQUAL(3, v2[2]); + CHECK_EQUAL(4, v2[3]); + CHECK_EQUAL(5, v2[4]); + } + + //************************************************************************* + TEST(ranges_swap_ranges_empty) + { + std::vector v1{}; + std::vector v2{}; + + auto result = etl::ranges::swap_ranges(v1.begin(), v1.end(), v2.begin(), v2.end()); + + CHECK(result.in1 == v1.end()); + CHECK(result.in2 == v2.end()); + } + + //************************************************************************* + TEST(ranges_swap_ranges_empty_range) + { + std::vector v1{}; + std::vector v2{}; + + auto result = etl::ranges::swap_ranges(v1, v2); + + CHECK(result.in1 == v1.end()); + CHECK(result.in2 == v2.end()); + } + + //************************************************************************* + TEST(ranges_swap_ranges_different_lengths_first_shorter) + { + std::vector v1{1, 2, 3}; + std::vector v2{6, 7, 8, 9, 10}; + + auto result = etl::ranges::swap_ranges(v1.begin(), v1.end(), v2.begin(), v2.end()); + + CHECK(result.in1 == v1.end()); + CHECK(result.in2 == v2.begin() + 3); + CHECK_EQUAL(6, v1[0]); + CHECK_EQUAL(7, v1[1]); + CHECK_EQUAL(8, v1[2]); + CHECK_EQUAL(1, v2[0]); + CHECK_EQUAL(2, v2[1]); + CHECK_EQUAL(3, v2[2]); + CHECK_EQUAL(9, v2[3]); + CHECK_EQUAL(10, v2[4]); + } + + //************************************************************************* + TEST(ranges_swap_ranges_different_lengths_second_shorter) + { + std::vector v1{1, 2, 3, 4, 5}; + std::vector v2{6, 7, 8}; + + auto result = etl::ranges::swap_ranges(v1, v2); + + CHECK(result.in1 == v1.begin() + 3); + CHECK(result.in2 == v2.end()); + CHECK_EQUAL(6, v1[0]); + CHECK_EQUAL(7, v1[1]); + CHECK_EQUAL(8, v1[2]); + CHECK_EQUAL(4, v1[3]); + CHECK_EQUAL(5, v1[4]); + CHECK_EQUAL(1, v2[0]); + CHECK_EQUAL(2, v2[1]); + CHECK_EQUAL(3, v2[2]); + } + + //************************************************************************* + TEST(ranges_swap_ranges_single_element) + { + std::vector v1{42}; + std::vector v2{99}; + + auto result = etl::ranges::swap_ranges(v1.begin(), v1.end(), v2.begin(), v2.end()); + + CHECK(result.in1 == v1.end()); + CHECK(result.in2 == v2.end()); + CHECK_EQUAL(99, v1[0]); + CHECK_EQUAL(42, v2[0]); + } + + //************************************************************************* + TEST(ranges_swap_ranges_array) + { + int a1[] = {10, 20, 30}; + int a2[] = {40, 50, 60}; + + auto result = etl::ranges::swap_ranges(a1, a2); + (void)result; + + CHECK_EQUAL(40, a1[0]); + CHECK_EQUAL(50, a1[1]); + CHECK_EQUAL(60, a1[2]); + CHECK_EQUAL(10, a2[0]); + CHECK_EQUAL(20, a2[1]); + CHECK_EQUAL(30, a2[2]); + } + + //************************************************************************* + TEST(ranges_transform_unary_iterator) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dst(5); + + auto result = etl::ranges::transform(src.begin(), src.end(), dst.begin(), [](int x) { return x * 2; }); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(2, dst[0]); + CHECK_EQUAL(4, dst[1]); + CHECK_EQUAL(6, dst[2]); + CHECK_EQUAL(8, dst[3]); + CHECK_EQUAL(10, dst[4]); + } + + //************************************************************************* + TEST(ranges_transform_unary_range) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dst(5); + + auto result = etl::ranges::transform(src, dst.begin(), [](int x) { return x * 2; }); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(2, dst[0]); + CHECK_EQUAL(4, dst[1]); + CHECK_EQUAL(6, dst[2]); + CHECK_EQUAL(8, dst[3]); + CHECK_EQUAL(10, dst[4]); + } + + //************************************************************************* + TEST(ranges_transform_unary_empty) + { + std::vector src{}; + std::vector dst{}; + + auto result = etl::ranges::transform(src.begin(), src.end(), dst.begin(), [](int x) { return x; }); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.begin()); + } + + //************************************************************************* + TEST(ranges_transform_unary_with_projection) + { + struct Item { int value; }; + std::vector src{{1}, {2}, {3}}; + std::vector dst(3); + + auto result = etl::ranges::transform(src.begin(), src.end(), dst.begin(), + [](int v) { return v * 10; }, + &Item::value); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(10, dst[0]); + CHECK_EQUAL(20, dst[1]); + CHECK_EQUAL(30, dst[2]); + } + + //************************************************************************* + TEST(ranges_transform_unary_range_with_projection) + { + struct Item { int value; }; + std::vector src{{1}, {2}, {3}}; + std::vector dst(3); + + auto result = etl::ranges::transform(src, dst.begin(), + [](int v) { return v * 10; }, + &Item::value); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(10, dst[0]); + CHECK_EQUAL(20, dst[1]); + CHECK_EQUAL(30, dst[2]); + } + + //************************************************************************* + TEST(ranges_transform_binary_iterator) + { + std::vector src1{1, 2, 3, 4, 5}; + std::vector src2{10, 20, 30, 40, 50}; + std::vector dst(5); + + auto result = etl::ranges::transform(src1.begin(), src1.end(), + src2.begin(), src2.end(), + dst.begin(), + [](int a, int b) { return a + b; }); + + CHECK(result.in1 == src1.end()); + CHECK(result.in2 == src2.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(11, dst[0]); + CHECK_EQUAL(22, dst[1]); + CHECK_EQUAL(33, dst[2]); + CHECK_EQUAL(44, dst[3]); + CHECK_EQUAL(55, dst[4]); + } + + //************************************************************************* + TEST(ranges_transform_binary_range) + { + std::vector src1{1, 2, 3, 4, 5}; + std::vector src2{10, 20, 30, 40, 50}; + std::vector dst(5); + + auto result = etl::ranges::transform(src1, src2, dst.begin(), + [](int a, int b) { return a + b; }); + + CHECK(result.in1 == src1.end()); + CHECK(result.in2 == src2.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(11, dst[0]); + CHECK_EQUAL(22, dst[1]); + CHECK_EQUAL(33, dst[2]); + CHECK_EQUAL(44, dst[3]); + CHECK_EQUAL(55, dst[4]); + } + + //************************************************************************* + TEST(ranges_transform_binary_different_lengths) + { + std::vector src1{1, 2, 3, 4, 5}; + std::vector src2{10, 20, 30}; + std::vector dst(5, 0); + + auto result = etl::ranges::transform(src1.begin(), src1.end(), + src2.begin(), src2.end(), + dst.begin(), + [](int a, int b) { return a + b; }); + + CHECK(result.in1 == src1.begin() + 3); + CHECK(result.in2 == src2.end()); + CHECK(result.out == dst.begin() + 3); + CHECK_EQUAL(11, dst[0]); + CHECK_EQUAL(22, dst[1]); + CHECK_EQUAL(33, dst[2]); + CHECK_EQUAL(0, dst[3]); + CHECK_EQUAL(0, dst[4]); + } + + //************************************************************************* + TEST(ranges_transform_binary_empty) + { + std::vector src1{}; + std::vector src2{}; + std::vector dst{}; + + auto result = etl::ranges::transform(src1.begin(), src1.end(), + src2.begin(), src2.end(), + dst.begin(), + [](int a, int b) { return a + b; }); + + CHECK(result.in1 == src1.end()); + CHECK(result.in2 == src2.end()); + CHECK(result.out == dst.begin()); + } + + //************************************************************************* + TEST(ranges_transform_binary_with_projections) + { + struct Item { int value; }; + std::vector src1{{1}, {2}, {3}}; + std::vector src2{{10}, {20}, {30}}; + std::vector dst(3); + + auto result = etl::ranges::transform(src1.begin(), src1.end(), + src2.begin(), src2.end(), + dst.begin(), + [](int a, int b) { return a + b; }, + &Item::value, + &Item::value); + + CHECK(result.in1 == src1.end()); + CHECK(result.in2 == src2.end()); + CHECK(result.out == dst.end()); + CHECK_EQUAL(11, dst[0]); + CHECK_EQUAL(22, dst[1]); + CHECK_EQUAL(33, dst[2]); + } + + //************************************************************************* + TEST(ranges_transform_unary_to_different_type) + { + std::vector src{1, 2, 3}; + std::vector dst(3); + + auto result = etl::ranges::transform(src, dst.begin(), + [](int x) { return std::to_string(x); }); + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + CHECK(dst[0] == "1"); + CHECK(dst[1] == "2"); + CHECK(dst[2] == "3"); + } + + //************************************************************************* + TEST(ranges_transform_unary_in_place) + { + std::vector v{1, 2, 3, 4, 5}; + + auto result = etl::ranges::transform(v, v.begin(), [](int x) { return x * x; }); + + CHECK(result.in == v.end()); + CHECK(result.out == v.end()); + CHECK_EQUAL(1, v[0]); + CHECK_EQUAL(4, v[1]); + CHECK_EQUAL(9, v[2]); + CHECK_EQUAL(16, v[3]); + CHECK_EQUAL(25, v[4]); + } + + //************************************************************************* + TEST(ranges_replace_iterator) + { + std::vector vec{1, 2, 3, 2, 5, 2}; + auto it = etl::ranges::replace(vec.begin(), vec.end(), 2, 9); + CHECK(it == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(9, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(9, vec[3]); + CHECK_EQUAL(5, vec[4]); + CHECK_EQUAL(9, vec[5]); + } + + //************************************************************************* + TEST(ranges_replace_range) + { + std::vector vec{1, 2, 3, 2, 5, 2}; + auto it = etl::ranges::replace(vec, 2, 9); + CHECK(it == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(9, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(9, vec[3]); + CHECK_EQUAL(5, vec[4]); + CHECK_EQUAL(9, vec[5]); + } + + //************************************************************************* + TEST(ranges_replace_no_match) + { + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::replace(vec.begin(), vec.end(), 9, 0); + CHECK(it == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_replace_empty) + { + std::vector vec; + auto it = etl::ranges::replace(vec.begin(), vec.end(), 1, 2); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_replace_with_projection) + { + auto proj = [](const int& v) { return v * 2; }; + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::replace(vec.begin(), vec.end(), 6, 9, proj); + CHECK(it == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(9, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_replace_with_projection_range) + { + auto proj = [](const int& v) { return v * 2; }; + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::replace(vec, 6, 9, proj); + CHECK(it == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(9, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_replace_if_iterator) + { + auto is_even = [](int v) { return v % 2 == 0; }; + std::vector vec{1, 2, 3, 4, 5, 6}; + auto it = etl::ranges::replace_if(vec.begin(), vec.end(), is_even, 0); + CHECK(it == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(0, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(0, vec[3]); + CHECK_EQUAL(5, vec[4]); + CHECK_EQUAL(0, vec[5]); + } + + //************************************************************************* + TEST(ranges_replace_if_range) + { + auto is_even = [](int v) { return v % 2 == 0; }; + std::vector vec{1, 2, 3, 4, 5, 6}; + auto it = etl::ranges::replace_if(vec, is_even, 0); + CHECK(it == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(0, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(0, vec[3]); + CHECK_EQUAL(5, vec[4]); + CHECK_EQUAL(0, vec[5]); + } + + //************************************************************************* + TEST(ranges_replace_if_no_match) + { + auto is_negative = [](int v) { return v < 0; }; + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::replace_if(vec.begin(), vec.end(), is_negative, 0); + CHECK(it == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_replace_if_empty) + { + auto always_true = [](int) { return true; }; + std::vector vec; + auto it = etl::ranges::replace_if(vec.begin(), vec.end(), always_true, 0); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_replace_if_with_projection) + { + auto proj = [](const int& v) { return v * 2; }; + auto is_greater_than_6 = [](int v) { return v > 6; }; + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::replace_if(vec.begin(), vec.end(), is_greater_than_6, 0, proj); + CHECK(it == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(0, vec[3]); + CHECK_EQUAL(0, vec[4]); + } + + //************************************************************************* + TEST(ranges_replace_if_with_projection_range) + { + auto proj = [](const int& v) { return v * 2; }; + auto is_greater_than_6 = [](int v) { return v > 6; }; + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::replace_if(vec, is_greater_than_6, 0, proj); + CHECK(it == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(0, vec[3]); + CHECK_EQUAL(0, vec[4]); + } + + //************************************************************************* + TEST(ranges_replace_copy_iterator) + { + std::vector vec{1, 2, 3, 2, 5, 2}; + std::vector out(6); + auto [in_it, out_it] = etl::ranges::replace_copy(vec.begin(), vec.end(), out.begin(), 2, 9); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(9, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(9, out[3]); + CHECK_EQUAL(5, out[4]); + CHECK_EQUAL(9, out[5]); + } + + //************************************************************************* + TEST(ranges_replace_copy_range) + { + std::vector vec{1, 2, 3, 2, 5, 2}; + std::vector out(6); + auto [in_it, out_it] = etl::ranges::replace_copy(vec, out.begin(), 2, 9); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(9, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(9, out[3]); + CHECK_EQUAL(5, out[4]); + CHECK_EQUAL(9, out[5]); + } + + //************************************************************************* + TEST(ranges_replace_copy_no_match) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5); + auto [in_it, out_it] = etl::ranges::replace_copy(vec.begin(), vec.end(), out.begin(), 9, 0); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(4, out[3]); + CHECK_EQUAL(5, out[4]); + } + + //************************************************************************* + TEST(ranges_replace_copy_empty) + { + std::vector vec; + std::vector out; + auto [in_it, out_it] = etl::ranges::replace_copy(vec.begin(), vec.end(), out.begin(), 1, 2); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + } + + //************************************************************************* + TEST(ranges_replace_copy_with_projection) + { + auto proj = [](const int& v) { return v * 2; }; + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5); + auto [in_it, out_it] = etl::ranges::replace_copy(vec.begin(), vec.end(), out.begin(), 6, 9, proj); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(9, out[2]); + CHECK_EQUAL(4, out[3]); + CHECK_EQUAL(5, out[4]); + } + + //************************************************************************* + TEST(ranges_replace_copy_with_projection_range) + { + auto proj = [](const int& v) { return v * 2; }; + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5); + auto [in_it, out_it] = etl::ranges::replace_copy(vec, out.begin(), 6, 9, proj); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(9, out[2]); + CHECK_EQUAL(4, out[3]); + CHECK_EQUAL(5, out[4]); + } + + //************************************************************************* + TEST(ranges_replace_copy_if_iterator) + { + auto is_even = [](int v) { return v % 2 == 0; }; + std::vector vec{1, 2, 3, 4, 5, 6}; + std::vector out(6); + auto [in_it, out_it] = etl::ranges::replace_copy_if(vec.begin(), vec.end(), out.begin(), is_even, 0); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(0, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(0, out[3]); + CHECK_EQUAL(5, out[4]); + CHECK_EQUAL(0, out[5]); + } + + //************************************************************************* + TEST(ranges_replace_copy_if_range) + { + auto is_even = [](int v) { return v % 2 == 0; }; + std::vector vec{1, 2, 3, 4, 5, 6}; + std::vector out(6); + auto [in_it, out_it] = etl::ranges::replace_copy_if(vec, out.begin(), is_even, 0); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(0, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(0, out[3]); + CHECK_EQUAL(5, out[4]); + CHECK_EQUAL(0, out[5]); + } + + //************************************************************************* + TEST(ranges_replace_copy_if_no_match) + { + auto is_negative = [](int v) { return v < 0; }; + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5); + auto [in_it, out_it] = etl::ranges::replace_copy_if(vec.begin(), vec.end(), out.begin(), is_negative, 0); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(4, out[3]); + CHECK_EQUAL(5, out[4]); + } + + //************************************************************************* + TEST(ranges_replace_copy_if_empty) + { + auto always_true = [](int) { return true; }; + std::vector vec; + std::vector out; + auto [in_it, out_it] = etl::ranges::replace_copy_if(vec.begin(), vec.end(), out.begin(), always_true, 0); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + } + + //************************************************************************* + TEST(ranges_replace_copy_if_with_projection) + { + auto proj = [](const int& v) { return v * 2; }; + auto is_greater_than_6 = [](int v) { return v > 6; }; + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5); + auto [in_it, out_it] = etl::ranges::replace_copy_if(vec.begin(), vec.end(), out.begin(), is_greater_than_6, 0, proj); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(0, out[3]); + CHECK_EQUAL(0, out[4]); + } + + //************************************************************************* + TEST(ranges_replace_copy_if_with_projection_range) + { + auto proj = [](const int& v) { return v * 2; }; + auto is_greater_than_6 = [](int v) { return v > 6; }; + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5); + auto [in_it, out_it] = etl::ranges::replace_copy_if(vec, out.begin(), is_greater_than_6, 0, proj); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(0, out[3]); + CHECK_EQUAL(0, out[4]); + } + + //************************************************************************* + TEST(ranges_remove_iterator) + { + std::vector vec{1, 2, 3, 2, 5, 2}; + auto result = etl::ranges::remove(vec.begin(), vec.end(), 2); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(3, vec[1]); + CHECK_EQUAL(5, vec[2]); + } + + //************************************************************************* + TEST(ranges_remove_range) + { + std::vector vec{1, 2, 3, 2, 5, 2}; + auto result = etl::ranges::remove(vec, 2); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(3, vec[1]); + CHECK_EQUAL(5, vec[2]); + } + + //************************************************************************* + TEST(ranges_remove_no_match) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::remove(vec.begin(), vec.end(), 9); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_remove_empty) + { + std::vector vec; + auto result = etl::ranges::remove(vec.begin(), vec.end(), 1); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_remove_all_same) + { + std::vector vec{2, 2, 2, 2}; + auto result = etl::ranges::remove(vec, 2); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_remove_with_projection) + { + auto proj = [](const int& v) { return v * 2; }; + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::remove(vec.begin(), vec.end(), 6, proj); + CHECK(result.begin() == vec.begin() + 4); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(4, vec[2]); + CHECK_EQUAL(5, vec[3]); + } + + //************************************************************************* + TEST(ranges_remove_with_projection_range) + { + auto proj = [](const int& v) { return v * 2; }; + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::remove(vec, 6, proj); + CHECK(result.begin() == vec.begin() + 4); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(4, vec[2]); + CHECK_EQUAL(5, vec[3]); + } + + //************************************************************************* + TEST(ranges_remove_if_iterator) + { + auto is_even = [](int v) { return v % 2 == 0; }; + std::vector vec{1, 2, 3, 4, 5, 6}; + auto result = etl::ranges::remove_if(vec.begin(), vec.end(), is_even); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(3, vec[1]); + CHECK_EQUAL(5, vec[2]); + } + + //************************************************************************* + TEST(ranges_remove_if_range) + { + auto is_even = [](int v) { return v % 2 == 0; }; + std::vector vec{1, 2, 3, 4, 5, 6}; + auto result = etl::ranges::remove_if(vec, is_even); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(3, vec[1]); + CHECK_EQUAL(5, vec[2]); + } + + //************************************************************************* + TEST(ranges_remove_if_no_match) + { + auto is_negative = [](int v) { return v < 0; }; + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::remove_if(vec.begin(), vec.end(), is_negative); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_remove_if_empty) + { + auto is_even = [](int v) { return v % 2 == 0; }; + std::vector vec; + auto result = etl::ranges::remove_if(vec.begin(), vec.end(), is_even); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_remove_if_all_match) + { + auto always_true = [](int) { return true; }; + std::vector vec{1, 2, 3, 4}; + auto result = etl::ranges::remove_if(vec, always_true); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_remove_if_with_projection) + { + auto proj = [](const int& v) { return v * 2; }; + auto is_greater_than_6 = [](int v) { return v > 6; }; + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::remove_if(vec.begin(), vec.end(), is_greater_than_6, proj); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + } + + //************************************************************************* + TEST(ranges_remove_if_with_projection_range) + { + auto proj = [](const int& v) { return v * 2; }; + auto is_greater_than_6 = [](int v) { return v > 6; }; + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::remove_if(vec, is_greater_than_6, proj); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + } + + //************************************************************************* + TEST(ranges_remove_copy_iterator) + { + std::vector vec{1, 2, 3, 2, 5, 2}; + std::vector out(6, 0); + auto [in_it, out_it] = etl::ranges::remove_copy(vec.begin(), vec.end(), out.begin(), 2); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 3); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(3, out[1]); + CHECK_EQUAL(5, out[2]); + } + + //************************************************************************* + TEST(ranges_remove_copy_range) + { + std::vector vec{1, 2, 3, 2, 5, 2}; + std::vector out(6, 0); + auto [in_it, out_it] = etl::ranges::remove_copy(vec, out.begin(), 2); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 3); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(3, out[1]); + CHECK_EQUAL(5, out[2]); + } + + //************************************************************************* + TEST(ranges_remove_copy_no_match) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::remove_copy(vec.begin(), vec.end(), out.begin(), 9); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 5); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(4, out[3]); + CHECK_EQUAL(5, out[4]); + } + + //************************************************************************* + TEST(ranges_remove_copy_empty) + { + std::vector vec; + std::vector out; + auto [in_it, out_it] = etl::ranges::remove_copy(vec.begin(), vec.end(), out.begin(), 1); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin()); + } + + //************************************************************************* + TEST(ranges_remove_copy_all_same) + { + std::vector vec{2, 2, 2, 2}; + std::vector out(4, 0); + auto [in_it, out_it] = etl::ranges::remove_copy(vec, out.begin(), 2); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin()); + } + + //************************************************************************* + TEST(ranges_remove_copy_with_projection) + { + auto proj = [](const int& v) { return v * 2; }; + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::remove_copy(vec.begin(), vec.end(), out.begin(), 6, proj); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 4); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(4, out[2]); + CHECK_EQUAL(5, out[3]); + } + + //************************************************************************* + TEST(ranges_remove_copy_with_projection_range) + { + auto proj = [](const int& v) { return v * 2; }; + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::remove_copy(vec, out.begin(), 6, proj); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 4); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(4, out[2]); + CHECK_EQUAL(5, out[3]); + } + + //************************************************************************* + TEST(ranges_fill_iterator) + { + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::fill(vec.begin(), vec.end(), 7); + CHECK(it == vec.end()); + CHECK_EQUAL(7, vec[0]); + CHECK_EQUAL(7, vec[1]); + CHECK_EQUAL(7, vec[2]); + CHECK_EQUAL(7, vec[3]); + CHECK_EQUAL(7, vec[4]); + } + + //************************************************************************* + TEST(ranges_fill_range) + { + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::fill(vec, 7); + CHECK(it == vec.end()); + CHECK_EQUAL(7, vec[0]); + CHECK_EQUAL(7, vec[1]); + CHECK_EQUAL(7, vec[2]); + CHECK_EQUAL(7, vec[3]); + CHECK_EQUAL(7, vec[4]); + } + + //************************************************************************* + TEST(ranges_fill_empty) + { + std::vector vec; + auto it = etl::ranges::fill(vec.begin(), vec.end(), 7); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_fill_empty_range) + { + std::vector vec; + auto it = etl::ranges::fill(vec, 7); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_fill_partial) + { + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::fill(vec.begin(), vec.begin() + 3, 9); + CHECK(it == vec.begin() + 3); + CHECK_EQUAL(9, vec[0]); + CHECK_EQUAL(9, vec[1]); + CHECK_EQUAL(9, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_fill_n_iterator) + { + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::fill_n(vec.begin(), 3, 7); + CHECK(it == vec.begin() + 3); + CHECK_EQUAL(7, vec[0]); + CHECK_EQUAL(7, vec[1]); + CHECK_EQUAL(7, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_fill_n_zero) + { + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::fill_n(vec.begin(), 0, 7); + CHECK(it == vec.begin()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_fill_n_all) + { + std::vector vec{1, 2, 3, 4, 5}; + auto it = etl::ranges::fill_n(vec.begin(), 5, 0); + CHECK(it == vec.end()); + CHECK_EQUAL(0, vec[0]); + CHECK_EQUAL(0, vec[1]); + CHECK_EQUAL(0, vec[2]); + CHECK_EQUAL(0, vec[3]); + CHECK_EQUAL(0, vec[4]); + } + + //************************************************************************* + TEST(ranges_generate_iterator) + { + std::vector vec{1, 2, 3, 4, 5}; + int counter = 10; + auto it = etl::ranges::generate(vec.begin(), vec.end(), [&counter]() { return counter++; }); + CHECK(it == vec.end()); + CHECK_EQUAL(10, vec[0]); + CHECK_EQUAL(11, vec[1]); + CHECK_EQUAL(12, vec[2]); + CHECK_EQUAL(13, vec[3]); + CHECK_EQUAL(14, vec[4]); + } + + //************************************************************************* + TEST(ranges_generate_range) + { + std::vector vec{1, 2, 3, 4, 5}; + int counter = 10; + auto it = etl::ranges::generate(vec, [&counter]() { return counter++; }); + CHECK(it == vec.end()); + CHECK_EQUAL(10, vec[0]); + CHECK_EQUAL(11, vec[1]); + CHECK_EQUAL(12, vec[2]); + CHECK_EQUAL(13, vec[3]); + CHECK_EQUAL(14, vec[4]); + } + + //************************************************************************* + TEST(ranges_generate_empty) + { + std::vector vec; + int counter = 10; + auto it = etl::ranges::generate(vec.begin(), vec.end(), [&counter]() { return counter++; }); + CHECK(it == vec.end()); + CHECK_EQUAL(10, counter); + } + + //************************************************************************* + TEST(ranges_generate_empty_range) + { + std::vector vec; + int counter = 10; + auto it = etl::ranges::generate(vec, [&counter]() { return counter++; }); + CHECK(it == vec.end()); + CHECK_EQUAL(10, counter); + } + + //************************************************************************* + TEST(ranges_generate_partial) + { + std::vector vec{1, 2, 3, 4, 5}; + int counter = 10; + auto it = etl::ranges::generate(vec.begin(), vec.begin() + 3, [&counter]() { return counter++; }); + CHECK(it == vec.begin() + 3); + CHECK_EQUAL(10, vec[0]); + CHECK_EQUAL(11, vec[1]); + CHECK_EQUAL(12, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_generate_n_iterator) + { + std::vector vec{1, 2, 3, 4, 5}; + int counter = 10; + auto it = etl::ranges::generate_n(vec.begin(), 3, [&counter]() { return counter++; }); + CHECK(it == vec.begin() + 3); + CHECK_EQUAL(10, vec[0]); + CHECK_EQUAL(11, vec[1]); + CHECK_EQUAL(12, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_generate_n_zero) + { + std::vector vec{1, 2, 3, 4, 5}; + int counter = 10; + auto it = etl::ranges::generate_n(vec.begin(), 0, [&counter]() { return counter++; }); + CHECK(it == vec.begin()); + CHECK_EQUAL(10, counter); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_generate_n_all) + { + std::vector vec{1, 2, 3, 4, 5}; + int counter = 0; + auto it = etl::ranges::generate_n(vec.begin(), 5, [&counter]() { return counter++; }); + CHECK(it == vec.end()); + CHECK_EQUAL(0, vec[0]); + CHECK_EQUAL(1, vec[1]); + CHECK_EQUAL(2, vec[2]); + CHECK_EQUAL(3, vec[3]); + CHECK_EQUAL(4, vec[4]); + } + + //************************************************************************* + TEST(ranges_iota_iterator) + { + std::vector vec(5, 0); + auto result = etl::ranges::iota(vec.begin(), vec.end(), 1); + CHECK(result.out == vec.end()); + CHECK_EQUAL(6, result.value); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_iota_range) + { + std::vector vec(5, 0); + auto result = etl::ranges::iota(vec, 1); + CHECK(result.out == vec.end()); + CHECK_EQUAL(6, result.value); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_iota_empty) + { + std::vector vec; + auto result = etl::ranges::iota(vec.begin(), vec.end(), 10); + CHECK(result.out == vec.end()); + CHECK_EQUAL(10, result.value); + } + + //************************************************************************* + TEST(ranges_iota_empty_range) + { + std::vector vec; + auto result = etl::ranges::iota(vec, 10); + CHECK(result.out == vec.end()); + CHECK_EQUAL(10, result.value); + } + + //************************************************************************* + TEST(ranges_iota_partial) + { + std::vector vec{0, 0, 0, 0, 0}; + auto result = etl::ranges::iota(vec.begin(), vec.begin() + 3, 5); + CHECK(result.out == vec.begin() + 3); + CHECK_EQUAL(8, result.value); + CHECK_EQUAL(5, vec[0]); + CHECK_EQUAL(6, vec[1]); + CHECK_EQUAL(7, vec[2]); + CHECK_EQUAL(0, vec[3]); + CHECK_EQUAL(0, vec[4]); + } + + //************************************************************************* + TEST(ranges_iota_negative_start) + { + std::vector vec(5, 0); + auto result = etl::ranges::iota(vec, -2); + CHECK(result.out == vec.end()); + CHECK_EQUAL(3, result.value); + CHECK_EQUAL(-2, vec[0]); + CHECK_EQUAL(-1, vec[1]); + CHECK_EQUAL(0, vec[2]); + CHECK_EQUAL(1, vec[3]); + CHECK_EQUAL(2, vec[4]); + } + + //************************************************************************* + TEST(ranges_iota_single_element) + { + std::vector vec(1, 0); + auto result = etl::ranges::iota(vec, 42); + CHECK(result.out == vec.end()); + CHECK_EQUAL(43, result.value); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_iota_array) + { + int arr[5] = {}; + auto result = etl::ranges::iota(arr, 10); + CHECK_EQUAL(15, result.value); + CHECK_EQUAL(10, arr[0]); + CHECK_EQUAL(11, arr[1]); + CHECK_EQUAL(12, arr[2]); + CHECK_EQUAL(13, arr[3]); + CHECK_EQUAL(14, arr[4]); + } + + //************************************************************************* + TEST(ranges_unique_iterator) + { + std::vector vec{1, 1, 2, 2, 2, 3, 3, 4, 5, 5}; + auto result = etl::ranges::unique(vec.begin(), vec.end()); + CHECK(result.begin() == vec.begin() + 5); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_unique_range) + { + std::vector vec{1, 1, 2, 2, 2, 3, 3, 4, 5, 5}; + auto result = etl::ranges::unique(vec); + CHECK(result.begin() == vec.begin() + 5); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_unique_no_duplicates) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::unique(vec.begin(), vec.end()); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_unique_all_same) + { + std::vector vec{2, 2, 2, 2}; + auto result = etl::ranges::unique(vec); + CHECK(result.begin() == vec.begin() + 1); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(2, vec[0]); + } + + //************************************************************************* + TEST(ranges_unique_empty) + { + std::vector vec; + auto result = etl::ranges::unique(vec.begin(), vec.end()); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_unique_single_element) + { + std::vector vec{42}; + auto result = etl::ranges::unique(vec); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_unique_with_predicate) + { + auto pred = [](int a, int b) { return (a / 10) == (b / 10); }; + std::vector vec{11, 15, 21, 25, 29, 31}; + auto result = etl::ranges::unique(vec.begin(), vec.end(), pred); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(11, vec[0]); + CHECK_EQUAL(21, vec[1]); + CHECK_EQUAL(31, vec[2]); + } + + //************************************************************************* + TEST(ranges_unique_with_predicate_range) + { + auto pred = [](int a, int b) { return (a / 10) == (b / 10); }; + std::vector vec{11, 15, 21, 25, 29, 31}; + auto result = etl::ranges::unique(vec, pred); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(11, vec[0]); + CHECK_EQUAL(21, vec[1]); + CHECK_EQUAL(31, vec[2]); + } + + //************************************************************************* + TEST(ranges_unique_with_projection) + { + auto proj = [](const int& v) { return v / 10; }; + std::vector vec{11, 15, 21, 25, 29, 31}; + auto result = etl::ranges::unique(vec.begin(), vec.end(), etl::ranges::equal_to{}, proj); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(11, vec[0]); + CHECK_EQUAL(21, vec[1]); + CHECK_EQUAL(31, vec[2]); + } + + //************************************************************************* + TEST(ranges_unique_with_projection_range) + { + auto proj = [](const int& v) { return v / 10; }; + std::vector vec{11, 15, 21, 25, 29, 31}; + auto result = etl::ranges::unique(vec, etl::ranges::equal_to{}, proj); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(11, vec[0]); + CHECK_EQUAL(21, vec[1]); + CHECK_EQUAL(31, vec[2]); + } + + //************************************************************************* + TEST(ranges_unique_consecutive_pairs) + { + std::vector vec{1, 1, 2, 2, 1, 1}; + auto result = etl::ranges::unique(vec); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(1, vec[2]); + } + + //************************************************************************* + TEST(ranges_unique_copy_iterator) + { + std::vector vec{1, 1, 2, 2, 2, 3, 3, 4, 5, 5}; + std::vector out(10, 0); + auto [in_it, out_it] = etl::ranges::unique_copy(vec.begin(), vec.end(), out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 5); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(4, out[3]); + CHECK_EQUAL(5, out[4]); + } + + //************************************************************************* + TEST(ranges_unique_copy_range) + { + std::vector vec{1, 1, 2, 2, 2, 3, 3, 4, 5, 5}; + std::vector out(10, 0); + auto [in_it, out_it] = etl::ranges::unique_copy(vec, out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 5); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(4, out[3]); + CHECK_EQUAL(5, out[4]); + } + + //************************************************************************* + TEST(ranges_unique_copy_no_duplicates) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::unique_copy(vec.begin(), vec.end(), out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 5); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(4, out[3]); + CHECK_EQUAL(5, out[4]); + } + + //************************************************************************* + TEST(ranges_unique_copy_empty) + { + std::vector vec; + std::vector out; + auto [in_it, out_it] = etl::ranges::unique_copy(vec.begin(), vec.end(), out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin()); + } + + //************************************************************************* + TEST(ranges_unique_copy_all_same) + { + std::vector vec{2, 2, 2, 2}; + std::vector out(4, 0); + auto [in_it, out_it] = etl::ranges::unique_copy(vec, out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 1); + CHECK_EQUAL(2, out[0]); + } + + //************************************************************************* + TEST(ranges_unique_copy_single_element) + { + std::vector vec{42}; + std::vector out(1, 0); + auto [in_it, out_it] = etl::ranges::unique_copy(vec, out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 1); + CHECK_EQUAL(42, out[0]); + } + + //************************************************************************* + TEST(ranges_unique_copy_with_predicate) + { + auto pred = [](int a, int b) { return (a / 10) == (b / 10); }; + std::vector vec{11, 15, 21, 25, 29, 31}; + std::vector out(6, 0); + auto [in_it, out_it] = etl::ranges::unique_copy(vec.begin(), vec.end(), out.begin(), pred); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 3); + CHECK_EQUAL(11, out[0]); + CHECK_EQUAL(21, out[1]); + CHECK_EQUAL(31, out[2]); + } + + //************************************************************************* + TEST(ranges_unique_copy_with_predicate_range) + { + auto pred = [](int a, int b) { return (a / 10) == (b / 10); }; + std::vector vec{11, 15, 21, 25, 29, 31}; + std::vector out(6, 0); + auto [in_it, out_it] = etl::ranges::unique_copy(vec, out.begin(), pred); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 3); + CHECK_EQUAL(11, out[0]); + CHECK_EQUAL(21, out[1]); + CHECK_EQUAL(31, out[2]); + } + + //************************************************************************* + TEST(ranges_unique_copy_with_projection) + { + auto proj = [](const int& v) { return v / 10; }; + std::vector vec{11, 15, 21, 25, 29, 31}; + std::vector out(6, 0); + auto [in_it, out_it] = etl::ranges::unique_copy(vec.begin(), vec.end(), out.begin(), etl::ranges::equal_to{}, proj); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 3); + CHECK_EQUAL(11, out[0]); + CHECK_EQUAL(21, out[1]); + CHECK_EQUAL(31, out[2]); + } + + //************************************************************************* + TEST(ranges_unique_copy_with_projection_range) + { + auto proj = [](const int& v) { return v / 10; }; + std::vector vec{11, 15, 21, 25, 29, 31}; + std::vector out(6, 0); + auto [in_it, out_it] = etl::ranges::unique_copy(vec, out.begin(), etl::ranges::equal_to{}, proj); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 3); + CHECK_EQUAL(11, out[0]); + CHECK_EQUAL(21, out[1]); + CHECK_EQUAL(31, out[2]); + } + + //************************************************************************* + TEST(ranges_unique_copy_consecutive_pairs) + { + std::vector vec{1, 1, 2, 2, 1, 1}; + std::vector out(6, 0); + auto [in_it, out_it] = etl::ranges::unique_copy(vec, out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin() + 3); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(1, out[2]); + } + + //************************************************************************* + TEST(ranges_reverse_iterator_sentinel) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::reverse(vec.begin(), vec.end()); + CHECK(result == vec.end()); + CHECK_EQUAL(5, vec[0]); + CHECK_EQUAL(4, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(2, vec[3]); + CHECK_EQUAL(1, vec[4]); + } + + //************************************************************************* + TEST(ranges_reverse_range) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::reverse(vec); + CHECK(result == vec.end()); + CHECK_EQUAL(5, vec[0]); + CHECK_EQUAL(4, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(2, vec[3]); + CHECK_EQUAL(1, vec[4]); + } + + //************************************************************************* + TEST(ranges_reverse_even_count) + { + std::vector vec{1, 2, 3, 4}; + etl::ranges::reverse(vec); + CHECK_EQUAL(4, vec[0]); + CHECK_EQUAL(3, vec[1]); + CHECK_EQUAL(2, vec[2]); + CHECK_EQUAL(1, vec[3]); + } + + //************************************************************************* + TEST(ranges_reverse_single_element) + { + std::vector vec{42}; + etl::ranges::reverse(vec); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_reverse_empty) + { + std::vector vec{}; + auto result = etl::ranges::reverse(vec); + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_reverse_copy_iterator_sentinel) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::reverse_copy(vec.begin(), vec.end(), out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(5, out[0]); + CHECK_EQUAL(4, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(2, out[3]); + CHECK_EQUAL(1, out[4]); + } + + //************************************************************************* + TEST(ranges_reverse_copy_range) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::reverse_copy(vec, out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(5, out[0]); + CHECK_EQUAL(4, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(2, out[3]); + CHECK_EQUAL(1, out[4]); + } + + //************************************************************************* + TEST(ranges_reverse_copy_empty) + { + std::vector vec{}; + std::vector out{}; + auto [in_it, out_it] = etl::ranges::reverse_copy(vec, out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin()); + } + + //************************************************************************* + TEST(ranges_reverse_copy_single_element) + { + std::vector vec{42}; + std::vector out(1, 0); + auto [in_it, out_it] = etl::ranges::reverse_copy(vec, out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(42, out[0]); + } + + //************************************************************************* + TEST(ranges_rotate_iterator_sentinel) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::rotate(vec.begin(), vec.begin() + 2, vec.end()); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(3, vec[0]); + CHECK_EQUAL(4, vec[1]); + CHECK_EQUAL(5, vec[2]); + CHECK_EQUAL(1, vec[3]); + CHECK_EQUAL(2, vec[4]); + } + + //************************************************************************* + TEST(ranges_rotate_range) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::rotate(vec, vec.begin() + 2); + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(3, vec[0]); + CHECK_EQUAL(4, vec[1]); + CHECK_EQUAL(5, vec[2]); + CHECK_EQUAL(1, vec[3]); + CHECK_EQUAL(2, vec[4]); + } + + //************************************************************************* + TEST(ranges_rotate_middle_at_begin) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::rotate(vec, vec.begin()); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_rotate_middle_at_end) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::rotate(vec, vec.end()); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_rotate_single_element) + { + std::vector vec{42}; + auto result = etl::ranges::rotate(vec, vec.begin()); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_rotate_by_one) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::rotate(vec, vec.begin() + 1); + CHECK(result.begin() == vec.begin() + 4); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(2, vec[0]); + CHECK_EQUAL(3, vec[1]); + CHECK_EQUAL(4, vec[2]); + CHECK_EQUAL(5, vec[3]); + CHECK_EQUAL(1, vec[4]); + } + + //************************************************************************* + TEST(ranges_rotate_by_last_minus_one) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::rotate(vec, vec.begin() + 4); + CHECK(result.begin() == vec.begin() + 1); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(5, vec[0]); + CHECK_EQUAL(1, vec[1]); + CHECK_EQUAL(2, vec[2]); + CHECK_EQUAL(3, vec[3]); + CHECK_EQUAL(4, vec[4]); + } + + //************************************************************************* + TEST(ranges_rotate_copy_iterator_sentinel) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::rotate_copy(vec.begin(), vec.begin() + 2, vec.end(), out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(3, out[0]); + CHECK_EQUAL(4, out[1]); + CHECK_EQUAL(5, out[2]); + CHECK_EQUAL(1, out[3]); + CHECK_EQUAL(2, out[4]); + } + + //************************************************************************* + TEST(ranges_rotate_copy_range) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::rotate_copy(vec, vec.begin() + 2, out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(3, out[0]); + CHECK_EQUAL(4, out[1]); + CHECK_EQUAL(5, out[2]); + CHECK_EQUAL(1, out[3]); + CHECK_EQUAL(2, out[4]); + } + + //************************************************************************* + TEST(ranges_rotate_copy_middle_at_begin) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::rotate_copy(vec, vec.begin(), out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(4, out[3]); + CHECK_EQUAL(5, out[4]); + } + + //************************************************************************* + TEST(ranges_rotate_copy_middle_at_end) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::rotate_copy(vec, vec.end(), out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(1, out[0]); + CHECK_EQUAL(2, out[1]); + CHECK_EQUAL(3, out[2]); + CHECK_EQUAL(4, out[3]); + CHECK_EQUAL(5, out[4]); + } + + //************************************************************************* + TEST(ranges_rotate_copy_single_element) + { + std::vector vec{42}; + std::vector out(1, 0); + auto [in_it, out_it] = etl::ranges::rotate_copy(vec, vec.begin(), out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(42, out[0]); + } + + //************************************************************************* + TEST(ranges_rotate_copy_empty) + { + std::vector vec{}; + std::vector out{}; + auto [in_it, out_it] = etl::ranges::rotate_copy(vec, vec.begin(), out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.begin()); + } + + //************************************************************************* + TEST(ranges_rotate_copy_by_one) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::rotate_copy(vec, vec.begin() + 1, out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(2, out[0]); + CHECK_EQUAL(3, out[1]); + CHECK_EQUAL(4, out[2]); + CHECK_EQUAL(5, out[3]); + CHECK_EQUAL(1, out[4]); + } + + //************************************************************************* + TEST(ranges_rotate_copy_by_last_minus_one) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector out(5, 0); + auto [in_it, out_it] = etl::ranges::rotate_copy(vec, vec.begin() + 4, out.begin()); + CHECK(in_it == vec.end()); + CHECK(out_it == out.end()); + CHECK_EQUAL(5, out[0]); + CHECK_EQUAL(1, out[1]); + CHECK_EQUAL(2, out[2]); + CHECK_EQUAL(3, out[3]); + CHECK_EQUAL(4, out[4]); + } + + //************************************************************************* + TEST(ranges_shift_left_iterator_sentinel) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_left(vec.begin(), vec.end(), 2); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.begin() + 3); + CHECK_EQUAL(3, vec[0]); + CHECK_EQUAL(4, vec[1]); + CHECK_EQUAL(5, vec[2]); + } + + //************************************************************************* + TEST(ranges_shift_left_range) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_left(vec, 2); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.begin() + 3); + CHECK_EQUAL(3, vec[0]); + CHECK_EQUAL(4, vec[1]); + CHECK_EQUAL(5, vec[2]); + } + + //************************************************************************* + TEST(ranges_shift_left_by_zero) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_left(vec, 0); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_shift_left_by_negative) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_left(vec, -1); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_shift_left_by_size) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_left(vec, 5); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.begin()); + } + + //************************************************************************* + TEST(ranges_shift_left_by_more_than_size) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_left(vec, 10); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.begin()); + } + + //************************************************************************* + TEST(ranges_shift_left_by_one) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_left(vec, 1); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.begin() + 4); + CHECK_EQUAL(2, vec[0]); + CHECK_EQUAL(3, vec[1]); + CHECK_EQUAL(4, vec[2]); + CHECK_EQUAL(5, vec[3]); + } + + //************************************************************************* + TEST(ranges_shift_left_by_last_minus_one) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_left(vec, 4); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.begin() + 1); + CHECK_EQUAL(5, vec[0]); + } + + //************************************************************************* + TEST(ranges_shift_left_empty) + { + std::vector vec{}; + auto result = etl::ranges::shift_left(vec, 1); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.begin()); + } + + //************************************************************************* + TEST(ranges_shift_left_single_element) + { + std::vector vec{42}; + auto result = etl::ranges::shift_left(vec, 1); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.begin()); + } + + //************************************************************************* + TEST(ranges_shift_left_single_element_by_zero) + { + std::vector vec{42}; + auto result = etl::ranges::shift_left(vec, 0); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_shift_right_iterator_sentinel) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_right(vec.begin(), vec.end(), 2); + CHECK(result.begin() == vec.begin() + 2); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[2]); + CHECK_EQUAL(2, vec[3]); + CHECK_EQUAL(3, vec[4]); + } + + //************************************************************************* + TEST(ranges_shift_right_range) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_right(vec, 2); + CHECK(result.begin() == vec.begin() + 2); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[2]); + CHECK_EQUAL(2, vec[3]); + CHECK_EQUAL(3, vec[4]); + } + + //************************************************************************* + TEST(ranges_shift_right_by_zero) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_right(vec, 0); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_shift_right_by_negative) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_right(vec, -1); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(4, vec[3]); + CHECK_EQUAL(5, vec[4]); + } + + //************************************************************************* + TEST(ranges_shift_right_by_size) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_right(vec, 5); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_shift_right_by_more_than_size) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_right(vec, 10); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_shift_right_by_one) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_right(vec, 1); + CHECK(result.begin() == vec.begin() + 1); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[1]); + CHECK_EQUAL(2, vec[2]); + CHECK_EQUAL(3, vec[3]); + CHECK_EQUAL(4, vec[4]); + } + + //************************************************************************* + TEST(ranges_shift_right_by_last_minus_one) + { + std::vector vec{1, 2, 3, 4, 5}; + auto result = etl::ranges::shift_right(vec, 4); + CHECK(result.begin() == vec.begin() + 4); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(1, vec[4]); + } + + //************************************************************************* + TEST(ranges_shift_right_empty) + { + std::vector vec{}; + auto result = etl::ranges::shift_right(vec, 1); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_shift_right_single_element) + { + std::vector vec{42}; + auto result = etl::ranges::shift_right(vec, 1); + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_shift_right_single_element_by_zero) + { + std::vector vec{42}; + auto result = etl::ranges::shift_right(vec, 0); + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_shuffle_iterator_sentinel) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector original = vec; + std::mt19937 gen(42); + auto result = etl::ranges::shuffle(vec.begin(), vec.end(), gen); + + CHECK(result == vec.end()); + // All original elements must still be present (permutation check) + std::vector sorted_vec = vec; + std::sort(sorted_vec.begin(), sorted_vec.end()); + CHECK(sorted_vec == original); + } + + //************************************************************************* + TEST(ranges_shuffle_range) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector original = vec; + std::mt19937 gen(42); + auto result = etl::ranges::shuffle(vec, gen); + + CHECK(result == vec.end()); + // All original elements must still be present (permutation check) + std::vector sorted_vec = vec; + std::sort(sorted_vec.begin(), sorted_vec.end()); + CHECK(sorted_vec == original); + } + + //************************************************************************* + TEST(ranges_shuffle_empty) + { + std::vector vec{}; + std::mt19937 gen(42); + auto result = etl::ranges::shuffle(vec.begin(), vec.end(), gen); + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_shuffle_single_element) + { + std::vector vec{42}; + std::mt19937 gen(42); + auto result = etl::ranges::shuffle(vec, gen); + CHECK(result == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_shuffle_two_elements) + { + std::vector vec{1, 2}; + std::vector original = vec; + std::mt19937 gen(42); + auto result = etl::ranges::shuffle(vec, gen); + + CHECK(result == vec.end()); + std::vector sorted_vec = vec; + std::sort(sorted_vec.begin(), sorted_vec.end()); + CHECK(sorted_vec == original); + } + + //************************************************************************* + TEST(ranges_shuffle_deterministic) + { + // Same seed should produce the same permutation + std::vector vec1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector vec2{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::mt19937 gen1(123); + std::mt19937 gen2(123); + + etl::ranges::shuffle(vec1, gen1); + etl::ranges::shuffle(vec2, gen2); + + CHECK(vec1 == vec2); + } + + //************************************************************************* + TEST(ranges_shuffle_large) + { + std::vector vec(100); + std::iota(vec.begin(), vec.end(), 0); + std::vector original = vec; + std::mt19937 gen(99); + + etl::ranges::shuffle(vec, gen); + + // All original elements must still be present + std::vector sorted_vec = vec; + std::sort(sorted_vec.begin(), sorted_vec.end()); + CHECK(sorted_vec == original); + + // It's extremely unlikely a shuffle of 100 elements leaves them in order + CHECK(vec != original); + } + + //************************************************************************* + TEST(ranges_sample_iterator_sentinel) + { + std::vector src{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector dest(5); + std::mt19937 gen(42); + + auto result = etl::ranges::sample(src.begin(), src.end(), dest.begin(), 5, gen); + + // result should point past the last written element + CHECK(result == dest.begin() + 5); + + // All sampled elements must come from the source + for (auto& v : dest) + { + CHECK(std::find(src.begin(), src.end(), v) != src.end()); + } + + // No duplicates in the sample + std::sort(dest.begin(), dest.end()); + CHECK(std::unique(dest.begin(), dest.end()) == dest.end()); + } + + //************************************************************************* + TEST(ranges_sample_range) + { + std::vector src{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector dest(5); + std::mt19937 gen(42); + + auto result = etl::ranges::sample(src, dest.begin(), 5, gen); + + CHECK(result == dest.begin() + 5); + + for (auto& v : dest) + { + CHECK(std::find(src.begin(), src.end(), v) != src.end()); + } + + std::sort(dest.begin(), dest.end()); + CHECK(std::unique(dest.begin(), dest.end()) == dest.end()); + } + + //************************************************************************* + TEST(ranges_sample_n_greater_than_population) + { + std::vector src{1, 2, 3}; + std::vector dest(5, 0); + std::mt19937 gen(42); + + auto result = etl::ranges::sample(src.begin(), src.end(), dest.begin(), 5, gen); + + // Only 3 elements available, so only 3 should be copied + CHECK(result == dest.begin() + 3); + CHECK_EQUAL(1, dest[0]); + CHECK_EQUAL(2, dest[1]); + CHECK_EQUAL(3, dest[2]); + } + + //************************************************************************* + TEST(ranges_sample_n_equal_to_population) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dest(5, 0); + std::mt19937 gen(42); + + auto result = etl::ranges::sample(src, dest.begin(), 5, gen); + + CHECK(result == dest.begin() + 5); + + // All elements should be copied + std::vector sorted_dest = dest; + std::sort(sorted_dest.begin(), sorted_dest.end()); + CHECK(sorted_dest == src); + } + + //************************************************************************* + TEST(ranges_sample_empty_source) + { + std::vector src{}; + std::vector dest(5, 0); + std::mt19937 gen(42); + + auto result = etl::ranges::sample(src.begin(), src.end(), dest.begin(), 5, gen); + + CHECK(result == dest.begin()); + } + + //************************************************************************* + TEST(ranges_sample_zero_count) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector dest(5, 0); + std::mt19937 gen(42); + + auto result = etl::ranges::sample(src, dest.begin(), 0, gen); + + CHECK(result == dest.begin()); + } + + //************************************************************************* + TEST(ranges_sample_single_element_source) + { + std::vector src{42}; + std::vector dest(1, 0); + std::mt19937 gen(42); + + auto result = etl::ranges::sample(src, dest.begin(), 1, gen); + + CHECK(result == dest.begin() + 1); + CHECK_EQUAL(42, dest[0]); + } + + //************************************************************************* + TEST(ranges_sample_deterministic) + { + // Same seed should produce the same sample + std::vector src{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector dest1(5); + std::vector dest2(5); + std::mt19937 gen1(123); + std::mt19937 gen2(123); + + etl::ranges::sample(src, dest1.begin(), 5, gen1); + etl::ranges::sample(src, dest2.begin(), 5, gen2); + + CHECK(dest1 == dest2); + } + + //************************************************************************* + TEST(ranges_sample_preserves_relative_order) + { + // Selection sampling preserves relative order of elements + std::vector src{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector dest(5); + std::mt19937 gen(42); + + etl::ranges::sample(src, dest.begin(), 5, gen); + + // The sampled elements should be in ascending order (since source is) + for (size_t i = 1; i < dest.size(); ++i) + { + CHECK(dest[i - 1] < dest[i]); + } + } + + //************************************************************************* + TEST(ranges_sample_large) + { + std::vector src(100); + std::iota(src.begin(), src.end(), 0); + std::vector dest(20); + std::mt19937 gen(99); + + auto result = etl::ranges::sample(src, dest.begin(), 20, gen); + + CHECK(result == dest.begin() + 20); + + // All sampled elements must be from the source + for (auto& v : dest) + { + CHECK(v >= 0); + CHECK(v < 100); + } + + // No duplicates + std::sort(dest.begin(), dest.end()); + CHECK(std::unique(dest.begin(), dest.end()) == dest.end()); + } + + //************************************************************************* + TEST(ranges_partition_iterator_sentinel) + { + std::vector vec{1, 20, 3, 10, 2, 30}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition(vec.begin(), vec.end(), pred); + + // All elements before the partition point should satisfy the predicate + for (auto it = vec.begin(); it != result.begin(); ++it) + { + CHECK(*it < 10); + } + + // All elements from the partition point onward should not satisfy the predicate + for (auto it = result.begin(); it != result.end(); ++it) + { + CHECK(*it >= 10); + } + } + + //************************************************************************* + TEST(ranges_partition_range) + { + std::vector vec{1, 20, 3, 10, 2, 30}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition(vec, pred); + + for (auto it = vec.begin(); it != result.begin(); ++it) + { + CHECK(*it < 10); + } + + for (auto it = result.begin(); it != result.end(); ++it) + { + CHECK(*it >= 10); + } + } + + //************************************************************************* + TEST(ranges_partition_already_partitioned) + { + std::vector vec{1, 2, 3, 10, 20, 30}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition(vec.begin(), vec.end(), pred); + + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + + // Verify the range is correctly partitioned + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + } + + //************************************************************************* + TEST(ranges_partition_all_true) + { + std::vector vec{1, 2, 3, 4, 5}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition(vec.begin(), vec.end(), pred); + + // Partition point should be at the end (all elements satisfy predicate) + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_partition_all_false) + { + std::vector vec{10, 20, 30}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition(vec.begin(), vec.end(), pred); + + // Partition point should be at the beginning (no elements satisfy predicate) + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_partition_empty) + { + std::vector vec{}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition(vec.begin(), vec.end(), pred); + + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.begin()); + } + + //************************************************************************* + TEST(ranges_partition_single_true) + { + std::vector vec{1}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition(vec.begin(), vec.end(), pred); + + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_partition_single_false) + { + std::vector vec{20}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition(vec.begin(), vec.end(), pred); + + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_partition_with_projection) + { + std::vector vec{1, 20, 3, 10, 2, 30}; + auto pred = [](const int& v) { return v < 100; }; + auto proj = [](const int& v) { return v * 10; }; + + // With projection: values become 10,200,30,100,20,300 + // pred(proj(v)) < 100: true for 1,3,2 (proj gives 10,30,20), false for 20,10,30 (proj gives 200,100,300) + auto result = etl::ranges::partition(vec.begin(), vec.end(), pred, proj); + + for (auto it = vec.begin(); it != result.begin(); ++it) + { + CHECK((*it * 10) < 100); + } + + for (auto it = result.begin(); it != result.end(); ++it) + { + CHECK((*it * 10) >= 100); + } + } + + //************************************************************************* + TEST(ranges_partition_with_projection_range) + { + std::vector vec{1, 20, 3, 10, 2, 30}; + auto pred = [](const int& v) { return v < 100; }; + auto proj = [](const int& v) { return v * 10; }; + + auto result = etl::ranges::partition(vec, pred, proj); + + for (auto it = vec.begin(); it != result.begin(); ++it) + { + CHECK((*it * 10) < 100); + } + + for (auto it = result.begin(); it != result.end(); ++it) + { + CHECK((*it * 10) >= 100); + } + } + + //************************************************************************* + TEST(ranges_partition_preserves_elements) + { + std::vector vec{5, 1, 4, 2, 3}; + std::vector sorted_original{1, 2, 3, 4, 5}; + auto pred = [](const int& v) { return v <= 3; }; + + etl::ranges::partition(vec, pred); + + // All original elements should still be present + std::vector sorted_result(vec.begin(), vec.end()); + std::sort(sorted_result.begin(), sorted_result.end()); + CHECK(sorted_result == sorted_original); + } + + //************************************************************************* + TEST(ranges_is_partitioned_iterator_sentinel) + { + std::vector vec{1, 2, 3, 10, 20, 30}; + auto pred = [](const int& v) { return v < 10; }; + + CHECK(etl::ranges::is_partitioned(vec.begin(), vec.end(), pred)); + } + + //************************************************************************* + TEST(ranges_is_partitioned_range) + { + std::vector vec{1, 2, 3, 10, 20, 30}; + auto pred = [](const int& v) { return v < 10; }; + + CHECK(etl::ranges::is_partitioned(vec, pred)); + } + + //************************************************************************* + TEST(ranges_is_partitioned_not_partitioned) + { + std::vector vec{1, 20, 3, 10, 2, 30}; + auto pred = [](const int& v) { return v < 10; }; + + CHECK_FALSE(etl::ranges::is_partitioned(vec.begin(), vec.end(), pred)); + CHECK_FALSE(etl::ranges::is_partitioned(vec, pred)); + } + + //************************************************************************* + TEST(ranges_is_partitioned_all_true) + { + std::vector vec{1, 2, 3, 4, 5}; + auto pred = [](const int& v) { return v < 10; }; + + CHECK(etl::ranges::is_partitioned(vec.begin(), vec.end(), pred)); + CHECK(etl::ranges::is_partitioned(vec, pred)); + } + + //************************************************************************* + TEST(ranges_is_partitioned_all_false) + { + std::vector vec{10, 20, 30}; + auto pred = [](const int& v) { return v < 10; }; + + CHECK(etl::ranges::is_partitioned(vec.begin(), vec.end(), pred)); + CHECK(etl::ranges::is_partitioned(vec, pred)); + } + + //************************************************************************* + TEST(ranges_is_partitioned_empty) + { + std::vector vec{}; + auto pred = [](const int& v) { return v < 10; }; + + CHECK(etl::ranges::is_partitioned(vec.begin(), vec.end(), pred)); + CHECK(etl::ranges::is_partitioned(vec, pred)); + } + + //************************************************************************* + TEST(ranges_is_partitioned_single_true) + { + std::vector vec{1}; + auto pred = [](const int& v) { return v < 10; }; + + CHECK(etl::ranges::is_partitioned(vec.begin(), vec.end(), pred)); + CHECK(etl::ranges::is_partitioned(vec, pred)); + } + + //************************************************************************* + TEST(ranges_is_partitioned_single_false) + { + std::vector vec{20}; + auto pred = [](const int& v) { return v < 10; }; + + CHECK(etl::ranges::is_partitioned(vec.begin(), vec.end(), pred)); + CHECK(etl::ranges::is_partitioned(vec, pred)); + } + + //************************************************************************* + TEST(ranges_is_partitioned_with_projection) + { + std::vector vec{1, 2, 3, 10, 20, 30}; + auto pred = [](const int& v) { return v < 100; }; + auto proj = [](const int& v) { return v * 10; }; + + // With projection: values become 10,20,30,100,200,300 + // pred(proj(v)) < 100: true for 10,20,30, false for 100,200,300 + CHECK(etl::ranges::is_partitioned(vec.begin(), vec.end(), pred, proj)); + CHECK(etl::ranges::is_partitioned(vec, pred, proj)); + } + + //************************************************************************* + TEST(ranges_is_partitioned_with_projection_not_partitioned) + { + std::vector vec{1, 20, 3, 10, 2, 30}; + auto pred = [](const int& v) { return v < 100; }; + auto proj = [](const int& v) { return v * 10; }; + + // With projection: values become 10,200,30,100,20,300 + // pred(proj(v)) < 100: true,false,true,false,true,false => not partitioned + CHECK_FALSE(etl::ranges::is_partitioned(vec.begin(), vec.end(), pred, proj)); + CHECK_FALSE(etl::ranges::is_partitioned(vec, pred, proj)); + } + + //************************************************************************* + TEST(ranges_partition_copy_iterator_sentinel) + { + std::vector src{1, 20, 3, 10, 2, 30}; + std::vector out_true(6, 0); + std::vector out_false(6, 0); + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition_copy(src.begin(), src.end(), out_true.begin(), out_false.begin(), pred); + + CHECK(result.in == src.end()); + + std::vector expected_true{1, 3, 2}; + std::vector expected_false{20, 10, 30}; + + CHECK_EQUAL(3, std::distance(out_true.begin(), result.out1)); + CHECK_EQUAL(3, std::distance(out_false.begin(), result.out2)); + + CHECK(std::equal(expected_true.begin(), expected_true.end(), out_true.begin())); + CHECK(std::equal(expected_false.begin(), expected_false.end(), out_false.begin())); + } + + //************************************************************************* + TEST(ranges_partition_copy_range) + { + std::vector src{1, 20, 3, 10, 2, 30}; + std::vector out_true(6, 0); + std::vector out_false(6, 0); + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition_copy(src, out_true.begin(), out_false.begin(), pred); + + CHECK(result.in == src.end()); + + std::vector expected_true{1, 3, 2}; + std::vector expected_false{20, 10, 30}; + + CHECK_EQUAL(3, std::distance(out_true.begin(), result.out1)); + CHECK_EQUAL(3, std::distance(out_false.begin(), result.out2)); + + CHECK(std::equal(expected_true.begin(), expected_true.end(), out_true.begin())); + CHECK(std::equal(expected_false.begin(), expected_false.end(), out_false.begin())); + } + + //************************************************************************* + TEST(ranges_partition_copy_all_true) + { + std::vector src{1, 2, 3, 4, 5}; + std::vector out_true(5, 0); + std::vector out_false(5, 0); + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition_copy(src.begin(), src.end(), out_true.begin(), out_false.begin(), pred); + + CHECK(result.in == src.end()); + CHECK_EQUAL(5, std::distance(out_true.begin(), result.out1)); + CHECK_EQUAL(0, std::distance(out_false.begin(), result.out2)); + + CHECK(std::equal(src.begin(), src.end(), out_true.begin())); + } + + //************************************************************************* + TEST(ranges_partition_copy_all_false) + { + std::vector src{10, 20, 30}; + std::vector out_true(3, 0); + std::vector out_false(3, 0); + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition_copy(src.begin(), src.end(), out_true.begin(), out_false.begin(), pred); + + CHECK(result.in == src.end()); + CHECK_EQUAL(0, std::distance(out_true.begin(), result.out1)); + CHECK_EQUAL(3, std::distance(out_false.begin(), result.out2)); + + CHECK(std::equal(src.begin(), src.end(), out_false.begin())); + } + + //************************************************************************* + TEST(ranges_partition_copy_empty) + { + std::vector src{}; + std::vector out_true{}; + std::vector out_false{}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition_copy(src.begin(), src.end(), out_true.begin(), out_false.begin(), pred); + + CHECK(result.in == src.end()); + CHECK(result.out1 == out_true.begin()); + CHECK(result.out2 == out_false.begin()); + } + + //************************************************************************* + TEST(ranges_partition_copy_single_true) + { + std::vector src{5}; + std::vector out_true(1, 0); + std::vector out_false(1, 0); + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition_copy(src.begin(), src.end(), out_true.begin(), out_false.begin(), pred); + + CHECK(result.in == src.end()); + CHECK_EQUAL(1, std::distance(out_true.begin(), result.out1)); + CHECK_EQUAL(0, std::distance(out_false.begin(), result.out2)); + CHECK_EQUAL(5, out_true[0]); + } + + //************************************************************************* + TEST(ranges_partition_copy_single_false) + { + std::vector src{20}; + std::vector out_true(1, 0); + std::vector out_false(1, 0); + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition_copy(src.begin(), src.end(), out_true.begin(), out_false.begin(), pred); + + CHECK(result.in == src.end()); + CHECK_EQUAL(0, std::distance(out_true.begin(), result.out1)); + CHECK_EQUAL(1, std::distance(out_false.begin(), result.out2)); + CHECK_EQUAL(20, out_false[0]); + } + + //************************************************************************* + TEST(ranges_partition_copy_with_projection) + { + std::vector src{1, 20, 3, 10, 2, 30}; + std::vector out_true(6, 0); + std::vector out_false(6, 0); + auto pred = [](const int& v) { return v < 100; }; + auto proj = [](const int& v) { return v * 10; }; + + // With projection: values become 10,200,30,100,20,300 + // pred(proj(v)) < 100: true for 1,3,2 (proj gives 10,30,20), false for 20,10,30 (proj gives 200,100,300) + auto result = etl::ranges::partition_copy(src.begin(), src.end(), out_true.begin(), out_false.begin(), pred, proj); + + CHECK(result.in == src.end()); + + std::vector expected_true{1, 3, 2}; + std::vector expected_false{20, 10, 30}; + + CHECK_EQUAL(3, std::distance(out_true.begin(), result.out1)); + CHECK_EQUAL(3, std::distance(out_false.begin(), result.out2)); + + CHECK(std::equal(expected_true.begin(), expected_true.end(), out_true.begin())); + CHECK(std::equal(expected_false.begin(), expected_false.end(), out_false.begin())); + } + + //************************************************************************* + TEST(ranges_partition_copy_with_projection_range) + { + std::vector src{1, 20, 3, 10, 2, 30}; + std::vector out_true(6, 0); + std::vector out_false(6, 0); + auto pred = [](const int& v) { return v < 100; }; + auto proj = [](const int& v) { return v * 10; }; + + auto result = etl::ranges::partition_copy(src, out_true.begin(), out_false.begin(), pred, proj); + + CHECK(result.in == src.end()); + + std::vector expected_true{1, 3, 2}; + std::vector expected_false{20, 10, 30}; + + CHECK_EQUAL(3, std::distance(out_true.begin(), result.out1)); + CHECK_EQUAL(3, std::distance(out_false.begin(), result.out2)); + + CHECK(std::equal(expected_true.begin(), expected_true.end(), out_true.begin())); + CHECK(std::equal(expected_false.begin(), expected_false.end(), out_false.begin())); + } + + //************************************************************************* + TEST(ranges_partition_copy_preserves_order) + { + std::vector src{2, 8, 1, 7, 3, 6, 4, 5}; + std::vector out_true(8, 0); + std::vector out_false(8, 0); + auto pred = [](const int& v) { return v <= 4; }; + + auto result = etl::ranges::partition_copy(src, out_true.begin(), out_false.begin(), pred); + + // partition_copy is a stable operation - relative order is preserved + std::vector expected_true{2, 1, 3, 4}; + std::vector expected_false{8, 7, 6, 5}; + + CHECK_EQUAL(4, std::distance(out_true.begin(), result.out1)); + CHECK_EQUAL(4, std::distance(out_false.begin(), result.out2)); + + CHECK(std::equal(expected_true.begin(), expected_true.end(), out_true.begin())); + CHECK(std::equal(expected_false.begin(), expected_false.end(), out_false.begin())); + } + + //************************************************************************* + TEST(ranges_partition_copy_matches_std) + { + int data1[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + int std_true[8] = {}; + int std_false[8] = {}; + int etl_true[8] = {}; + int etl_false[8] = {}; + auto pred = [](const int& v) { return v > 4; }; + + std::partition_copy(std::begin(data1), std::end(data1), std::begin(std_true), std::begin(std_false), pred); + auto result = etl::ranges::partition_copy(std::begin(data1), std::end(data1), std::begin(etl_true), std::begin(etl_false), pred); + + CHECK(result.in == std::end(data1)); + + bool are_equal; + are_equal = std::equal(std::begin(std_true), std::end(std_true), std::begin(etl_true)); + CHECK(are_equal); + + are_equal = std::equal(std::begin(std_false), std::end(std_false), std::begin(etl_false)); + CHECK(are_equal); + } + + //************************************************************************* + TEST(ranges_partition_point_iterator_sentinel) + { + std::vector vec{1, 3, 5, 7, 2, 4, 6, 8}; + auto pred = [](const int& v) { return v % 2 != 0; }; + + auto result = etl::ranges::partition_point(vec.begin(), vec.end(), pred); + auto expected = std::partition_point(vec.begin(), vec.end(), pred); + + CHECK(result == expected); + } + + //************************************************************************* + TEST(ranges_partition_point_range) + { + std::vector vec{1, 3, 5, 7, 2, 4, 6, 8}; + auto pred = [](const int& v) { return v % 2 != 0; }; + + auto result = etl::ranges::partition_point(vec, pred); + auto expected = std::partition_point(vec.begin(), vec.end(), pred); + + CHECK(result == expected); + } + + //************************************************************************* + TEST(ranges_partition_point_all_true) + { + std::vector vec{1, 3, 5, 7, 9}; + auto pred = [](const int& v) { return v % 2 != 0; }; + + auto result = etl::ranges::partition_point(vec.begin(), vec.end(), pred); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_partition_point_all_false) + { + std::vector vec{2, 4, 6, 8}; + auto pred = [](const int& v) { return v % 2 != 0; }; + + auto result = etl::ranges::partition_point(vec.begin(), vec.end(), pred); + + CHECK(result == vec.begin()); + } + + //************************************************************************* + TEST(ranges_partition_point_empty) + { + std::vector vec{}; + auto pred = [](const int& v) { return v % 2 != 0; }; + + auto result = etl::ranges::partition_point(vec.begin(), vec.end(), pred); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_partition_point_single_true) + { + std::vector vec{1}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition_point(vec.begin(), vec.end(), pred); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_partition_point_single_false) + { + std::vector vec{20}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::partition_point(vec.begin(), vec.end(), pred); + + CHECK(result == vec.begin()); + } + + //************************************************************************* + TEST(ranges_partition_point_with_projection) + { + std::vector vec{1, 2, 3, 10, 20, 30}; + auto pred = [](const int& v) { return v < 100; }; + auto proj = [](const int& v) { return v * 10; }; + + // With projection: values become 10, 20, 30, 100, 200, 300 + // pred(proj(v)) < 100 is true for first 3 elements + auto result = etl::ranges::partition_point(vec.begin(), vec.end(), pred, proj); + + CHECK(result == vec.begin() + 3); + } + + //************************************************************************* + TEST(ranges_partition_point_with_projection_range) + { + std::vector vec{1, 2, 3, 10, 20, 30}; + auto pred = [](const int& v) { return v < 100; }; + auto proj = [](const int& v) { return v * 10; }; + + auto result = etl::ranges::partition_point(vec, pred, proj); + + CHECK(result == vec.begin() + 3); + } + + //************************************************************************* + TEST(ranges_partition_point_matches_std) + { + int data1[] = { 10, 8, 6, 4, 3, 2, 1 }; + + auto pred = [](const int& v) { return v > 4; }; + + int* std_result = std::partition_point(std::begin(data1), std::end(data1), pred); + auto etl_result = etl::ranges::partition_point(std::begin(data1), std::end(data1), pred); + + CHECK(std_result == etl_result); + } + + //************************************************************************* + TEST(ranges_stable_partition_iterator_sentinel) + { + std::vector vec{1, 20, 3, 10, 2, 30}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::stable_partition(vec.begin(), vec.end(), pred); + + // All elements before the partition point should satisfy the predicate + for (auto it = vec.begin(); it != result.begin(); ++it) + { + CHECK(*it < 10); + } + + // All elements from the partition point onward should not satisfy the predicate + for (auto it = result.begin(); it != result.end(); ++it) + { + CHECK(*it >= 10); + } + + // Verify stability: relative order of elements should be preserved + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(3, vec[1]); + CHECK_EQUAL(2, vec[2]); + CHECK_EQUAL(20, vec[3]); + CHECK_EQUAL(10, vec[4]); + CHECK_EQUAL(30, vec[5]); + } + + //************************************************************************* + TEST(ranges_stable_partition_range) + { + std::vector vec{1, 20, 3, 10, 2, 30}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::stable_partition(vec, pred); + + for (auto it = vec.begin(); it != result.begin(); ++it) + { + CHECK(*it < 10); + } + + for (auto it = result.begin(); it != result.end(); ++it) + { + CHECK(*it >= 10); + } + + // Verify stability + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(3, vec[1]); + CHECK_EQUAL(2, vec[2]); + CHECK_EQUAL(20, vec[3]); + CHECK_EQUAL(10, vec[4]); + CHECK_EQUAL(30, vec[5]); + } + + //************************************************************************* + TEST(ranges_stable_partition_already_partitioned) + { + std::vector vec{1, 2, 3, 10, 20, 30}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::stable_partition(vec.begin(), vec.end(), pred); + + CHECK(result.begin() == vec.begin() + 3); + CHECK(result.end() == vec.end()); + + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(2, vec[1]); + CHECK_EQUAL(3, vec[2]); + CHECK_EQUAL(10, vec[3]); + CHECK_EQUAL(20, vec[4]); + CHECK_EQUAL(30, vec[5]); + } + + //************************************************************************* + TEST(ranges_stable_partition_all_true) + { + std::vector vec{1, 2, 3, 4, 5}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::stable_partition(vec.begin(), vec.end(), pred); + + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_stable_partition_all_false) + { + std::vector vec{10, 20, 30}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::stable_partition(vec.begin(), vec.end(), pred); + + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_stable_partition_empty) + { + std::vector vec{}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::stable_partition(vec.begin(), vec.end(), pred); + + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.begin()); + } + + //************************************************************************* + TEST(ranges_stable_partition_single_true) + { + std::vector vec{1}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::stable_partition(vec.begin(), vec.end(), pred); + + CHECK(result.begin() == vec.end()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_stable_partition_single_false) + { + std::vector vec{20}; + auto pred = [](const int& v) { return v < 10; }; + + auto result = etl::ranges::stable_partition(vec.begin(), vec.end(), pred); + + CHECK(result.begin() == vec.begin()); + CHECK(result.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_stable_partition_with_projection) + { + std::vector vec{1, 20, 3, 10, 2, 30}; + auto pred = [](const int& v) { return v < 100; }; + auto proj = [](const int& v) { return v * 10; }; + + // With projection: values become 10,200,30,100,20,300 + // pred(proj(v)) < 100: true for 1,3,2 (proj gives 10,30,20), false for 20,10,30 (proj gives 200,100,300) + auto result = etl::ranges::stable_partition(vec.begin(), vec.end(), pred, proj); + + for (auto it = vec.begin(); it != result.begin(); ++it) + { + CHECK((*it * 10) < 100); + } + + for (auto it = result.begin(); it != result.end(); ++it) + { + CHECK((*it * 10) >= 100); + } + + // Verify stability + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(3, vec[1]); + CHECK_EQUAL(2, vec[2]); + CHECK_EQUAL(20, vec[3]); + CHECK_EQUAL(10, vec[4]); + CHECK_EQUAL(30, vec[5]); + } + + //************************************************************************* + TEST(ranges_stable_partition_with_projection_range) + { + std::vector vec{1, 20, 3, 10, 2, 30}; + auto pred = [](const int& v) { return v < 100; }; + auto proj = [](const int& v) { return v * 10; }; + + auto result = etl::ranges::stable_partition(vec, pred, proj); + + for (auto it = vec.begin(); it != result.begin(); ++it) + { + CHECK((*it * 10) < 100); + } + + for (auto it = result.begin(); it != result.end(); ++it) + { + CHECK((*it * 10) >= 100); + } + + // Verify stability + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(3, vec[1]); + CHECK_EQUAL(2, vec[2]); + CHECK_EQUAL(20, vec[3]); + CHECK_EQUAL(10, vec[4]); + CHECK_EQUAL(30, vec[5]); + } + + //************************************************************************* + TEST(ranges_stable_partition_preserves_elements) + { + std::vector vec{5, 1, 4, 2, 3}; + std::vector sorted_original{1, 2, 3, 4, 5}; + auto pred = [](const int& v) { return v <= 3; }; + + etl::ranges::stable_partition(vec, pred); + + // All original elements should still be present + std::vector sorted_result(vec.begin(), vec.end()); + std::sort(sorted_result.begin(), sorted_result.end()); + CHECK(sorted_result == sorted_original); + } + + //************************************************************************* + TEST(ranges_stable_partition_matches_std) + { + std::vector data_std{1, 20, 3, 10, 2, 30, 5, 15, 7}; + std::vector data_etl = data_std; + auto pred = [](const int& v) { return v < 10; }; + + std::stable_partition(data_std.begin(), data_std.end(), pred); + etl::ranges::stable_partition(data_etl.begin(), data_etl.end(), pred); + + bool are_equal = std::equal(data_std.begin(), data_std.end(), data_etl.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST(ranges_sort_iterator) + { + std::vector vec{5, 3, 1, 4, 2}; + std::vector expected{1, 2, 3, 4, 5}; + + auto result = etl::ranges::sort(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_sort_range) + { + std::vector vec{5, 3, 1, 4, 2}; + std::vector expected{1, 2, 3, 4, 5}; + + auto result = etl::ranges::sort(vec); + + CHECK(result == vec.end()); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_sort_with_comparator_iterator) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector expected{5, 4, 3, 2, 1}; + + auto result = etl::ranges::sort(vec.begin(), vec.end(), etl::greater{}); + + CHECK(result == vec.end()); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_sort_with_comparator_range) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector expected{5, 4, 3, 2, 1}; + + auto result = etl::ranges::sort(vec, etl::greater{}); + + CHECK(result == vec.end()); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_sort_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {1, 10}, {2, 20}}; + + etl::ranges::sort(vec.begin(), vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(1, vec[0].key); + CHECK_EQUAL(2, vec[1].key); + CHECK_EQUAL(3, vec[2].key); + CHECK_EQUAL(10, vec[0].value); + CHECK_EQUAL(20, vec[1].value); + CHECK_EQUAL(30, vec[2].value); + } + + //************************************************************************* + TEST(ranges_sort_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {1, 10}, {2, 20}}; + + etl::ranges::sort(vec, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(1, vec[0].key); + CHECK_EQUAL(2, vec[1].key); + CHECK_EQUAL(3, vec[2].key); + CHECK_EQUAL(10, vec[0].value); + CHECK_EQUAL(20, vec[1].value); + CHECK_EQUAL(30, vec[2].value); + } + + //************************************************************************* + TEST(ranges_sort_empty) + { + std::vector vec{}; + + auto result = etl::ranges::sort(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + CHECK(vec.empty()); + } + + //************************************************************************* + TEST(ranges_sort_single_element) + { + std::vector vec{42}; + + auto result = etl::ranges::sort(vec); + + CHECK(result == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_sort_already_sorted) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector expected{1, 2, 3, 4, 5}; + + etl::ranges::sort(vec); + + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_sort_duplicates) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}; + std::vector expected{1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9}; + + etl::ranges::sort(vec); + + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_sort_matches_std) + { + std::vector data_std{9, 3, 7, 1, 5, 8, 2, 6, 4, 10}; + std::vector data_etl = data_std; + + std::sort(data_std.begin(), data_std.end()); + etl::ranges::sort(data_etl); + + bool are_equal = std::equal(data_std.begin(), data_std.end(), data_etl.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST(ranges_stable_sort_iterator) + { + std::vector vec{5, 3, 1, 4, 2}; + std::vector expected{1, 2, 3, 4, 5}; + + auto result = etl::ranges::stable_sort(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_stable_sort_range) + { + std::vector vec{5, 3, 1, 4, 2}; + std::vector expected{1, 2, 3, 4, 5}; + + auto result = etl::ranges::stable_sort(vec); + + CHECK(result == vec.end()); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_stable_sort_with_comparator_iterator) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector expected{5, 4, 3, 2, 1}; + + auto result = etl::ranges::stable_sort(vec.begin(), vec.end(), etl::greater{}); + + CHECK(result == vec.end()); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_stable_sort_with_comparator_range) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector expected{5, 4, 3, 2, 1}; + + auto result = etl::ranges::stable_sort(vec, etl::greater{}); + + CHECK(result == vec.end()); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_stable_sort_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {1, 10}, {2, 20}}; + + etl::ranges::stable_sort(vec.begin(), vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(1, vec[0].key); + CHECK_EQUAL(2, vec[1].key); + CHECK_EQUAL(3, vec[2].key); + CHECK_EQUAL(10, vec[0].value); + CHECK_EQUAL(20, vec[1].value); + CHECK_EQUAL(30, vec[2].value); + } + + //************************************************************************* + TEST(ranges_stable_sort_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {1, 10}, {2, 20}}; + + etl::ranges::stable_sort(vec, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(1, vec[0].key); + CHECK_EQUAL(2, vec[1].key); + CHECK_EQUAL(3, vec[2].key); + CHECK_EQUAL(10, vec[0].value); + CHECK_EQUAL(20, vec[1].value); + CHECK_EQUAL(30, vec[2].value); + } + + //************************************************************************* + TEST(ranges_stable_sort_empty) + { + std::vector vec{}; + + auto result = etl::ranges::stable_sort(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + CHECK(vec.empty()); + } + + //************************************************************************* + TEST(ranges_stable_sort_single_element) + { + std::vector vec{42}; + + auto result = etl::ranges::stable_sort(vec); + + CHECK(result == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_stable_sort_already_sorted) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector expected{1, 2, 3, 4, 5}; + + etl::ranges::stable_sort(vec); + + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_stable_sort_duplicates) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}; + std::vector expected{1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9}; + + etl::ranges::stable_sort(vec); + + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_stable_sort_stability) + { + // Verify stability: elements with equal keys preserve their relative order + struct Item { int key; int order; }; + std::vector vec{{2, 0}, {1, 1}, {2, 2}, {1, 3}, {3, 4}, {2, 5}}; + + etl::ranges::stable_sort(vec, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + // key==1 items should keep original relative order + CHECK_EQUAL(1, vec[0].key); CHECK_EQUAL(1, vec[0].order); + CHECK_EQUAL(1, vec[1].key); CHECK_EQUAL(3, vec[1].order); + // key==2 items should keep original relative order + CHECK_EQUAL(2, vec[2].key); CHECK_EQUAL(0, vec[2].order); + CHECK_EQUAL(2, vec[3].key); CHECK_EQUAL(2, vec[3].order); + CHECK_EQUAL(2, vec[4].key); CHECK_EQUAL(5, vec[4].order); + // key==3 items + CHECK_EQUAL(3, vec[5].key); CHECK_EQUAL(4, vec[5].order); + } + + //************************************************************************* + TEST(ranges_stable_sort_matches_std) + { + std::vector data_std{9, 3, 7, 1, 5, 8, 2, 6, 4, 10}; + std::vector data_etl = data_std; + + std::stable_sort(data_std.begin(), data_std.end()); + etl::ranges::stable_sort(data_etl); + + bool are_equal = std::equal(data_std.begin(), data_std.end(), data_etl.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST(ranges_partial_sort_iterator) + { + std::vector vec{5, 3, 1, 4, 2}; + std::vector expected_prefix{1, 2, 3}; + + auto result = etl::ranges::partial_sort(vec.begin(), vec.begin() + 3, vec.end()); + + CHECK(result == vec.end()); + CHECK(std::equal(expected_prefix.begin(), expected_prefix.end(), vec.begin())); + } + + //************************************************************************* + TEST(ranges_partial_sort_range) + { + std::vector vec{5, 3, 1, 4, 2}; + std::vector expected_prefix{1, 2, 3}; + + auto result = etl::ranges::partial_sort(vec, vec.begin() + 3); + + CHECK(result == vec.end()); + CHECK(std::equal(expected_prefix.begin(), expected_prefix.end(), vec.begin())); + } + + //************************************************************************* + TEST(ranges_partial_sort_with_comparator_iterator) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector expected_prefix{5, 4, 3}; + + auto result = etl::ranges::partial_sort(vec.begin(), vec.begin() + 3, vec.end(), etl::greater{}); + + CHECK(result == vec.end()); + CHECK(std::equal(expected_prefix.begin(), expected_prefix.end(), vec.begin())); + } + + //************************************************************************* + TEST(ranges_partial_sort_with_comparator_range) + { + std::vector vec{1, 2, 3, 4, 5}; + std::vector expected_prefix{5, 4, 3}; + + auto result = etl::ranges::partial_sort(vec, vec.begin() + 3, etl::greater{}); + + CHECK(result == vec.end()); + CHECK(std::equal(expected_prefix.begin(), expected_prefix.end(), vec.begin())); + } + + //************************************************************************* + TEST(ranges_partial_sort_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {1, 10}, {5, 50}, {2, 20}, {4, 40}}; + + etl::ranges::partial_sort(vec.begin(), vec.begin() + 3, vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(1, vec[0].key); + CHECK_EQUAL(2, vec[1].key); + CHECK_EQUAL(3, vec[2].key); + CHECK_EQUAL(10, vec[0].value); + CHECK_EQUAL(20, vec[1].value); + CHECK_EQUAL(30, vec[2].value); + } + + //************************************************************************* + TEST(ranges_partial_sort_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {1, 10}, {5, 50}, {2, 20}, {4, 40}}; + + etl::ranges::partial_sort(vec, vec.begin() + 3, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(1, vec[0].key); + CHECK_EQUAL(2, vec[1].key); + CHECK_EQUAL(3, vec[2].key); + CHECK_EQUAL(10, vec[0].value); + CHECK_EQUAL(20, vec[1].value); + CHECK_EQUAL(30, vec[2].value); + } + + //************************************************************************* + TEST(ranges_partial_sort_empty) + { + std::vector vec{}; + + auto result = etl::ranges::partial_sort(vec.begin(), vec.begin(), vec.end()); + + CHECK(result == vec.end()); + CHECK(vec.empty()); + } + + //************************************************************************* + TEST(ranges_partial_sort_single_element) + { + std::vector vec{42}; + + auto result = etl::ranges::partial_sort(vec, vec.begin() + 1); + + CHECK(result == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_partial_sort_middle_equals_last) + { + std::vector vec{5, 3, 1, 4, 2}; + std::vector expected{1, 2, 3, 4, 5}; + + auto result = etl::ranges::partial_sort(vec.begin(), vec.end(), vec.end()); + + CHECK(result == vec.end()); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_partial_sort_middle_equals_first) + { + std::vector vec{5, 3, 1, 4, 2}; + std::vector original = vec; + + auto result = etl::ranges::partial_sort(vec.begin(), vec.begin(), vec.end()); + + CHECK(result == vec.end()); + CHECK(vec == original); + } + + //************************************************************************* + TEST(ranges_partial_sort_matches_std) + { + std::vector data_std{9, 3, 7, 1, 5, 8, 2, 6, 4, 10}; + std::vector data_etl = data_std; + + std::partial_sort(data_std.begin(), data_std.begin() + 4, data_std.end()); + etl::ranges::partial_sort(data_etl.begin(), data_etl.begin() + 4, data_etl.end()); + + // The first 4 elements should be the same smallest values in sorted order + bool prefix_equal = std::equal(data_std.begin(), data_std.begin() + 4, data_etl.begin()); + CHECK(prefix_equal); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_iterator) + { + std::vector input{5, 3, 1, 4, 2}; + std::vector output(3, 0); + std::vector expected{1, 2, 3}; + + auto result = etl::ranges::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(result.in == input.end()); + CHECK(result.out == output.end()); + CHECK(output == expected); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_range) + { + std::vector input{5, 3, 1, 4, 2}; + std::vector output(3, 0); + std::vector expected{1, 2, 3}; + + auto result = etl::ranges::partial_sort_copy(input, output); + + CHECK(result.in == input.end()); + CHECK(result.out == output.end()); + CHECK(output == expected); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_with_comparator_iterator) + { + std::vector input{1, 2, 3, 4, 5}; + std::vector output(3, 0); + std::vector expected{5, 4, 3}; + + auto result = etl::ranges::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end(), etl::greater{}); + + CHECK(result.in == input.end()); + CHECK(result.out == output.end()); + CHECK(output == expected); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_with_comparator_range) + { + std::vector input{1, 2, 3, 4, 5}; + std::vector output(3, 0); + std::vector expected{5, 4, 3}; + + auto result = etl::ranges::partial_sort_copy(input, output, etl::greater{}); + + CHECK(result.in == input.end()); + CHECK(result.out == output.end()); + CHECK(output == expected); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector input{{3, 30}, {1, 10}, {5, 50}, {2, 20}, {4, 40}}; + std::vector output(3, Item{0, 0}); + + etl::ranges::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end(), + etl::ranges::less{}, [](const Item& item) { return item.key; }, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(1, output[0].key); + CHECK_EQUAL(2, output[1].key); + CHECK_EQUAL(3, output[2].key); + CHECK_EQUAL(10, output[0].value); + CHECK_EQUAL(20, output[1].value); + CHECK_EQUAL(30, output[2].value); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_with_projection_range) + { + struct Item { int key; int value; }; + std::vector input{{3, 30}, {1, 10}, {5, 50}, {2, 20}, {4, 40}}; + std::vector output(3, Item{0, 0}); + + etl::ranges::partial_sort_copy(input, output, + etl::ranges::less{}, [](const Item& item) { return item.key; }, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(1, output[0].key); + CHECK_EQUAL(2, output[1].key); + CHECK_EQUAL(3, output[2].key); + CHECK_EQUAL(10, output[0].value); + CHECK_EQUAL(20, output[1].value); + CHECK_EQUAL(30, output[2].value); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_empty_input) + { + std::vector input{}; + std::vector output(3, 0); + std::vector expected{0, 0, 0}; + + auto result = etl::ranges::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(result.in == input.end()); + CHECK(result.out == output.begin()); + CHECK(output == expected); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_empty_output) + { + std::vector input{5, 3, 1, 4, 2}; + std::vector output{}; + + auto result = etl::ranges::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(result.in == input.end()); + CHECK(result.out == output.end()); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_output_larger_than_input) + { + std::vector input{3, 1, 2}; + std::vector output(5, 0); + std::vector expected{1, 2, 3, 0, 0}; + + auto result = etl::ranges::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(result.in == input.end()); + CHECK(result.out == output.begin() + 3); + CHECK(output == expected); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_output_same_size_as_input) + { + std::vector input{5, 3, 1, 4, 2}; + std::vector output(5, 0); + std::vector expected{1, 2, 3, 4, 5}; + + auto result = etl::ranges::partial_sort_copy(input, output); + + CHECK(result.in == input.end()); + CHECK(result.out == output.end()); + CHECK(output == expected); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_single_element) + { + std::vector input{42}; + std::vector output(1, 0); + + auto result = etl::ranges::partial_sort_copy(input, output); + + CHECK(result.in == input.end()); + CHECK(result.out == output.end()); + CHECK_EQUAL(42, output[0]); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_does_not_modify_input) + { + std::vector input{5, 3, 1, 4, 2}; + std::vector original = input; + std::vector output(3, 0); + + etl::ranges::partial_sort_copy(input, output); + + CHECK(input == original); + } + + //************************************************************************* + TEST(ranges_partial_sort_copy_matches_std) + { + std::vector data{9, 3, 7, 1, 5, 8, 2, 6, 4, 10}; + std::vector output_std(4, 0); + std::vector output_etl(4, 0); + + std::partial_sort_copy(data.begin(), data.end(), output_std.begin(), output_std.end()); + etl::ranges::partial_sort_copy(data.begin(), data.end(), output_etl.begin(), output_etl.end()); + + CHECK(output_std == output_etl); + } + + //************************************************************************* + TEST(ranges_nth_element_iterator) + { + std::vector vec{5, 3, 1, 4, 2}; + + auto result = etl::ranges::nth_element(vec.begin(), vec.begin() + 2, vec.end()); + + CHECK(result == vec.end()); + CHECK_EQUAL(3, vec[2]); + + // All elements before nth should be <= vec[2] + for (int i = 0; i < 2; ++i) + { + CHECK(vec[i] <= vec[2]); + } + + // All elements after nth should be >= vec[2] + for (int i = 3; i < 5; ++i) + { + CHECK(vec[i] >= vec[2]); + } + } + + //************************************************************************* + TEST(ranges_nth_element_range) + { + std::vector vec{5, 3, 1, 4, 2}; + + auto result = etl::ranges::nth_element(vec, vec.begin() + 2); + + CHECK(result == vec.end()); + CHECK_EQUAL(3, vec[2]); + + for (int i = 0; i < 2; ++i) + { + CHECK(vec[i] <= vec[2]); + } + + for (int i = 3; i < 5; ++i) + { + CHECK(vec[i] >= vec[2]); + } + } + + //************************************************************************* + TEST(ranges_nth_element_with_comparator_iterator) + { + std::vector vec{1, 2, 3, 4, 5}; + + auto result = etl::ranges::nth_element(vec.begin(), vec.begin() + 2, vec.end(), etl::greater{}); + + CHECK(result == vec.end()); + CHECK_EQUAL(3, vec[2]); + + for (int i = 0; i < 2; ++i) + { + CHECK(vec[i] >= vec[2]); + } + + for (int i = 3; i < 5; ++i) + { + CHECK(vec[i] <= vec[2]); + } + } + + //************************************************************************* + TEST(ranges_nth_element_with_comparator_range) + { + std::vector vec{1, 2, 3, 4, 5}; + + auto result = etl::ranges::nth_element(vec, vec.begin() + 2, etl::greater{}); + + CHECK(result == vec.end()); + CHECK_EQUAL(3, vec[2]); + + for (int i = 0; i < 2; ++i) + { + CHECK(vec[i] >= vec[2]); + } + + for (int i = 3; i < 5; ++i) + { + CHECK(vec[i] <= vec[2]); + } + } + + //************************************************************************* + TEST(ranges_nth_element_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {1, 10}, {5, 50}, {2, 20}, {4, 40}}; + + etl::ranges::nth_element(vec.begin(), vec.begin() + 2, vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(3, vec[2].key); + CHECK_EQUAL(30, vec[2].value); + + for (int i = 0; i < 2; ++i) + { + CHECK(vec[i].key <= vec[2].key); + } + + for (int i = 3; i < 5; ++i) + { + CHECK(vec[i].key >= vec[2].key); + } + } + + //************************************************************************* + TEST(ranges_nth_element_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {1, 10}, {5, 50}, {2, 20}, {4, 40}}; + + etl::ranges::nth_element(vec, vec.begin() + 2, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(3, vec[2].key); + CHECK_EQUAL(30, vec[2].value); + + for (int i = 0; i < 2; ++i) + { + CHECK(vec[i].key <= vec[2].key); + } + + for (int i = 3; i < 5; ++i) + { + CHECK(vec[i].key >= vec[2].key); + } + } + + //************************************************************************* + TEST(ranges_nth_element_empty) + { + std::vector vec{}; + + auto result = etl::ranges::nth_element(vec.begin(), vec.begin(), vec.end()); + + CHECK(result == vec.end()); + CHECK(vec.empty()); + } + + //************************************************************************* + TEST(ranges_nth_element_single_element) + { + std::vector vec{42}; + + auto result = etl::ranges::nth_element(vec, vec.begin()); + + CHECK(result == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_nth_element_first_position) + { + std::vector vec{5, 3, 1, 4, 2}; + + etl::ranges::nth_element(vec.begin(), vec.begin(), vec.end()); + + CHECK_EQUAL(1, vec[0]); + + for (size_t i = 1; i < vec.size(); ++i) + { + CHECK(vec[i] >= vec[0]); + } + } + + //************************************************************************* + TEST(ranges_nth_element_last_position) + { + std::vector vec{5, 3, 1, 4, 2}; + + etl::ranges::nth_element(vec.begin(), vec.begin() + 4, vec.end()); + + CHECK_EQUAL(5, vec[4]); + + for (int i = 0; i < 4; ++i) + { + CHECK(vec[i] <= vec[4]); + } + } + + //************************************************************************* + TEST(ranges_nth_element_matches_std) + { + std::vector data_std{9, 3, 7, 1, 5, 8, 2, 6, 4, 10}; + std::vector data_etl = data_std; + + std::nth_element(data_std.begin(), data_std.begin() + 4, data_std.end()); + etl::ranges::nth_element(data_etl.begin(), data_etl.begin() + 4, data_etl.end()); + + // The element at position 4 should be the same + CHECK_EQUAL(data_std[4], data_etl[4]); + } + + //************************************************************************* + TEST(ranges_is_sorted_until_iterator_sentinel_sorted) + { + std::vector vec{1, 2, 3, 4, 5}; + + auto result = etl::ranges::is_sorted_until(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_is_sorted_until_range_sorted) + { + std::vector vec{1, 2, 3, 4, 5}; + + auto result = etl::ranges::is_sorted_until(vec); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_is_sorted_until_iterator_sentinel_not_sorted) + { + std::vector vec{1, 2, 5, 4, 3}; + + auto result = etl::ranges::is_sorted_until(vec.begin(), vec.end()); + + CHECK(result == vec.begin() + 3); + } + + //************************************************************************* + TEST(ranges_is_sorted_until_range_not_sorted) + { + std::vector vec{1, 2, 5, 4, 3}; + + auto result = etl::ranges::is_sorted_until(vec); + + CHECK(result == vec.begin() + 3); + } + + //************************************************************************* + TEST(ranges_is_sorted_until_empty) + { + std::vector vec{}; + + auto result = etl::ranges::is_sorted_until(vec.begin(), vec.end()); + CHECK(result == vec.end()); + + result = etl::ranges::is_sorted_until(vec); + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_is_sorted_until_single) + { + std::vector vec{42}; + + auto result = etl::ranges::is_sorted_until(vec.begin(), vec.end()); + CHECK(result == vec.end()); + + result = etl::ranges::is_sorted_until(vec); + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_is_sorted_until_with_comparator) + { + std::vector vec{5, 4, 3, 2, 1}; + + auto result = etl::ranges::is_sorted_until(vec.begin(), vec.end(), etl::greater{}); + CHECK(result == vec.end()); + + result = etl::ranges::is_sorted_until(vec, etl::greater{}); + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_is_sorted_until_with_projection) + { + std::vector vec{-1, -2, -3, -4, -5}; + auto proj = [](const int& v) { return -v; }; + + // After projection, values become 1,2,3,4,5 which is sorted + auto result = etl::ranges::is_sorted_until(vec.begin(), vec.end(), etl::ranges::less{}, proj); + CHECK(result == vec.end()); + + result = etl::ranges::is_sorted_until(vec, etl::ranges::less{}, proj); + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_is_sorted_iterator_sentinel_sorted) + { + std::vector vec{1, 2, 3, 4, 5}; + + CHECK(etl::ranges::is_sorted(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_is_sorted_range_sorted) + { + std::vector vec{1, 2, 3, 4, 5}; + + CHECK(etl::ranges::is_sorted(vec)); + } + + //************************************************************************* + TEST(ranges_is_sorted_iterator_sentinel_not_sorted) + { + std::vector vec{1, 3, 2, 4, 5}; + + CHECK_FALSE(etl::ranges::is_sorted(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_is_sorted_range_not_sorted) + { + std::vector vec{1, 3, 2, 4, 5}; + + CHECK_FALSE(etl::ranges::is_sorted(vec)); + } + + //************************************************************************* + TEST(ranges_is_sorted_empty) + { + std::vector vec{}; + + CHECK(etl::ranges::is_sorted(vec.begin(), vec.end())); + CHECK(etl::ranges::is_sorted(vec)); + } + + //************************************************************************* + TEST(ranges_is_sorted_single) + { + std::vector vec{42}; + + CHECK(etl::ranges::is_sorted(vec.begin(), vec.end())); + CHECK(etl::ranges::is_sorted(vec)); + } + + //************************************************************************* + TEST(ranges_is_sorted_with_comparator) + { + std::vector vec{5, 4, 3, 2, 1}; + + CHECK(etl::ranges::is_sorted(vec.begin(), vec.end(), etl::greater{})); + CHECK(etl::ranges::is_sorted(vec, etl::greater{})); + } + + //************************************************************************* + TEST(ranges_is_sorted_with_comparator_not_sorted) + { + std::vector vec{1, 2, 3, 4, 5}; + + CHECK_FALSE(etl::ranges::is_sorted(vec.begin(), vec.end(), etl::greater{})); + CHECK_FALSE(etl::ranges::is_sorted(vec, etl::greater{})); + } + + //************************************************************************* + TEST(ranges_is_sorted_with_projection) + { + std::vector vec{-1, -2, -3, -4, -5}; + auto proj = [](const int& v) { return -v; }; + + // After projection, values become 1,2,3,4,5 which is sorted + CHECK(etl::ranges::is_sorted(vec.begin(), vec.end(), etl::ranges::less{}, proj)); + CHECK(etl::ranges::is_sorted(vec, etl::ranges::less{}, proj)); + } + + //************************************************************************* + TEST(ranges_is_sorted_with_projection_not_sorted) + { + std::vector vec{1, 2, 3, 4, 5}; + auto proj = [](const int& v) { return -v; }; + + // After projection, values become -1,-2,-3,-4,-5 which is not sorted ascending + CHECK_FALSE(etl::ranges::is_sorted(vec.begin(), vec.end(), etl::ranges::less{}, proj)); + CHECK_FALSE(etl::ranges::is_sorted(vec, etl::ranges::less{}, proj)); + } + + //************************************************************************* + TEST(ranges_is_sorted_equal_elements) + { + std::vector vec{3, 3, 3, 3, 3}; + + CHECK(etl::ranges::is_sorted(vec.begin(), vec.end())); + CHECK(etl::ranges::is_sorted(vec)); + } + + //************************************************************************* + TEST(ranges_is_sorted_two_elements_sorted) + { + std::vector vec{1, 2}; + + CHECK(etl::ranges::is_sorted(vec.begin(), vec.end())); + CHECK(etl::ranges::is_sorted(vec)); + } + + //************************************************************************* + TEST(ranges_is_sorted_two_elements_not_sorted) + { + std::vector vec{2, 1}; + + CHECK_FALSE(etl::ranges::is_sorted(vec.begin(), vec.end())); + CHECK_FALSE(etl::ranges::is_sorted(vec)); + } + + //************************************************************************* + TEST(ranges_lower_bound_iterator) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + auto result = etl::ranges::lower_bound(vec.begin(), vec.end(), 5); + CHECK_EQUAL(5, *result); + CHECK_EQUAL(4, etl::distance(vec.begin(), result)); + } + + //************************************************************************* + TEST(ranges_lower_bound_range) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + auto result = etl::ranges::lower_bound(vec, 5); + CHECK_EQUAL(5, *result); + CHECK_EQUAL(4, etl::distance(vec.begin(), result)); + } + + //************************************************************************* + TEST(ranges_lower_bound_value_not_present) + { + std::vector vec{1, 3, 5, 7, 9}; + + auto result_it = etl::ranges::lower_bound(vec.begin(), vec.end(), 4); + CHECK_EQUAL(5, *result_it); + CHECK_EQUAL(2, etl::distance(vec.begin(), result_it)); + + auto result_r = etl::ranges::lower_bound(vec, 4); + CHECK_EQUAL(5, *result_r); + CHECK_EQUAL(2, etl::distance(vec.begin(), result_r)); + } + + //************************************************************************* + TEST(ranges_lower_bound_value_at_beginning) + { + std::vector vec{1, 2, 3, 4, 5}; + + auto result_it = etl::ranges::lower_bound(vec.begin(), vec.end(), 1); + CHECK(result_it == vec.begin()); + + auto result_r = etl::ranges::lower_bound(vec, 1); + CHECK(result_r == vec.begin()); + } + + //************************************************************************* + TEST(ranges_lower_bound_value_past_end) + { + std::vector vec{1, 2, 3, 4, 5}; + + auto result_it = etl::ranges::lower_bound(vec.begin(), vec.end(), 10); + CHECK(result_it == vec.end()); + + auto result_r = etl::ranges::lower_bound(vec, 10); + CHECK(result_r == vec.end()); + } + + //************************************************************************* + TEST(ranges_lower_bound_empty) + { + std::vector vec{}; + + auto result_it = etl::ranges::lower_bound(vec.begin(), vec.end(), 5); + CHECK(result_it == vec.end()); + + auto result_r = etl::ranges::lower_bound(vec, 5); + CHECK(result_r == vec.end()); + } + + //************************************************************************* + TEST(ranges_lower_bound_with_comparator_iterator) + { + std::vector vec{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + + auto result = etl::ranges::lower_bound(vec.begin(), vec.end(), 5, etl::greater{}); + CHECK_EQUAL(5, *result); + CHECK_EQUAL(5, etl::distance(vec.begin(), result)); + } + + //************************************************************************* + TEST(ranges_lower_bound_with_comparator_range) + { + std::vector vec{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + + auto result = etl::ranges::lower_bound(vec, 5, etl::greater{}); + CHECK_EQUAL(5, *result); + CHECK_EQUAL(5, etl::distance(vec.begin(), result)); + } + + //************************************************************************* + TEST(ranges_lower_bound_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}}; + + auto result = etl::ranges::lower_bound(vec.begin(), vec.end(), 3, etl::ranges::less{}, [](const Item& item) { return item.key; }); + CHECK_EQUAL(3, result->key); + CHECK_EQUAL(30, result->value); + } + + //************************************************************************* + TEST(ranges_lower_bound_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}}; + + auto result = etl::ranges::lower_bound(vec, 3, etl::ranges::less{}, [](const Item& item) { return item.key; }); + CHECK_EQUAL(3, result->key); + CHECK_EQUAL(30, result->value); + } + + //************************************************************************* + TEST(ranges_lower_bound_duplicates) + { + std::vector vec{1, 2, 2, 2, 3, 4, 5}; + + auto result_it = etl::ranges::lower_bound(vec.begin(), vec.end(), 2); + CHECK_EQUAL(2, *result_it); + CHECK_EQUAL(1, etl::distance(vec.begin(), result_it)); + + auto result_r = etl::ranges::lower_bound(vec, 2); + CHECK_EQUAL(2, *result_r); + CHECK_EQUAL(1, etl::distance(vec.begin(), result_r)); + } + + //************************************************************************* + TEST(ranges_lower_bound_single_element) + { + std::vector vec{5}; + + auto result_found = etl::ranges::lower_bound(vec, 5); + CHECK(result_found == vec.begin()); + + auto result_less = etl::ranges::lower_bound(vec, 3); + CHECK(result_less == vec.begin()); + + auto result_greater = etl::ranges::lower_bound(vec, 10); + CHECK(result_greater == vec.end()); + } + + //************************************************************************* + TEST(ranges_lower_bound_matches_std) + { + std::vector vec{1, 3, 5, 7, 9, 11, 13, 15}; + + for (int val = 0; val <= 16; ++val) + { + auto std_result = std::lower_bound(vec.begin(), vec.end(), val); + auto etl_result = etl::ranges::lower_bound(vec.begin(), vec.end(), val); + CHECK(std_result == etl_result); + } + } + + //************************************************************************* + TEST(ranges_upper_bound_iterator) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + auto result = etl::ranges::upper_bound(vec.begin(), vec.end(), 5); + CHECK_EQUAL(6, *result); + CHECK_EQUAL(5, etl::distance(vec.begin(), result)); + } + + //************************************************************************* + TEST(ranges_upper_bound_range) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + auto result = etl::ranges::upper_bound(vec, 5); + CHECK_EQUAL(6, *result); + CHECK_EQUAL(5, etl::distance(vec.begin(), result)); + } + + //************************************************************************* + TEST(ranges_upper_bound_value_not_present) + { + std::vector vec{1, 3, 5, 7, 9}; + + auto result_it = etl::ranges::upper_bound(vec.begin(), vec.end(), 4); + CHECK_EQUAL(5, *result_it); + CHECK_EQUAL(2, etl::distance(vec.begin(), result_it)); + + auto result_r = etl::ranges::upper_bound(vec, 4); + CHECK_EQUAL(5, *result_r); + CHECK_EQUAL(2, etl::distance(vec.begin(), result_r)); + } + + //************************************************************************* + TEST(ranges_upper_bound_value_at_beginning) + { + std::vector vec{1, 2, 3, 4, 5}; + + auto result_it = etl::ranges::upper_bound(vec.begin(), vec.end(), 1); + CHECK_EQUAL(2, *result_it); + CHECK_EQUAL(1, etl::distance(vec.begin(), result_it)); + + auto result_r = etl::ranges::upper_bound(vec, 1); + CHECK_EQUAL(2, *result_r); + CHECK_EQUAL(1, etl::distance(vec.begin(), result_r)); + } + + //************************************************************************* + TEST(ranges_upper_bound_value_past_end) + { + std::vector vec{1, 2, 3, 4, 5}; + + auto result_it = etl::ranges::upper_bound(vec.begin(), vec.end(), 10); + CHECK(result_it == vec.end()); + + auto result_r = etl::ranges::upper_bound(vec, 10); + CHECK(result_r == vec.end()); + } + + //************************************************************************* + TEST(ranges_upper_bound_empty) + { + std::vector vec{}; + + auto result_it = etl::ranges::upper_bound(vec.begin(), vec.end(), 5); + CHECK(result_it == vec.end()); + + auto result_r = etl::ranges::upper_bound(vec, 5); + CHECK(result_r == vec.end()); + } + + //************************************************************************* + TEST(ranges_upper_bound_with_comparator_iterator) + { + std::vector vec{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + + auto result = etl::ranges::upper_bound(vec.begin(), vec.end(), 5, etl::greater{}); + CHECK_EQUAL(4, *result); + CHECK_EQUAL(6, etl::distance(vec.begin(), result)); + } + + //************************************************************************* + TEST(ranges_upper_bound_with_comparator_range) + { + std::vector vec{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + + auto result = etl::ranges::upper_bound(vec, 5, etl::greater{}); + CHECK_EQUAL(4, *result); + CHECK_EQUAL(6, etl::distance(vec.begin(), result)); + } + + //************************************************************************* + TEST(ranges_upper_bound_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}}; + + auto result = etl::ranges::upper_bound(vec.begin(), vec.end(), 3, etl::ranges::less{}, [](const Item& item) { return item.key; }); + CHECK_EQUAL(4, result->key); + CHECK_EQUAL(40, result->value); + } + + //************************************************************************* + TEST(ranges_upper_bound_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}}; + + auto result = etl::ranges::upper_bound(vec, 3, etl::ranges::less{}, [](const Item& item) { return item.key; }); + CHECK_EQUAL(4, result->key); + CHECK_EQUAL(40, result->value); + } + + //************************************************************************* + TEST(ranges_upper_bound_duplicates) + { + std::vector vec{1, 2, 2, 2, 3, 4, 5}; + + auto result_it = etl::ranges::upper_bound(vec.begin(), vec.end(), 2); + CHECK_EQUAL(3, *result_it); + CHECK_EQUAL(4, etl::distance(vec.begin(), result_it)); + + auto result_r = etl::ranges::upper_bound(vec, 2); + CHECK_EQUAL(3, *result_r); + CHECK_EQUAL(4, etl::distance(vec.begin(), result_r)); + } + + //************************************************************************* + TEST(ranges_upper_bound_single_element) + { + std::vector vec{5}; + + auto result_found = etl::ranges::upper_bound(vec, 5); + CHECK(result_found == vec.end()); + + auto result_less = etl::ranges::upper_bound(vec, 3); + CHECK(result_less == vec.begin()); + + auto result_greater = etl::ranges::upper_bound(vec, 10); + CHECK(result_greater == vec.end()); + } + + //************************************************************************* + TEST(ranges_upper_bound_matches_std) + { + std::vector vec{1, 3, 5, 7, 9, 11, 13, 15}; + + for (int val = 0; val <= 16; ++val) + { + auto std_result = std::upper_bound(vec.begin(), vec.end(), val); + auto etl_result = etl::ranges::upper_bound(vec.begin(), vec.end(), val); + CHECK(std_result == etl_result); + } + } + + //************************************************************************* + TEST(ranges_equal_range_iterator) + { + std::vector vec{1, 2, 3, 3, 3, 4, 5, 6, 7, 8}; + + auto result = etl::ranges::equal_range(vec.begin(), vec.end(), 3); + CHECK_EQUAL(3, *result.begin()); + CHECK_EQUAL(2, etl::distance(vec.begin(), result.begin())); + CHECK_EQUAL(5, etl::distance(vec.begin(), result.end())); + } + + //************************************************************************* + TEST(ranges_equal_range_range) + { + std::vector vec{1, 2, 3, 3, 3, 4, 5, 6, 7, 8}; + + auto result = etl::ranges::equal_range(vec, 3); + CHECK_EQUAL(3, *result.begin()); + CHECK_EQUAL(2, etl::distance(vec.begin(), result.begin())); + CHECK_EQUAL(5, etl::distance(vec.begin(), result.end())); + } + + //************************************************************************* + TEST(ranges_equal_range_value_not_present) + { + std::vector vec{1, 3, 5, 7, 9}; + + auto result_it = etl::ranges::equal_range(vec.begin(), vec.end(), 4); + CHECK(result_it.begin() == result_it.end()); + CHECK_EQUAL(2, etl::distance(vec.begin(), result_it.begin())); + + auto result_r = etl::ranges::equal_range(vec, 4); + CHECK(result_r.begin() == result_r.end()); + CHECK_EQUAL(2, etl::distance(vec.begin(), result_r.begin())); + } + + //************************************************************************* + TEST(ranges_equal_range_value_at_beginning) + { + std::vector vec{1, 1, 1, 2, 3, 4, 5}; + + auto result_it = etl::ranges::equal_range(vec.begin(), vec.end(), 1); + CHECK(result_it.begin() == vec.begin()); + CHECK_EQUAL(3, etl::distance(result_it.begin(), result_it.end())); + + auto result_r = etl::ranges::equal_range(vec, 1); + CHECK(result_r.begin() == vec.begin()); + CHECK_EQUAL(3, etl::distance(result_r.begin(), result_r.end())); + } + + //************************************************************************* + TEST(ranges_equal_range_value_at_end) + { + std::vector vec{1, 2, 3, 4, 5, 5, 5}; + + auto result_it = etl::ranges::equal_range(vec.begin(), vec.end(), 5); + CHECK_EQUAL(4, etl::distance(vec.begin(), result_it.begin())); + CHECK(result_it.end() == vec.end()); + CHECK_EQUAL(3, etl::distance(result_it.begin(), result_it.end())); + + auto result_r = etl::ranges::equal_range(vec, 5); + CHECK_EQUAL(4, etl::distance(vec.begin(), result_r.begin())); + CHECK(result_r.end() == vec.end()); + CHECK_EQUAL(3, etl::distance(result_r.begin(), result_r.end())); + } + + //************************************************************************* + TEST(ranges_equal_range_value_past_end) + { + std::vector vec{1, 2, 3, 4, 5}; + + auto result_it = etl::ranges::equal_range(vec.begin(), vec.end(), 10); + CHECK(result_it.begin() == vec.end()); + CHECK(result_it.end() == vec.end()); + + auto result_r = etl::ranges::equal_range(vec, 10); + CHECK(result_r.begin() == vec.end()); + CHECK(result_r.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_equal_range_empty) + { + std::vector vec{}; + + auto result_it = etl::ranges::equal_range(vec.begin(), vec.end(), 5); + CHECK(result_it.begin() == vec.end()); + CHECK(result_it.end() == vec.end()); + + auto result_r = etl::ranges::equal_range(vec, 5); + CHECK(result_r.begin() == vec.end()); + CHECK(result_r.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_equal_range_with_comparator_iterator) + { + std::vector vec{10, 9, 8, 7, 6, 5, 5, 5, 4, 3, 2, 1}; + + auto result = etl::ranges::equal_range(vec.begin(), vec.end(), 5, etl::greater{}); + CHECK_EQUAL(5, *result.begin()); + CHECK_EQUAL(5, etl::distance(vec.begin(), result.begin())); + CHECK_EQUAL(8, etl::distance(vec.begin(), result.end())); + } + + //************************************************************************* + TEST(ranges_equal_range_with_comparator_range) + { + std::vector vec{10, 9, 8, 7, 6, 5, 5, 5, 4, 3, 2, 1}; + + auto result = etl::ranges::equal_range(vec, 5, etl::greater{}); + CHECK_EQUAL(5, *result.begin()); + CHECK_EQUAL(5, etl::distance(vec.begin(), result.begin())); + CHECK_EQUAL(8, etl::distance(vec.begin(), result.end())); + } + + //************************************************************************* + TEST(ranges_equal_range_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{1, 10}, {2, 20}, {3, 30}, {3, 31}, {3, 32}, {4, 40}, {5, 50}}; + + auto result = etl::ranges::equal_range(vec.begin(), vec.end(), 3, etl::ranges::less{}, [](const Item& item) { return item.key; }); + CHECK_EQUAL(3, result.begin()->key); + CHECK_EQUAL(30, result.begin()->value); + CHECK_EQUAL(3, etl::distance(result.begin(), result.end())); + } + + //************************************************************************* + TEST(ranges_equal_range_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{1, 10}, {2, 20}, {3, 30}, {3, 31}, {3, 32}, {4, 40}, {5, 50}}; + + auto result = etl::ranges::equal_range(vec, 3, etl::ranges::less{}, [](const Item& item) { return item.key; }); + CHECK_EQUAL(3, result.begin()->key); + CHECK_EQUAL(30, result.begin()->value); + CHECK_EQUAL(3, etl::distance(result.begin(), result.end())); + } + + //************************************************************************* + TEST(ranges_equal_range_duplicates) + { + std::vector vec{1, 2, 2, 2, 3, 4, 5}; + + auto result_it = etl::ranges::equal_range(vec.begin(), vec.end(), 2); + CHECK_EQUAL(1, etl::distance(vec.begin(), result_it.begin())); + CHECK_EQUAL(4, etl::distance(vec.begin(), result_it.end())); + CHECK_EQUAL(3, etl::distance(result_it.begin(), result_it.end())); + + auto result_r = etl::ranges::equal_range(vec, 2); + CHECK_EQUAL(1, etl::distance(vec.begin(), result_r.begin())); + CHECK_EQUAL(4, etl::distance(vec.begin(), result_r.end())); + CHECK_EQUAL(3, etl::distance(result_r.begin(), result_r.end())); + } + + //************************************************************************* + TEST(ranges_equal_range_single_element) + { + std::vector vec{5}; + + auto result_found = etl::ranges::equal_range(vec, 5); + CHECK(result_found.begin() == vec.begin()); + CHECK(result_found.end() == vec.end()); + + auto result_less = etl::ranges::equal_range(vec, 3); + CHECK(result_less.begin() == vec.begin()); + CHECK(result_less.end() == vec.begin()); + + auto result_greater = etl::ranges::equal_range(vec, 10); + CHECK(result_greater.begin() == vec.end()); + CHECK(result_greater.end() == vec.end()); + } + + //************************************************************************* + TEST(ranges_equal_range_matches_std) + { + std::vector vec{1, 2, 2, 3, 3, 3, 5, 5, 7, 9, 11, 13, 15}; + + for (int val = 0; val <= 16; ++val) + { + auto std_result = std::equal_range(vec.begin(), vec.end(), val); + auto etl_result = etl::ranges::equal_range(vec.begin(), vec.end(), val); + CHECK(std_result.first == etl_result.begin()); + CHECK(std_result.second == etl_result.end()); + } + } + + //************************************************************************* + TEST(ranges_binary_search_iterator_found) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + bool result = etl::ranges::binary_search(vec.begin(), vec.end(), 5); + CHECK(result); + } + + //************************************************************************* + TEST(ranges_binary_search_range_found) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + bool result = etl::ranges::binary_search(vec, 5); + CHECK(result); + } + + //************************************************************************* + TEST(ranges_binary_search_not_found) + { + std::vector vec{1, 3, 5, 7, 9}; + + bool result_it = etl::ranges::binary_search(vec.begin(), vec.end(), 4); + CHECK(!result_it); + + bool result_r = etl::ranges::binary_search(vec, 4); + CHECK(!result_r); + } + + //************************************************************************* + TEST(ranges_binary_search_value_at_beginning) + { + std::vector vec{1, 2, 3, 4, 5}; + + bool result_it = etl::ranges::binary_search(vec.begin(), vec.end(), 1); + CHECK(result_it); + + bool result_r = etl::ranges::binary_search(vec, 1); + CHECK(result_r); + } + + //************************************************************************* + TEST(ranges_binary_search_value_at_end) + { + std::vector vec{1, 2, 3, 4, 5}; + + bool result_it = etl::ranges::binary_search(vec.begin(), vec.end(), 5); + CHECK(result_it); + + bool result_r = etl::ranges::binary_search(vec, 5); + CHECK(result_r); + } + + //************************************************************************* + TEST(ranges_binary_search_value_past_end) + { + std::vector vec{1, 2, 3, 4, 5}; + + bool result_it = etl::ranges::binary_search(vec.begin(), vec.end(), 10); + CHECK(!result_it); + + bool result_r = etl::ranges::binary_search(vec, 10); + CHECK(!result_r); + } + + //************************************************************************* + TEST(ranges_binary_search_empty) + { + std::vector vec{}; + + bool result_it = etl::ranges::binary_search(vec.begin(), vec.end(), 5); + CHECK(!result_it); + + bool result_r = etl::ranges::binary_search(vec, 5); + CHECK(!result_r); + } + + //************************************************************************* + TEST(ranges_binary_search_with_comparator_iterator) + { + std::vector vec{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + + bool result = etl::ranges::binary_search(vec.begin(), vec.end(), 5, etl::greater{}); + CHECK(result); + } + + //************************************************************************* + TEST(ranges_binary_search_with_comparator_range) + { + std::vector vec{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + + bool result = etl::ranges::binary_search(vec, 5, etl::greater{}); + CHECK(result); + } + + //************************************************************************* + TEST(ranges_binary_search_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}}; + + bool result = etl::ranges::binary_search(vec.begin(), vec.end(), 3, etl::ranges::less{}, [](const Item& item) { return item.key; }); + CHECK(result); + + bool result_not_found = etl::ranges::binary_search(vec.begin(), vec.end(), 6, etl::ranges::less{}, [](const Item& item) { return item.key; }); + CHECK(!result_not_found); + } + + //************************************************************************* + TEST(ranges_binary_search_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}}; + + bool result = etl::ranges::binary_search(vec, 3, etl::ranges::less{}, [](const Item& item) { return item.key; }); + CHECK(result); + + bool result_not_found = etl::ranges::binary_search(vec, 6, etl::ranges::less{}, [](const Item& item) { return item.key; }); + CHECK(!result_not_found); + } + + //************************************************************************* + TEST(ranges_binary_search_duplicates) + { + std::vector vec{1, 2, 2, 2, 3, 4, 5}; + + bool result_it = etl::ranges::binary_search(vec.begin(), vec.end(), 2); + CHECK(result_it); + + bool result_r = etl::ranges::binary_search(vec, 2); + CHECK(result_r); + } + + //************************************************************************* + TEST(ranges_binary_search_single_element) + { + std::vector vec{5}; + + bool result_found = etl::ranges::binary_search(vec, 5); + CHECK(result_found); + + bool result_less = etl::ranges::binary_search(vec, 3); + CHECK(!result_less); + + bool result_greater = etl::ranges::binary_search(vec, 10); + CHECK(!result_greater); + } + + //************************************************************************* + TEST(ranges_binary_search_matches_std) + { + std::vector vec{1, 2, 2, 3, 3, 3, 5, 5, 7, 9, 11, 13, 15}; + + for (int val = 0; val <= 16; ++val) + { + bool std_result = std::binary_search(vec.begin(), vec.end(), val); + bool etl_result = etl::ranges::binary_search(vec.begin(), vec.end(), val); + CHECK_EQUAL(std_result, etl_result); + } + } + + //************************************************************************* + TEST(ranges_includes_iterator_basic) + { + std::vector vec1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector vec2{2, 4, 6}; + + bool result = etl::ranges::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); + CHECK(result); + } + + //************************************************************************* + TEST(ranges_includes_range_basic) + { + std::vector vec1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector vec2{2, 4, 6}; + + bool result = etl::ranges::includes(vec1, vec2); + CHECK(result); + } + + //************************************************************************* + TEST(ranges_includes_not_included) + { + std::vector vec1{1, 2, 3, 5, 7}; + std::vector vec2{2, 4, 6}; + + bool result_it = etl::ranges::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); + CHECK(!result_it); + + bool result_r = etl::ranges::includes(vec1, vec2); + CHECK(!result_r); + } + + //************************************************************************* + TEST(ranges_includes_empty_second_range) + { + std::vector vec1{1, 2, 3}; + std::vector vec2{}; + + bool result_it = etl::ranges::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); + CHECK(result_it); + + bool result_r = etl::ranges::includes(vec1, vec2); + CHECK(result_r); + } + + //************************************************************************* + TEST(ranges_includes_empty_first_range) + { + std::vector vec1{}; + std::vector vec2{1, 2, 3}; + + bool result_it = etl::ranges::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); + CHECK(!result_it); + + bool result_r = etl::ranges::includes(vec1, vec2); + CHECK(!result_r); + } + + //************************************************************************* + TEST(ranges_includes_both_empty) + { + std::vector vec1{}; + std::vector vec2{}; + + bool result_it = etl::ranges::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); + CHECK(result_it); + + bool result_r = etl::ranges::includes(vec1, vec2); + CHECK(result_r); + } + + //************************************************************************* + TEST(ranges_includes_identical_ranges) + { + std::vector vec1{1, 2, 3, 4, 5}; + std::vector vec2{1, 2, 3, 4, 5}; + + bool result_it = etl::ranges::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); + CHECK(result_it); + + bool result_r = etl::ranges::includes(vec1, vec2); + CHECK(result_r); + } + + //************************************************************************* + TEST(ranges_includes_with_duplicates) + { + std::vector vec1{1, 2, 2, 3, 3, 3, 4, 5}; + std::vector vec2{2, 3, 3}; + + bool result_it = etl::ranges::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); + CHECK(result_it); + + bool result_r = etl::ranges::includes(vec1, vec2); + CHECK(result_r); + } + + //************************************************************************* + TEST(ranges_includes_with_duplicates_not_enough) + { + std::vector vec1{1, 2, 3, 4, 5}; + std::vector vec2{2, 3, 3}; + + bool result_it = etl::ranges::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); + CHECK(!result_it); + + bool result_r = etl::ranges::includes(vec1, vec2); + CHECK(!result_r); + } + + //************************************************************************* + TEST(ranges_includes_with_custom_comparator) + { + std::vector vec1{10, 8, 6, 4, 2}; + std::vector vec2{8, 4, 2}; + + bool result_it = etl::ranges::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), etl::greater{}); + CHECK(result_it); + + bool result_r = etl::ranges::includes(vec1, vec2, etl::greater{}); + CHECK(result_r); + } + + //************************************************************************* + TEST(ranges_includes_with_projection) + { + struct Item { int key; int value; }; + + std::vector vec1{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}}; + std::vector vec2{{2, 99}, {4, 99}}; + + bool result_it = etl::ranges::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), + etl::ranges::less{}, + [](const Item& item) { return item.key; }, + [](const Item& item) { return item.key; }); + CHECK(result_it); + + bool result_r = etl::ranges::includes(vec1, vec2, + etl::ranges::less{}, + [](const Item& item) { return item.key; }, + [](const Item& item) { return item.key; }); + CHECK(result_r); + } + + //************************************************************************* + TEST(ranges_includes_single_element_included) + { + std::vector vec1{1, 2, 3, 4, 5}; + std::vector vec2{3}; + + bool result = etl::ranges::includes(vec1, vec2); + CHECK(result); + } + + //************************************************************************* + TEST(ranges_includes_single_element_not_included) + { + std::vector vec1{1, 2, 3, 4, 5}; + std::vector vec2{6}; + + bool result = etl::ranges::includes(vec1, vec2); + CHECK(!result); + } + + //************************************************************************* + TEST(ranges_includes_matches_std) + { + std::vector vec1{1, 2, 2, 3, 3, 3, 5, 5, 7, 9, 11, 13, 15}; + std::vector> test_cases = { + {1, 3, 5}, + {2, 2, 3}, + {1, 2, 3, 4}, + {1, 15}, + {}, + {1, 2, 2, 3, 3, 3, 5, 5, 7, 9, 11, 13, 15}, + {3, 3, 3, 3}, + {0}, + {16}, + }; + + for (const auto& vec2 : test_cases) + { + bool std_result = std::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); + bool etl_result = etl::ranges::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); + CHECK_EQUAL(std_result, etl_result); + } + } + + //************************************************************************* + TEST(ranges_set_union_iterator_basic) + { + std::vector vec1{1, 2, 3, 5, 7}; + std::vector vec2{2, 4, 6, 7, 8}; + std::vector result(10); + + auto [in1, in2, out] = etl::ranges::set_union(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 2, 3, 4, 5, 6, 7, 8}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + CHECK(in1 == vec1.end()); + CHECK(in2 == vec2.end()); + } + + //************************************************************************* + TEST(ranges_set_union_range_basic) + { + std::vector vec1{1, 2, 3, 5, 7}; + std::vector vec2{2, 4, 6, 7, 8}; + std::vector result(10); + + auto [in1, in2, out] = etl::ranges::set_union(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 2, 3, 4, 5, 6, 7, 8}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_union_empty_first) + { + std::vector vec1{}; + std::vector vec2{1, 2, 3}; + std::vector result(3); + + auto [in1, in2, out] = etl::ranges::set_union(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 2, 3}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_union_empty_second) + { + std::vector vec1{1, 2, 3}; + std::vector vec2{}; + std::vector result(3); + + auto [in1, in2, out] = etl::ranges::set_union(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 2, 3}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_union_both_empty) + { + std::vector vec1{}; + std::vector vec2{}; + std::vector result{}; + + auto [in1, in2, out] = etl::ranges::set_union(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + + CHECK(out == result.begin()); + } + + //************************************************************************* + TEST(ranges_set_union_identical_ranges) + { + std::vector vec1{1, 2, 3, 4, 5}; + std::vector vec2{1, 2, 3, 4, 5}; + std::vector result(5); + + auto [in1, in2, out] = etl::ranges::set_union(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 2, 3, 4, 5}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_union_disjoint_ranges) + { + std::vector vec1{1, 3, 5}; + std::vector vec2{2, 4, 6}; + std::vector result(6); + + auto [in1, in2, out] = etl::ranges::set_union(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 2, 3, 4, 5, 6}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_union_with_duplicates) + { + std::vector vec1{1, 2, 2, 3}; + std::vector vec2{2, 2, 2, 4}; + std::vector result(7); + + auto [in1, in2, out] = etl::ranges::set_union(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector std_result(7); + auto std_out = std::set_union(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), std_result.begin()); + std_result.erase(std_out, std_result.end()); + + CHECK_EQUAL(std_result.size(), result.size()); + for (size_t i = 0; i < std_result.size(); ++i) + { + CHECK_EQUAL(std_result[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_union_with_custom_comparator) + { + std::vector vec1{7, 5, 3, 1}; + std::vector vec2{6, 4, 3, 2}; + std::vector result(8); + + auto [in1, in2, out] = etl::ranges::set_union(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), + result.begin(), etl::greater{}); + result.erase(out, result.end()); + + std::vector expected{7, 6, 5, 4, 3, 2, 1}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_union_with_projection) + { + struct Item { int key; int value; }; + + std::vector vec1{{1, 10}, {3, 30}, {5, 50}}; + std::vector vec2{{2, 20}, {3, 99}, {4, 40}}; + std::vector result(5); + + auto [in1, in2, out] = etl::ranges::set_union(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), + result.begin(), etl::ranges::less{}, + [](const Item& item) { return item.key; }, + [](const Item& item) { return item.key; }); + result.erase(out, result.end()); + + // Should contain keys: 1, 2, 3, 4, 5 + CHECK_EQUAL(5u, result.size()); + CHECK_EQUAL(1, result[0].key); + CHECK_EQUAL(2, result[1].key); + CHECK_EQUAL(3, result[2].key); + CHECK_EQUAL(30, result[2].value); // From first range when equal + CHECK_EQUAL(4, result[3].key); + CHECK_EQUAL(5, result[4].key); + } + + //************************************************************************* + TEST(ranges_set_union_matches_std) + { + std::vector vec1{1, 2, 2, 3, 3, 3, 5, 5, 7, 9}; + std::vector> test_cases = { + {1, 3, 5}, + {2, 2, 3}, + {1, 2, 3, 4}, + {1, 9}, + {}, + {1, 2, 2, 3, 3, 3, 5, 5, 7, 9}, + {3, 3, 3, 3}, + {0}, + {10}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }; + + for (const auto& vec2 : test_cases) + { + std::vector std_result(vec1.size() + vec2.size()); + auto std_out = std::set_union(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), std_result.begin()); + std_result.erase(std_out, std_result.end()); + + std::vector etl_result(vec1.size() + vec2.size()); + auto [in1, in2, out] = etl::ranges::set_union(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), etl_result.begin()); + etl_result.erase(out, etl_result.end()); + + CHECK_EQUAL(std_result.size(), etl_result.size()); + for (size_t i = 0; i < std_result.size(); ++i) + { + CHECK_EQUAL(std_result[i], etl_result[i]); + } + } + } + + //************************************************************************* + TEST(ranges_set_intersection_iterator_basic) + { + std::vector vec1{1, 2, 3, 5, 7}; + std::vector vec2{2, 4, 5, 7, 8}; + std::vector result(5); + + auto [in1, in2, out] = etl::ranges::set_intersection(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + std::vector expected{2, 5, 7}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + CHECK(in1 == vec1.end()); + } + + //************************************************************************* + TEST(ranges_set_intersection_range_basic) + { + std::vector vec1{1, 2, 3, 5, 7}; + std::vector vec2{2, 4, 5, 7, 8}; + std::vector result(5); + + auto [in1, in2, out] = etl::ranges::set_intersection(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector expected{2, 5, 7}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_intersection_empty_first) + { + std::vector vec1{}; + std::vector vec2{1, 2, 3}; + std::vector result(3); + + auto [in1, in2, out] = etl::ranges::set_intersection(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + CHECK_EQUAL(0u, result.size()); + } + + //************************************************************************* + TEST(ranges_set_intersection_empty_second) + { + std::vector vec1{1, 2, 3}; + std::vector vec2{}; + std::vector result(3); + + auto [in1, in2, out] = etl::ranges::set_intersection(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + CHECK_EQUAL(0u, result.size()); + } + + //************************************************************************* + TEST(ranges_set_intersection_both_empty) + { + std::vector vec1{}; + std::vector vec2{}; + std::vector result{}; + + auto [in1, in2, out] = etl::ranges::set_intersection(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + + CHECK(out == result.begin()); + } + + //************************************************************************* + TEST(ranges_set_intersection_identical_ranges) + { + std::vector vec1{1, 2, 3, 4, 5}; + std::vector vec2{1, 2, 3, 4, 5}; + std::vector result(5); + + auto [in1, in2, out] = etl::ranges::set_intersection(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 2, 3, 4, 5}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_intersection_disjoint_ranges) + { + std::vector vec1{1, 3, 5}; + std::vector vec2{2, 4, 6}; + std::vector result(3); + + auto [in1, in2, out] = etl::ranges::set_intersection(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + CHECK_EQUAL(0u, result.size()); + } + + //************************************************************************* + TEST(ranges_set_intersection_with_duplicates) + { + std::vector vec1{1, 2, 2, 3}; + std::vector vec2{2, 2, 2, 4}; + std::vector result(4); + + auto [in1, in2, out] = etl::ranges::set_intersection(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector std_result(4); + auto std_out = std::set_intersection(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), std_result.begin()); + std_result.erase(std_out, std_result.end()); + + CHECK_EQUAL(std_result.size(), result.size()); + for (size_t i = 0; i < std_result.size(); ++i) + { + CHECK_EQUAL(std_result[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_intersection_with_custom_comparator) + { + std::vector vec1{7, 5, 3, 1}; + std::vector vec2{6, 5, 3, 2}; + std::vector result(4); + + auto [in1, in2, out] = etl::ranges::set_intersection(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), + result.begin(), etl::greater{}); + result.erase(out, result.end()); + + std::vector expected{5, 3}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_intersection_with_projection) + { + struct Item { int key; int value; }; + + std::vector vec1{{1, 10}, {3, 30}, {5, 50}}; + std::vector vec2{{2, 20}, {3, 99}, {4, 40}}; + std::vector result(3); + + auto [in1, in2, out] = etl::ranges::set_intersection(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), + result.begin(), etl::ranges::less{}, + [](const Item& item) { return item.key; }, + [](const Item& item) { return item.key; }); + result.erase(out, result.end()); + + // Should contain only key 3 (from first range) + CHECK_EQUAL(1u, result.size()); + CHECK_EQUAL(3, result[0].key); + CHECK_EQUAL(30, result[0].value); // From first range when equal + } + + //************************************************************************* + TEST(ranges_set_intersection_matches_std) + { + std::vector vec1{1, 2, 2, 3, 3, 3, 5, 5, 7, 9}; + std::vector> test_cases = { + {1, 3, 5}, + {2, 2, 3}, + {1, 2, 3, 4}, + {1, 9}, + {}, + {1, 2, 2, 3, 3, 3, 5, 5, 7, 9}, + {3, 3, 3, 3}, + {0}, + {10}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }; + + for (const auto& vec2 : test_cases) + { + std::vector std_result(vec1.size() + vec2.size()); + auto std_out = std::set_intersection(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), std_result.begin()); + std_result.erase(std_out, std_result.end()); + + std::vector etl_result(vec1.size() + vec2.size()); + auto [in1, in2, out] = etl::ranges::set_intersection(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), etl_result.begin()); + etl_result.erase(out, etl_result.end()); + + CHECK_EQUAL(std_result.size(), etl_result.size()); + for (size_t i = 0; i < std_result.size(); ++i) + { + CHECK_EQUAL(std_result[i], etl_result[i]); + } + } + } + + //************************************************************************* + TEST(ranges_set_difference_iterator_basic) + { + std::vector vec1{1, 2, 3, 5, 7}; + std::vector vec2{2, 4, 5, 7, 8}; + std::vector result(5); + + auto [in, out] = etl::ranges::set_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 3}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + CHECK(in == vec1.end()); + } + + //************************************************************************* + TEST(ranges_set_difference_range_basic) + { + std::vector vec1{1, 2, 3, 5, 7}; + std::vector vec2{2, 4, 5, 7, 8}; + std::vector result(5); + + auto [in, out] = etl::ranges::set_difference(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 3}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_difference_empty_first) + { + std::vector vec1{}; + std::vector vec2{1, 2, 3}; + std::vector result(3); + + auto [in, out] = etl::ranges::set_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + CHECK_EQUAL(0u, result.size()); + } + + //************************************************************************* + TEST(ranges_set_difference_empty_second) + { + std::vector vec1{1, 2, 3}; + std::vector vec2{}; + std::vector result(3); + + auto [in, out] = etl::ranges::set_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 2, 3}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_difference_both_empty) + { + std::vector vec1{}; + std::vector vec2{}; + std::vector result{}; + + auto [in, out] = etl::ranges::set_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + + CHECK(out == result.begin()); + } + + //************************************************************************* + TEST(ranges_set_difference_identical_ranges) + { + std::vector vec1{1, 2, 3, 4, 5}; + std::vector vec2{1, 2, 3, 4, 5}; + std::vector result(5); + + auto [in, out] = etl::ranges::set_difference(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + CHECK_EQUAL(0u, result.size()); + } + + //************************************************************************* + TEST(ranges_set_difference_disjoint_ranges) + { + std::vector vec1{1, 3, 5}; + std::vector vec2{2, 4, 6}; + std::vector result(3); + + auto [in, out] = etl::ranges::set_difference(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 3, 5}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_difference_with_duplicates) + { + std::vector vec1{1, 2, 2, 3}; + std::vector vec2{2, 2, 2, 4}; + std::vector result(4); + + auto [in, out] = etl::ranges::set_difference(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector std_result(4); + auto std_out = std::set_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), std_result.begin()); + std_result.erase(std_out, std_result.end()); + + CHECK_EQUAL(std_result.size(), result.size()); + for (size_t i = 0; i < std_result.size(); ++i) + { + CHECK_EQUAL(std_result[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_difference_with_custom_comparator) + { + std::vector vec1{7, 5, 3, 1}; + std::vector vec2{6, 5, 3, 2}; + std::vector result(4); + + auto [in, out] = etl::ranges::set_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), + result.begin(), etl::greater{}); + result.erase(out, result.end()); + + std::vector expected{7, 1}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_difference_with_projection) + { + struct Item { int key; int value; }; + + std::vector vec1{{1, 10}, {3, 30}, {5, 50}}; + std::vector vec2{{2, 20}, {3, 99}, {4, 40}}; + std::vector result(3); + + auto [in, out] = etl::ranges::set_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), + result.begin(), etl::ranges::less{}, + [](const Item& item) { return item.key; }, + [](const Item& item) { return item.key; }); + result.erase(out, result.end()); + + // Should contain keys: 1, 5 (elements in vec1 not in vec2) + CHECK_EQUAL(2u, result.size()); + CHECK_EQUAL(1, result[0].key); + CHECK_EQUAL(10, result[0].value); + CHECK_EQUAL(5, result[1].key); + CHECK_EQUAL(50, result[1].value); + } + + //************************************************************************* + TEST(ranges_set_difference_matches_std) + { + std::vector vec1{1, 2, 2, 3, 3, 3, 5, 5, 7, 9}; + std::vector> test_cases = { + {1, 3, 5}, + {2, 2, 3}, + {1, 2, 3, 4}, + {1, 9}, + {}, + {1, 2, 2, 3, 3, 3, 5, 5, 7, 9}, + {3, 3, 3, 3}, + {0}, + {10}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }; + + for (const auto& vec2 : test_cases) + { + std::vector std_result(vec1.size() + vec2.size()); + auto std_out = std::set_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), std_result.begin()); + std_result.erase(std_out, std_result.end()); + + std::vector etl_result(vec1.size() + vec2.size()); + auto [in, out] = etl::ranges::set_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), etl_result.begin()); + etl_result.erase(out, etl_result.end()); + + CHECK_EQUAL(std_result.size(), etl_result.size()); + for (size_t i = 0; i < std_result.size(); ++i) + { + CHECK_EQUAL(std_result[i], etl_result[i]); + } + } + } + + //************************************************************************* + TEST(ranges_set_symmetric_difference_iterator_basic) + { + std::vector vec1{1, 2, 3, 5, 7}; + std::vector vec2{2, 4, 5, 7, 8}; + std::vector result(10); + + auto [in1, in2, out] = etl::ranges::set_symmetric_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 3, 4, 8}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + CHECK(in1 == vec1.end()); + CHECK(in2 == vec2.end()); + } + + //************************************************************************* + TEST(ranges_set_symmetric_difference_range_basic) + { + std::vector vec1{1, 2, 3, 5, 7}; + std::vector vec2{2, 4, 5, 7, 8}; + std::vector result(10); + + auto [in1, in2, out] = etl::ranges::set_symmetric_difference(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 3, 4, 8}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_symmetric_difference_empty_first) + { + std::vector vec1{}; + std::vector vec2{1, 2, 3}; + std::vector result(3); + + auto [in1, in2, out] = etl::ranges::set_symmetric_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 2, 3}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_symmetric_difference_empty_second) + { + std::vector vec1{1, 2, 3}; + std::vector vec2{}; + std::vector result(3); + + auto [in1, in2, out] = etl::ranges::set_symmetric_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 2, 3}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_symmetric_difference_both_empty) + { + std::vector vec1{}; + std::vector vec2{}; + std::vector result{}; + + auto [in1, in2, out] = etl::ranges::set_symmetric_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + + CHECK(out == result.begin()); + } + + //************************************************************************* + TEST(ranges_set_symmetric_difference_identical_ranges) + { + std::vector vec1{1, 2, 3, 4, 5}; + std::vector vec2{1, 2, 3, 4, 5}; + std::vector result(5); + + auto [in1, in2, out] = etl::ranges::set_symmetric_difference(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + CHECK_EQUAL(0u, result.size()); + } + + //************************************************************************* + TEST(ranges_set_symmetric_difference_disjoint_ranges) + { + std::vector vec1{1, 3, 5}; + std::vector vec2{2, 4, 6}; + std::vector result(6); + + auto [in1, in2, out] = etl::ranges::set_symmetric_difference(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector expected{1, 2, 3, 4, 5, 6}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_symmetric_difference_with_duplicates) + { + std::vector vec1{1, 2, 2, 3}; + std::vector vec2{2, 2, 2, 4}; + std::vector result(8); + + auto [in1, in2, out] = etl::ranges::set_symmetric_difference(vec1, vec2, result.begin()); + result.erase(out, result.end()); + + std::vector std_result(8); + auto std_out = std::set_symmetric_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), std_result.begin()); + std_result.erase(std_out, std_result.end()); + + CHECK_EQUAL(std_result.size(), result.size()); + for (size_t i = 0; i < std_result.size(); ++i) + { + CHECK_EQUAL(std_result[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_symmetric_difference_with_custom_comparator) + { + std::vector vec1{7, 5, 3, 1}; + std::vector vec2{6, 5, 3, 2}; + std::vector result(8); + + auto [in1, in2, out] = etl::ranges::set_symmetric_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), + result.begin(), etl::greater{}); + result.erase(out, result.end()); + + std::vector expected{7, 6, 2, 1}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_set_symmetric_difference_with_projection) + { + struct Item { int key; int value; }; + + std::vector vec1{{1, 10}, {3, 30}, {5, 50}}; + std::vector vec2{{2, 20}, {3, 99}, {4, 40}}; + std::vector result(6); + + auto [in1, in2, out] = etl::ranges::set_symmetric_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), + result.begin(), etl::ranges::less{}, + [](const Item& item) { return item.key; }, + [](const Item& item) { return item.key; }); + result.erase(out, result.end()); + + // Should contain keys: 1, 2, 4, 5 (elements in either but not both) + CHECK_EQUAL(4u, result.size()); + CHECK_EQUAL(1, result[0].key); + CHECK_EQUAL(10, result[0].value); + CHECK_EQUAL(2, result[1].key); + CHECK_EQUAL(20, result[1].value); + CHECK_EQUAL(4, result[2].key); + CHECK_EQUAL(40, result[2].value); + CHECK_EQUAL(5, result[3].key); + CHECK_EQUAL(50, result[3].value); + } + + //************************************************************************* + TEST(ranges_set_symmetric_difference_matches_std) + { + std::vector vec1{1, 2, 2, 3, 3, 3, 5, 5, 7, 9}; + std::vector> test_cases = { + {1, 3, 5}, + {2, 2, 3}, + {1, 2, 3, 4}, + {1, 9}, + {}, + {1, 2, 2, 3, 3, 3, 5, 5, 7, 9}, + {3, 3, 3, 3}, + {0}, + {10}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }; + + for (const auto& vec2 : test_cases) + { + std::vector std_result(vec1.size() + vec2.size()); + auto std_out = std::set_symmetric_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), std_result.begin()); + std_result.erase(std_out, std_result.end()); + + std::vector etl_result(vec1.size() + vec2.size()); + auto [in1, in2, out] = etl::ranges::set_symmetric_difference(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), etl_result.begin()); + etl_result.erase(out, etl_result.end()); + + CHECK_EQUAL(std_result.size(), etl_result.size()); + for (size_t i = 0; i < std_result.size(); ++i) + { + CHECK_EQUAL(std_result[i], etl_result[i]); + } + } + } + + //************************************************************************* + TEST(ranges_merge_iterator_basic) + { + std::vector vec1{1, 3, 5, 7}; + std::vector vec2{2, 4, 6, 8}; + std::vector result(8); + + auto [in1, in2, out] = etl::ranges::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + + std::vector expected{1, 2, 3, 4, 5, 6, 7, 8}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + CHECK(in1 == vec1.end()); + CHECK(in2 == vec2.end()); + CHECK(out == result.end()); + } + + //************************************************************************* + TEST(ranges_merge_range_basic) + { + std::vector vec1{1, 3, 5, 7}; + std::vector vec2{2, 4, 6, 8}; + std::vector result(8); + + auto [in1, in2, out] = etl::ranges::merge(vec1, vec2, result.begin()); + (void)in1; (void)in2; + + std::vector expected{1, 2, 3, 4, 5, 6, 7, 8}; + CHECK_EQUAL(expected.size(), size_t(out - result.begin())); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_merge_empty_first) + { + std::vector vec1{}; + std::vector vec2{1, 2, 3}; + std::vector result(3); + + auto [in1, in2, out] = etl::ranges::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + + std::vector expected{1, 2, 3}; + CHECK_EQUAL(expected.size(), size_t(out - result.begin())); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_merge_empty_second) + { + std::vector vec1{1, 2, 3}; + std::vector vec2{}; + std::vector result(3); + + auto [in1, in2, out] = etl::ranges::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + + std::vector expected{1, 2, 3}; + CHECK_EQUAL(expected.size(), size_t(out - result.begin())); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_merge_both_empty) + { + std::vector vec1{}; + std::vector vec2{}; + std::vector result{}; + + auto [in1, in2, out] = etl::ranges::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), result.begin()); + + CHECK(out == result.begin()); + } + + //************************************************************************* + TEST(ranges_merge_identical_ranges) + { + std::vector vec1{1, 2, 3}; + std::vector vec2{1, 2, 3}; + std::vector result(6); + + auto [in1, in2, out] = etl::ranges::merge(vec1, vec2, result.begin()); + (void)in1; (void)in2; + result.erase(out, result.end()); + + std::vector expected{1, 1, 2, 2, 3, 3}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_merge_with_duplicates) + { + std::vector vec1{1, 2, 2, 3}; + std::vector vec2{2, 2, 2, 4}; + std::vector result(8); + + auto [in1, in2, out] = etl::ranges::merge(vec1, vec2, result.begin()); + (void)in1; (void)in2; + result.erase(out, result.end()); + + std::vector std_result(8); + auto std_out = std::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), std_result.begin()); + std_result.erase(std_out, std_result.end()); + + CHECK_EQUAL(std_result.size(), result.size()); + for (size_t i = 0; i < std_result.size(); ++i) + { + CHECK_EQUAL(std_result[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_merge_with_custom_comparator) + { + std::vector vec1{7, 5, 3, 1}; + std::vector vec2{6, 4, 2}; + std::vector result(7); + + auto [in1, in2, out] = etl::ranges::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), + result.begin(), etl::greater{}); + result.erase(out, result.end()); + + std::vector expected{7, 6, 5, 4, 3, 2, 1}; + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(ranges_merge_with_projection) + { + struct Item { int key; int value; }; + + std::vector vec1{{1, 10}, {3, 30}, {5, 50}}; + std::vector vec2{{2, 20}, {3, 99}, {4, 40}}; + std::vector result(6); + + auto [in1, in2, out] = etl::ranges::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), + result.begin(), etl::ranges::less{}, + [](const Item& item) { return item.key; }, + [](const Item& item) { return item.key; }); + result.erase(out, result.end()); + + // Merge keeps all elements: 1, 2, 3(from vec1), 3(from vec2), 4, 5 + CHECK_EQUAL(6u, result.size()); + CHECK_EQUAL(1, result[0].key); + CHECK_EQUAL(10, result[0].value); + CHECK_EQUAL(2, result[1].key); + CHECK_EQUAL(20, result[1].value); + CHECK_EQUAL(3, result[2].key); + CHECK_EQUAL(30, result[2].value); // From first range (stable) + CHECK_EQUAL(3, result[3].key); + CHECK_EQUAL(99, result[3].value); // From second range + CHECK_EQUAL(4, result[4].key); + CHECK_EQUAL(40, result[4].value); + CHECK_EQUAL(5, result[5].key); + CHECK_EQUAL(50, result[5].value); + } + + //************************************************************************* + TEST(ranges_merge_matches_std) + { + std::vector vec1{1, 2, 2, 3, 3, 3, 5, 5, 7, 9}; + std::vector> test_cases = { + {1, 3, 5}, + {2, 2, 3}, + {1, 2, 3, 4}, + {1, 9}, + {}, + {1, 2, 2, 3, 3, 3, 5, 5, 7, 9}, + {3, 3, 3, 3}, + {0}, + {10}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }; + + for (const auto& vec2 : test_cases) + { + std::vector std_result(vec1.size() + vec2.size()); + auto std_out = std::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), std_result.begin()); + std_result.erase(std_out, std_result.end()); + + std::vector etl_result(vec1.size() + vec2.size()); + auto [in1, in2, out] = etl::ranges::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), etl_result.begin()); + etl_result.erase(out, etl_result.end()); + + CHECK_EQUAL(std_result.size(), etl_result.size()); + for (size_t i = 0; i < std_result.size(); ++i) + { + CHECK_EQUAL(std_result[i], etl_result[i]); + } + } + } + + //************************************************************************* + TEST(ranges_inplace_merge_iterator_basic) + { + std::vector vec{1, 3, 5, 7, 2, 4, 6, 8}; + auto middle = vec.begin() + 4; + + auto result = etl::ranges::inplace_merge(vec.begin(), middle, vec.end()); + + std::vector expected{1, 2, 3, 4, 5, 6, 7, 8}; + CHECK_EQUAL(expected.size(), vec.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], vec[i]); + } + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_inplace_merge_range_basic) + { + std::vector vec{1, 3, 5, 7, 2, 4, 6, 8}; + auto middle = vec.begin() + 4; + + auto result = etl::ranges::inplace_merge(vec, middle); + + std::vector expected{1, 2, 3, 4, 5, 6, 7, 8}; + CHECK_EQUAL(expected.size(), vec.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], vec[i]); + } + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_inplace_merge_empty_first_half) + { + std::vector vec{1, 2, 3}; + auto middle = vec.begin(); // empty first half + + auto result = etl::ranges::inplace_merge(vec.begin(), middle, vec.end()); + + std::vector expected{1, 2, 3}; + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], vec[i]); + } + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_inplace_merge_empty_second_half) + { + std::vector vec{1, 2, 3}; + auto middle = vec.end(); // empty second half + + auto result = etl::ranges::inplace_merge(vec.begin(), middle, vec.end()); + + std::vector expected{1, 2, 3}; + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], vec[i]); + } + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_inplace_merge_single_elements) + { + std::vector vec{5, 2}; + auto middle = vec.begin() + 1; + + etl::ranges::inplace_merge(vec.begin(), middle, vec.end()); + + CHECK_EQUAL(2, vec[0]); + CHECK_EQUAL(5, vec[1]); + } + + //************************************************************************* + TEST(ranges_inplace_merge_already_sorted) + { + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8}; + auto middle = vec.begin() + 4; + + etl::ranges::inplace_merge(vec.begin(), middle, vec.end()); + + std::vector expected{1, 2, 3, 4, 5, 6, 7, 8}; + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], vec[i]); + } + } + + //************************************************************************* + TEST(ranges_inplace_merge_with_duplicates) + { + std::vector vec{1, 2, 2, 3, 2, 2, 2, 4}; + auto middle = vec.begin() + 4; + + etl::ranges::inplace_merge(vec.begin(), middle, vec.end()); + + std::vector expected{1, 2, 2, 2, 2, 2, 3, 4}; + CHECK_EQUAL(expected.size(), vec.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], vec[i]); + } + } + + //************************************************************************* + TEST(ranges_inplace_merge_with_custom_comparator) + { + std::vector vec{7, 5, 3, 1, 8, 6, 4, 2}; + auto middle = vec.begin() + 4; + + etl::ranges::inplace_merge(vec.begin(), middle, vec.end(), etl::greater{}); + + std::vector expected{8, 7, 6, 5, 4, 3, 2, 1}; + CHECK_EQUAL(expected.size(), vec.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], vec[i]); + } + } + + //************************************************************************* + TEST(ranges_inplace_merge_with_projection) + { + struct Item { int key; int value; }; + + std::vector vec{{1, 10}, {3, 30}, {5, 50}, {2, 20}, {4, 40}}; + auto middle = vec.begin() + 3; + + etl::ranges::inplace_merge(vec.begin(), middle, vec.end(), etl::ranges::less{}, + [](const Item& item) { return item.key; }); + + CHECK_EQUAL(1, vec[0].key); + CHECK_EQUAL(10, vec[0].value); + CHECK_EQUAL(2, vec[1].key); + CHECK_EQUAL(20, vec[1].value); + CHECK_EQUAL(3, vec[2].key); + CHECK_EQUAL(30, vec[2].value); + CHECK_EQUAL(4, vec[3].key); + CHECK_EQUAL(40, vec[3].value); + CHECK_EQUAL(5, vec[4].key); + CHECK_EQUAL(50, vec[4].value); + } + + //************************************************************************* + TEST(ranges_inplace_merge_matches_std) + { + std::vector base1{1, 2, 2, 3, 3, 3, 5, 5, 7, 9}; + std::vector> test_cases = { + {1, 3, 5}, + {2, 2, 3}, + {1, 2, 3, 4}, + {1, 9}, + {}, + {1, 2, 2, 3, 3, 3, 5, 5, 7, 9}, + {3, 3, 3, 3}, + {0}, + {10}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + }; + + for (const auto& second : test_cases) + { + // Build a combined vector with two sorted halves + std::vector std_vec; + std_vec.insert(std_vec.end(), base1.begin(), base1.end()); + std_vec.insert(std_vec.end(), second.begin(), second.end()); + + std::vector etl_vec = std_vec; + + auto std_middle = std_vec.begin() + static_cast(base1.size()); + auto etl_middle = etl_vec.begin() + static_cast(base1.size()); + + std::inplace_merge(std_vec.begin(), std_middle, std_vec.end()); + etl::ranges::inplace_merge(etl_vec.begin(), etl_middle, etl_vec.end()); + + CHECK_EQUAL(std_vec.size(), etl_vec.size()); + for (size_t i = 0; i < std_vec.size(); ++i) + { + CHECK_EQUAL(std_vec[i], etl_vec[i]); + } + } + } + + //************************************************************************* + TEST(ranges_inplace_merge_single_element_halves) + { + std::vector vec{3, 1}; + auto middle = vec.begin() + 1; + + etl::ranges::inplace_merge(vec.begin(), middle, vec.end()); + + CHECK_EQUAL(1, vec[0]); + CHECK_EQUAL(3, vec[1]); + } + + //************************************************************************* + TEST(ranges_inplace_merge_unequal_halves) + { + std::vector vec{1, 5, 9, 2, 3, 4, 6, 7, 8, 10}; + auto middle = vec.begin() + 3; + + etl::ranges::inplace_merge(vec.begin(), middle, vec.end()); + + std::vector expected{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + CHECK_EQUAL(expected.size(), vec.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], vec[i]); + } + } + + //************************************************************************* + TEST(ranges_make_heap_iterator) + { + std::vector vec{5, 3, 1, 4, 2}; + + auto result = etl::ranges::make_heap(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + CHECK(std::is_heap(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_make_heap_range) + { + std::vector vec{5, 3, 1, 4, 2}; + + auto result = etl::ranges::make_heap(vec); + + CHECK(result == vec.end()); + CHECK(std::is_heap(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_make_heap_with_comparator_iterator) + { + std::vector vec{5, 3, 1, 4, 2}; + + auto result = etl::ranges::make_heap(vec.begin(), vec.end(), etl::greater{}); + + CHECK(result == vec.end()); + CHECK(std::is_heap(vec.begin(), vec.end(), etl::greater{})); + } + + //************************************************************************* + TEST(ranges_make_heap_with_comparator_range) + { + std::vector vec{5, 3, 1, 4, 2}; + + auto result = etl::ranges::make_heap(vec, etl::greater{}); + + CHECK(result == vec.end()); + CHECK(std::is_heap(vec.begin(), vec.end(), etl::greater{})); + } + + //************************************************************************* + TEST(ranges_make_heap_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {1, 10}, {5, 50}, {2, 20}, {4, 40}}; + + etl::ranges::make_heap(vec.begin(), vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + + // Verify max-heap property on keys + for (size_t i = 0; i < vec.size(); ++i) + { + size_t left = 2 * i + 1; + size_t right = 2 * i + 2; + + if (left < vec.size()) + { + CHECK(vec[i].key >= vec[left].key); + } + if (right < vec.size()) + { + CHECK(vec[i].key >= vec[right].key); + } + } + } + + //************************************************************************* + TEST(ranges_make_heap_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {1, 10}, {5, 50}, {2, 20}, {4, 40}}; + + etl::ranges::make_heap(vec, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + // Verify max-heap property on keys + for (size_t i = 0; i < vec.size(); ++i) + { + size_t left = 2 * i + 1; + size_t right = 2 * i + 2; + + if (left < vec.size()) + { + CHECK(vec[i].key >= vec[left].key); + } + if (right < vec.size()) + { + CHECK(vec[i].key >= vec[right].key); + } + } + } + + //************************************************************************* + TEST(ranges_make_heap_empty) + { + std::vector vec{}; + + auto result = etl::ranges::make_heap(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_make_heap_single_element) + { + std::vector vec{42}; + + auto result = etl::ranges::make_heap(vec); + + CHECK(result == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_make_heap_already_heap) + { + std::vector vec{5, 4, 3, 2, 1}; + + etl::ranges::make_heap(vec); + + CHECK(std::is_heap(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_make_heap_duplicates) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}; + + etl::ranges::make_heap(vec); + + CHECK(std::is_heap(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_make_heap_matches_std) + { + std::vector data_std{9, 3, 7, 1, 5, 8, 2, 6, 4, 10}; + std::vector data_etl = data_std; + + std::make_heap(data_std.begin(), data_std.end()); + etl::ranges::make_heap(data_etl); + + CHECK(std::is_heap(data_etl.begin(), data_etl.end())); + } + + //************************************************************************* + TEST(ranges_push_heap_iterator) + { + std::vector vec{5, 3, 1}; + std::make_heap(vec.begin(), vec.end()); + vec.push_back(10); + + auto result = etl::ranges::push_heap(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + CHECK(std::is_heap(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_push_heap_range) + { + std::vector vec{5, 3, 1}; + std::make_heap(vec.begin(), vec.end()); + vec.push_back(4); + + auto result = etl::ranges::push_heap(vec); + + CHECK(result == vec.end()); + CHECK(std::is_heap(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_push_heap_with_comparator_iterator) + { + std::vector vec{1, 3, 5}; + std::make_heap(vec.begin(), vec.end(), etl::greater{}); + vec.push_back(0); + + auto result = etl::ranges::push_heap(vec.begin(), vec.end(), etl::greater{}); + + CHECK(result == vec.end()); + CHECK(std::is_heap(vec.begin(), vec.end(), etl::greater{})); + } + + //************************************************************************* + TEST(ranges_push_heap_with_comparator_range) + { + std::vector vec{1, 3, 5}; + std::make_heap(vec.begin(), vec.end(), etl::greater{}); + vec.push_back(2); + + auto result = etl::ranges::push_heap(vec, etl::greater{}); + + CHECK(result == vec.end()); + CHECK(std::is_heap(vec.begin(), vec.end(), etl::greater{})); + } + + //************************************************************************* + TEST(ranges_push_heap_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{5, 50}, {3, 30}, {1, 10}}; + // Make it a heap by key first + etl::ranges::make_heap(vec.begin(), vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + vec.push_back({10, 100}); + + etl::ranges::push_heap(vec.begin(), vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + + // Verify max-heap property on keys + for (size_t i = 0; i < vec.size(); ++i) + { + size_t left = 2 * i + 1; + size_t right = 2 * i + 2; + + if (left < vec.size()) + { + CHECK(vec[i].key >= vec[left].key); + } + if (right < vec.size()) + { + CHECK(vec[i].key >= vec[right].key); + } + } + } + + //************************************************************************* + TEST(ranges_push_heap_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{5, 50}, {3, 30}, {1, 10}}; + etl::ranges::make_heap(vec, etl::ranges::less{}, [](const Item& item) { return item.key; }); + vec.push_back({4, 40}); + + etl::ranges::push_heap(vec, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + // Verify max-heap property on keys + for (size_t i = 0; i < vec.size(); ++i) + { + size_t left = 2 * i + 1; + size_t right = 2 * i + 2; + + if (left < vec.size()) + { + CHECK(vec[i].key >= vec[left].key); + } + if (right < vec.size()) + { + CHECK(vec[i].key >= vec[right].key); + } + } + } + + //************************************************************************* + TEST(ranges_push_heap_empty) + { + std::vector vec{}; + + auto result = etl::ranges::push_heap(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_push_heap_single_element) + { + std::vector vec{42}; + + auto result = etl::ranges::push_heap(vec); + + CHECK(result == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_push_heap_multiple_pushes) + { + std::vector vec; + int values[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3}; + + for (int v : values) + { + vec.push_back(v); + etl::ranges::push_heap(vec); + } + + CHECK(std::is_heap(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_push_heap_matches_std) + { + std::vector data_std; + std::vector data_etl; + int values[] = {9, 3, 7, 1, 5, 8, 2, 6, 4, 10}; + + for (int v : values) + { + data_std.push_back(v); + std::push_heap(data_std.begin(), data_std.end()); + + data_etl.push_back(v); + etl::ranges::push_heap(data_etl); + } + + CHECK(std::is_heap(data_etl.begin(), data_etl.end())); + + for (size_t i = 0; i < data_std.size(); ++i) + { + CHECK_EQUAL(data_std[i], data_etl[i]); + } + } + + //************************************************************************* + TEST(ranges_pop_heap_iterator) + { + std::vector vec{9, 5, 7, 1, 3}; + std::make_heap(vec.begin(), vec.end()); + + auto result = etl::ranges::pop_heap(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + CHECK_EQUAL(9, vec.back()); + CHECK(std::is_heap(vec.begin(), vec.end() - 1)); + } + + //************************************************************************* + TEST(ranges_pop_heap_range) + { + std::vector vec{9, 5, 7, 1, 3}; + std::make_heap(vec.begin(), vec.end()); + + auto result = etl::ranges::pop_heap(vec); + + CHECK(result == vec.end()); + CHECK_EQUAL(9, vec.back()); + CHECK(std::is_heap(vec.begin(), vec.end() - 1)); + } + + //************************************************************************* + TEST(ranges_pop_heap_with_comparator_iterator) + { + std::vector vec{1, 3, 5, 7, 9}; + std::make_heap(vec.begin(), vec.end(), etl::greater{}); + + auto result = etl::ranges::pop_heap(vec.begin(), vec.end(), etl::greater{}); + + CHECK(result == vec.end()); + CHECK_EQUAL(1, vec.back()); + CHECK(std::is_heap(vec.begin(), vec.end() - 1, etl::greater{})); + } + + //************************************************************************* + TEST(ranges_pop_heap_with_comparator_range) + { + std::vector vec{1, 3, 5, 7, 9}; + std::make_heap(vec.begin(), vec.end(), etl::greater{}); + + auto result = etl::ranges::pop_heap(vec, etl::greater{}); + + CHECK(result == vec.end()); + CHECK_EQUAL(1, vec.back()); + CHECK(std::is_heap(vec.begin(), vec.end() - 1, etl::greater{})); + } + + //************************************************************************* + TEST(ranges_pop_heap_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{9, 90}, {5, 50}, {7, 70}, {1, 10}, {3, 30}}; + etl::ranges::make_heap(vec.begin(), vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + + etl::ranges::pop_heap(vec.begin(), vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(9, vec.back().key); + + // Verify max-heap property on keys for remaining elements + for (size_t i = 0; i < vec.size() - 1; ++i) + { + size_t left = 2 * i + 1; + size_t right = 2 * i + 2; + + if (left < vec.size() - 1) + { + CHECK(vec[i].key >= vec[left].key); + } + if (right < vec.size() - 1) + { + CHECK(vec[i].key >= vec[right].key); + } + } + } + + //************************************************************************* + TEST(ranges_pop_heap_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{9, 90}, {5, 50}, {7, 70}, {1, 10}, {3, 30}}; + etl::ranges::make_heap(vec, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + etl::ranges::pop_heap(vec, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK_EQUAL(9, vec.back().key); + + // Verify max-heap property on keys for remaining elements + for (size_t i = 0; i < vec.size() - 1; ++i) + { + size_t left = 2 * i + 1; + size_t right = 2 * i + 2; + + if (left < vec.size() - 1) + { + CHECK(vec[i].key >= vec[left].key); + } + if (right < vec.size() - 1) + { + CHECK(vec[i].key >= vec[right].key); + } + } + } + + //************************************************************************* + TEST(ranges_pop_heap_empty) + { + std::vector vec{}; + + auto result = etl::ranges::pop_heap(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_pop_heap_single_element) + { + std::vector vec{42}; + + auto result = etl::ranges::pop_heap(vec); + + CHECK(result == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_pop_heap_multiple_pops) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3}; + std::make_heap(vec.begin(), vec.end()); + + std::vector sorted; + auto end = vec.end(); + + while (end != vec.begin()) + { + etl::ranges::pop_heap(vec.begin(), end); + --end; + sorted.push_back(*end); + } + + // Should produce descending order + for (size_t i = 1; i < sorted.size(); ++i) + { + CHECK(sorted[i - 1] >= sorted[i]); + } + } + + //************************************************************************* + TEST(ranges_pop_heap_matches_std) + { + std::vector data_std{9, 3, 7, 1, 5, 8, 2, 6, 4, 10}; + std::vector data_etl = data_std; + + std::make_heap(data_std.begin(), data_std.end()); + std::make_heap(data_etl.begin(), data_etl.end()); + + std::pop_heap(data_std.begin(), data_std.end()); + etl::ranges::pop_heap(data_etl); + + for (size_t i = 0; i < data_std.size(); ++i) + { + CHECK_EQUAL(data_std[i], data_etl[i]); + } + } + + //************************************************************************* + TEST(ranges_sort_heap_iterator) + { + std::vector vec{9, 5, 7, 1, 3}; + std::make_heap(vec.begin(), vec.end()); + + auto result = etl::ranges::sort_heap(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + CHECK(std::is_sorted(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_sort_heap_range) + { + std::vector vec{9, 5, 7, 1, 3}; + std::make_heap(vec.begin(), vec.end()); + + auto result = etl::ranges::sort_heap(vec); + + CHECK(result == vec.end()); + CHECK(std::is_sorted(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_sort_heap_with_comparator_iterator) + { + std::vector vec{1, 3, 5, 7, 9}; + std::make_heap(vec.begin(), vec.end(), etl::greater{}); + + auto result = etl::ranges::sort_heap(vec.begin(), vec.end(), etl::greater{}); + + CHECK(result == vec.end()); + CHECK(std::is_sorted(vec.begin(), vec.end(), etl::greater{})); + } + + //************************************************************************* + TEST(ranges_sort_heap_with_comparator_range) + { + std::vector vec{1, 3, 5, 7, 9}; + std::make_heap(vec.begin(), vec.end(), etl::greater{}); + + auto result = etl::ranges::sort_heap(vec, etl::greater{}); + + CHECK(result == vec.end()); + CHECK(std::is_sorted(vec.begin(), vec.end(), etl::greater{})); + } + + //************************************************************************* + TEST(ranges_sort_heap_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{9, 90}, {5, 50}, {7, 70}, {1, 10}, {3, 30}}; + etl::ranges::make_heap(vec.begin(), vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + + etl::ranges::sort_heap(vec.begin(), vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + + for (size_t i = 1; i < vec.size(); ++i) + { + CHECK(vec[i - 1].key <= vec[i].key); + } + } + + //************************************************************************* + TEST(ranges_sort_heap_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{9, 90}, {5, 50}, {7, 70}, {1, 10}, {3, 30}}; + etl::ranges::make_heap(vec, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + etl::ranges::sort_heap(vec, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + for (size_t i = 1; i < vec.size(); ++i) + { + CHECK(vec[i - 1].key <= vec[i].key); + } + } + + //************************************************************************* + TEST(ranges_sort_heap_empty) + { + std::vector vec{}; + + auto result = etl::ranges::sort_heap(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_sort_heap_single_element) + { + std::vector vec{42}; + + auto result = etl::ranges::sort_heap(vec); + + CHECK(result == vec.end()); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_sort_heap_matches_std) + { + std::vector data_std{9, 3, 7, 1, 5, 8, 2, 6, 4, 10}; + std::vector data_etl = data_std; + + std::make_heap(data_std.begin(), data_std.end()); + std::make_heap(data_etl.begin(), data_etl.end()); + + std::sort_heap(data_std.begin(), data_std.end()); + etl::ranges::sort_heap(data_etl); + + for (size_t i = 0; i < data_std.size(); ++i) + { + CHECK_EQUAL(data_std[i], data_etl[i]); + } + } + + //************************************************************************* + TEST(ranges_is_heap_until_iterator) + { + std::vector vec{9, 5, 7, 1, 3, 2, 6}; + + auto result = etl::ranges::is_heap_until(vec.begin(), vec.end()); + auto expected = std::is_heap_until(vec.begin(), vec.end()); + + CHECK(result == expected); + } + + //************************************************************************* + TEST(ranges_is_heap_until_range) + { + std::vector vec{9, 5, 7, 1, 3, 2, 6}; + + auto result = etl::ranges::is_heap_until(vec); + auto expected = std::is_heap_until(vec.begin(), vec.end()); + + CHECK(result == expected); + } + + //************************************************************************* + TEST(ranges_is_heap_until_not_heap) + { + std::vector vec{1, 5, 3, 7, 2}; + + auto result = etl::ranges::is_heap_until(vec.begin(), vec.end()); + auto expected = std::is_heap_until(vec.begin(), vec.end()); + + CHECK(result == expected); + } + + //************************************************************************* + TEST(ranges_is_heap_until_with_comparator_iterator) + { + std::vector vec{1, 3, 2, 5, 7, 4, 6}; + + auto result = etl::ranges::is_heap_until(vec.begin(), vec.end(), etl::greater{}); + auto expected = std::is_heap_until(vec.begin(), vec.end(), etl::greater{}); + + CHECK(result == expected); + } + + //************************************************************************* + TEST(ranges_is_heap_until_with_comparator_range) + { + std::vector vec{1, 3, 2, 5, 7, 4, 6}; + + auto result = etl::ranges::is_heap_until(vec, etl::greater{}); + auto expected = std::is_heap_until(vec.begin(), vec.end(), etl::greater{}); + + CHECK(result == expected); + } + + //************************************************************************* + TEST(ranges_is_heap_until_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{9, 90}, {5, 50}, {7, 70}, {1, 10}, {3, 30}}; + + auto result = etl::ranges::is_heap_until(vec.begin(), vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_is_heap_until_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{9, 90}, {5, 50}, {7, 70}, {1, 10}, {3, 30}}; + + auto result = etl::ranges::is_heap_until(vec, etl::ranges::less{}, [](const Item& item) { return item.key; }); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_is_heap_until_empty) + { + std::vector vec{}; + + auto result = etl::ranges::is_heap_until(vec.begin(), vec.end()); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_is_heap_until_single_element) + { + std::vector vec{42}; + + auto result = etl::ranges::is_heap_until(vec); + + CHECK(result == vec.end()); + } + + //************************************************************************* + TEST(ranges_is_heap_until_matches_std) + { + std::vector vec{9, 3, 7, 1, 5, 8, 2, 6, 4, 10}; + + auto result_etl = etl::ranges::is_heap_until(vec); + auto result_std = std::is_heap_until(vec.begin(), vec.end()); + + CHECK(result_etl == result_std); + } + + //************************************************************************* + TEST(ranges_is_heap_iterator) + { + std::vector vec{9, 5, 7, 1, 3, 2, 6}; + + CHECK(etl::ranges::is_heap(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_is_heap_range) + { + std::vector vec{9, 5, 7, 1, 3, 2, 6}; + + CHECK(etl::ranges::is_heap(vec)); + } + + //************************************************************************* + TEST(ranges_is_heap_not_heap_iterator) + { + std::vector vec{1, 5, 3, 7, 2}; + + CHECK(!etl::ranges::is_heap(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_is_heap_not_heap_range) + { + std::vector vec{1, 5, 3, 7, 2}; + + CHECK(!etl::ranges::is_heap(vec)); + } + + //************************************************************************* + TEST(ranges_is_heap_with_comparator_iterator) + { + std::vector vec{1, 3, 2, 5, 7, 4, 6}; + + CHECK(etl::ranges::is_heap(vec.begin(), vec.end(), etl::greater{})); + } + + //************************************************************************* + TEST(ranges_is_heap_with_comparator_range) + { + std::vector vec{1, 3, 2, 5, 7, 4, 6}; + + CHECK(etl::ranges::is_heap(vec, etl::greater{})); + } + + //************************************************************************* + TEST(ranges_is_heap_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{9, 90}, {5, 50}, {7, 70}, {1, 10}, {3, 30}}; + + CHECK(etl::ranges::is_heap(vec.begin(), vec.end(), etl::ranges::less{}, [](const Item& item) { return item.key; })); + } + + //************************************************************************* + TEST(ranges_is_heap_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{9, 90}, {5, 50}, {7, 70}, {1, 10}, {3, 30}}; + + CHECK(etl::ranges::is_heap(vec, etl::ranges::less{}, [](const Item& item) { return item.key; })); + } + + //************************************************************************* + TEST(ranges_is_heap_empty) + { + std::vector vec{}; + + CHECK(etl::ranges::is_heap(vec.begin(), vec.end())); + } + + //************************************************************************* + TEST(ranges_is_heap_single_element) + { + std::vector vec{42}; + + CHECK(etl::ranges::is_heap(vec)); + } + + //************************************************************************* + TEST(ranges_is_heap_after_make_heap) + { + std::vector vec{5, 3, 1, 4, 2}; + etl::ranges::make_heap(vec); + + CHECK(etl::ranges::is_heap(vec)); + } + + //************************************************************************* + TEST(ranges_is_heap_matches_std) + { + std::vector data1{9, 3, 7, 1, 5, 8, 2, 6, 4, 10}; + CHECK_EQUAL(std::is_heap(data1.begin(), data1.end()), etl::ranges::is_heap(data1)); + + std::vector data2{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + CHECK_EQUAL(std::is_heap(data2.begin(), data2.end()), etl::ranges::is_heap(data2)); + + std::vector data3{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + CHECK_EQUAL(std::is_heap(data3.begin(), data3.end()), etl::ranges::is_heap(data3)); + } + + //************************************************************************* + TEST(ranges_min_two_values) + { + CHECK_EQUAL(3, etl::ranges::min(3, 5)); + CHECK_EQUAL(3, etl::ranges::min(5, 3)); + CHECK_EQUAL(7, etl::ranges::min(7, 7)); + } + + //************************************************************************* + TEST(ranges_min_two_values_with_comparator) + { + CHECK_EQUAL(5, etl::ranges::min(3, 5, std::greater{})); + CHECK_EQUAL(5, etl::ranges::min(5, 3, std::greater{})); + } + + //************************************************************************* + TEST(ranges_min_two_values_with_projection) + { + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + CHECK_EQUAL(3, etl::ranges::min(3, -5, etl::ranges::less{}, abs_proj)); + CHECK_EQUAL(3, etl::ranges::min(-5, 3, etl::ranges::less{}, abs_proj)); + } + + //************************************************************************* + TEST(ranges_min_initializer_list) + { + CHECK_EQUAL(1, etl::ranges::min({3, 1, 4, 1, 5, 9, 2, 6})); + CHECK_EQUAL(42, etl::ranges::min({42})); + } + + //************************************************************************* + TEST(ranges_min_initializer_list_with_comparator) + { + CHECK_EQUAL(9, etl::ranges::min({3, 1, 4, 1, 5, 9, 2, 6}, std::greater{})); + } + + //************************************************************************* + TEST(ranges_min_initializer_list_with_projection) + { + auto negate = [](int v) { return -v; }; + CHECK_EQUAL(9, etl::ranges::min({3, 1, 4, 5, 9, 2, 6}, etl::ranges::less{}, negate)); + } + + //************************************************************************* + TEST(ranges_min_range) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + CHECK_EQUAL(1, etl::ranges::min(vec)); + } + + //************************************************************************* + TEST(ranges_min_range_with_comparator) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + CHECK_EQUAL(9, etl::ranges::min(vec, std::greater{})); + } + + //************************************************************************* + TEST(ranges_min_range_with_projection) + { + std::vector vec{3, -1, 4, -5, 2}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + CHECK_EQUAL(-1, etl::ranges::min(vec, etl::ranges::less{}, abs_proj)); + } + + //************************************************************************* + TEST(ranges_min_single_element_range) + { + std::vector vec{42}; + CHECK_EQUAL(42, etl::ranges::min(vec)); + } + + //************************************************************************* + TEST(ranges_min_all_equal) + { + std::vector vec{7, 7, 7, 7}; + CHECK_EQUAL(7, etl::ranges::min(vec)); + } + + //************************************************************************* + TEST(ranges_min_negative_values) + { + std::vector vec{-3, -1, -4, -1, -5}; + CHECK_EQUAL(-5, etl::ranges::min(vec)); + } + + //************************************************************************* + TEST(ranges_min_matches_std) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3}; + CHECK_EQUAL(*std::min_element(vec.begin(), vec.end()), etl::ranges::min(vec)); + + std::vector vec2{-10, -20, -5, -15}; + CHECK_EQUAL(*std::min_element(vec2.begin(), vec2.end()), etl::ranges::min(vec2)); + } + + //************************************************************************* + TEST(ranges_min_element_iterator_sentinel) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto it = etl::ranges::min_element(vec.begin(), vec.end()); + CHECK_EQUAL(1, *it); + } + + //************************************************************************* + TEST(ranges_min_element_iterator_sentinel_with_comparator) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto it = etl::ranges::min_element(vec.begin(), vec.end(), std::greater{}); + CHECK_EQUAL(9, *it); + } + + //************************************************************************* + TEST(ranges_min_element_iterator_sentinel_with_projection) + { + std::vector vec{3, -1, 4, -5, 2}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + auto it = etl::ranges::min_element(vec.begin(), vec.end(), etl::ranges::less{}, abs_proj); + CHECK_EQUAL(-1, *it); + } + + //************************************************************************* + TEST(ranges_min_element_range) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto it = etl::ranges::min_element(vec); + CHECK_EQUAL(1, *it); + } + + //************************************************************************* + TEST(ranges_min_element_range_with_comparator) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto it = etl::ranges::min_element(vec, std::greater{}); + CHECK_EQUAL(9, *it); + } + + //************************************************************************* + TEST(ranges_min_element_range_with_projection) + { + std::vector vec{3, -1, 4, -5, 2}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + auto it = etl::ranges::min_element(vec, etl::ranges::less{}, abs_proj); + CHECK_EQUAL(-1, *it); + } + + //************************************************************************* + TEST(ranges_min_element_single_element) + { + std::vector vec{42}; + auto it = etl::ranges::min_element(vec); + CHECK_EQUAL(42, *it); + CHECK(it == vec.begin()); + } + + //************************************************************************* + TEST(ranges_min_element_empty_range) + { + std::vector vec{}; + auto it = etl::ranges::min_element(vec.begin(), vec.end()); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_min_element_all_equal) + { + std::vector vec{7, 7, 7, 7}; + auto it = etl::ranges::min_element(vec); + CHECK_EQUAL(7, *it); + CHECK(it == vec.begin()); + } + + //************************************************************************* + TEST(ranges_min_element_matches_std) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3}; + CHECK(std::min_element(vec.begin(), vec.end()) == etl::ranges::min_element(vec.begin(), vec.end())); + + std::vector vec2{-10, -20, -5, -15}; + CHECK(std::min_element(vec2.begin(), vec2.end()) == etl::ranges::min_element(vec2)); + } + + //************************************************************************* + TEST(ranges_max_two_values) + { + CHECK_EQUAL(5, etl::ranges::max(3, 5)); + CHECK_EQUAL(5, etl::ranges::max(5, 3)); + CHECK_EQUAL(7, etl::ranges::max(7, 7)); + } + + //************************************************************************* + TEST(ranges_max_two_values_with_comparator) + { + CHECK_EQUAL(3, etl::ranges::max(3, 5, std::greater{})); + CHECK_EQUAL(3, etl::ranges::max(5, 3, std::greater{})); + } + + //************************************************************************* + TEST(ranges_max_two_values_with_projection) + { + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + CHECK_EQUAL(-5, etl::ranges::max(3, -5, etl::ranges::less{}, abs_proj)); + CHECK_EQUAL(-5, etl::ranges::max(-5, 3, etl::ranges::less{}, abs_proj)); + } + + //************************************************************************* + TEST(ranges_max_initializer_list) + { + CHECK_EQUAL(9, etl::ranges::max({3, 1, 4, 1, 5, 9, 2, 6})); + CHECK_EQUAL(42, etl::ranges::max({42})); + } + + //************************************************************************* + TEST(ranges_max_initializer_list_with_comparator) + { + CHECK_EQUAL(1, etl::ranges::max({3, 1, 4, 1, 5, 9, 2, 6}, std::greater{})); + } + + //************************************************************************* + TEST(ranges_max_initializer_list_with_projection) + { + auto negate = [](int v) { return -v; }; + CHECK_EQUAL(1, etl::ranges::max({3, 1, 4, 5, 9, 2, 6}, etl::ranges::less{}, negate)); + } + + //************************************************************************* + TEST(ranges_max_range) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + CHECK_EQUAL(9, etl::ranges::max(vec)); + } + + //************************************************************************* + TEST(ranges_max_range_with_comparator) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + CHECK_EQUAL(1, etl::ranges::max(vec, std::greater{})); + } + + //************************************************************************* + TEST(ranges_max_range_with_projection) + { + std::vector vec{3, -1, 4, -5, 2}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + CHECK_EQUAL(-5, etl::ranges::max(vec, etl::ranges::less{}, abs_proj)); + } + + //************************************************************************* + TEST(ranges_max_single_element_range) + { + std::vector vec{42}; + CHECK_EQUAL(42, etl::ranges::max(vec)); + } + + //************************************************************************* + TEST(ranges_max_all_equal) + { + std::vector vec{7, 7, 7, 7}; + CHECK_EQUAL(7, etl::ranges::max(vec)); + } + + //************************************************************************* + TEST(ranges_max_negative_values) + { + std::vector vec{-3, -1, -4, -1, -5}; + CHECK_EQUAL(-1, etl::ranges::max(vec)); + } + + //************************************************************************* + TEST(ranges_max_matches_std) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3}; + CHECK_EQUAL(*std::max_element(vec.begin(), vec.end()), etl::ranges::max(vec)); + + std::vector vec2{-10, -20, -5, -15}; + CHECK_EQUAL(*std::max_element(vec2.begin(), vec2.end()), etl::ranges::max(vec2)); + } + + //************************************************************************* + TEST(ranges_max_element_iterator_sentinel) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto it = etl::ranges::max_element(vec.begin(), vec.end()); + CHECK_EQUAL(9, *it); + } + + //************************************************************************* + TEST(ranges_max_element_iterator_sentinel_with_comparator) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto it = etl::ranges::max_element(vec.begin(), vec.end(), std::greater{}); + CHECK_EQUAL(1, *it); + } + + //************************************************************************* + TEST(ranges_max_element_iterator_sentinel_with_projection) + { + std::vector vec{3, -1, 4, -5, 2}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + auto it = etl::ranges::max_element(vec.begin(), vec.end(), etl::ranges::less{}, abs_proj); + CHECK_EQUAL(-5, *it); + } + + //************************************************************************* + TEST(ranges_max_element_range) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto it = etl::ranges::max_element(vec); + CHECK_EQUAL(9, *it); + } + + //************************************************************************* + TEST(ranges_max_element_range_with_comparator) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto it = etl::ranges::max_element(vec, std::greater{}); + CHECK_EQUAL(1, *it); + } + + //************************************************************************* + TEST(ranges_max_element_range_with_projection) + { + std::vector vec{3, -1, 4, -5, 2}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + auto it = etl::ranges::max_element(vec, etl::ranges::less{}, abs_proj); + CHECK_EQUAL(-5, *it); + } + + //************************************************************************* + TEST(ranges_max_element_single_element) + { + std::vector vec{42}; + auto it = etl::ranges::max_element(vec); + CHECK_EQUAL(42, *it); + CHECK(it == vec.begin()); + } + + //************************************************************************* + TEST(ranges_max_element_empty_range) + { + std::vector vec{}; + auto it = etl::ranges::max_element(vec.begin(), vec.end()); + CHECK(it == vec.end()); + } + + //************************************************************************* + TEST(ranges_max_element_all_equal) + { + std::vector vec{7, 7, 7, 7}; + auto it = etl::ranges::max_element(vec); + CHECK_EQUAL(7, *it); + CHECK(it == vec.begin()); + } + + //************************************************************************* + TEST(ranges_max_element_matches_std) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3}; + CHECK(std::max_element(vec.begin(), vec.end()) == etl::ranges::max_element(vec.begin(), vec.end())); + + std::vector vec2{-10, -20, -5, -15}; + CHECK(std::max_element(vec2.begin(), vec2.end()) == etl::ranges::max_element(vec2)); + } + + //************************************************************************* + TEST(ranges_minmax_two_values) + { + int a = 3, b = 5; + auto result = etl::ranges::minmax(a, b); + CHECK_EQUAL(3, result.min); + CHECK_EQUAL(5, result.max); + + int c = 5, d = 3; + auto result2 = etl::ranges::minmax(c, d); + CHECK_EQUAL(3, result2.min); + CHECK_EQUAL(5, result2.max); + + int e = 7, f = 7; + auto result3 = etl::ranges::minmax(e, f); + CHECK_EQUAL(7, result3.min); + CHECK_EQUAL(7, result3.max); + } + + //************************************************************************* + TEST(ranges_minmax_two_values_with_comparator) + { + int a = 3, b = 5; + auto result = etl::ranges::minmax(a, b, std::greater{}); + CHECK_EQUAL(5, result.min); + CHECK_EQUAL(3, result.max); + + int c = 5, d = 3; + auto result2 = etl::ranges::minmax(c, d, std::greater{}); + CHECK_EQUAL(5, result2.min); + CHECK_EQUAL(3, result2.max); + } + + //************************************************************************* + TEST(ranges_minmax_two_values_with_projection) + { + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + int a = 3, b = -5; + auto result = etl::ranges::minmax(a, b, etl::ranges::less{}, abs_proj); + CHECK_EQUAL(3, result.min); + CHECK_EQUAL(-5, result.max); + + int c = -5, d = 3; + auto result2 = etl::ranges::minmax(c, d, etl::ranges::less{}, abs_proj); + CHECK_EQUAL(3, result2.min); + CHECK_EQUAL(-5, result2.max); + } + + //************************************************************************* + TEST(ranges_minmax_initializer_list) + { + auto result = etl::ranges::minmax({3, 1, 4, 1, 5, 9, 2, 6}); + CHECK_EQUAL(1, result.min); + CHECK_EQUAL(9, result.max); + + auto result2 = etl::ranges::minmax({42}); + CHECK_EQUAL(42, result2.min); + CHECK_EQUAL(42, result2.max); + } + + //************************************************************************* + TEST(ranges_minmax_initializer_list_with_comparator) + { + auto result = etl::ranges::minmax({3, 1, 4, 1, 5, 9, 2, 6}, std::greater{}); + CHECK_EQUAL(9, result.min); + CHECK_EQUAL(1, result.max); + } + + //************************************************************************* + TEST(ranges_minmax_initializer_list_with_projection) + { + auto negate = [](int v) { return -v; }; + auto result = etl::ranges::minmax({3, 1, 4, 5, 9, 2, 6}, etl::ranges::less{}, negate); + CHECK_EQUAL(9, result.min); + CHECK_EQUAL(1, result.max); + } + + //************************************************************************* + TEST(ranges_minmax_range) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto result = etl::ranges::minmax(vec); + CHECK_EQUAL(1, result.min); + CHECK_EQUAL(9, result.max); + } + + //************************************************************************* + TEST(ranges_minmax_range_with_comparator) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto result = etl::ranges::minmax(vec, std::greater{}); + CHECK_EQUAL(9, result.min); + CHECK_EQUAL(1, result.max); + } + + //************************************************************************* + TEST(ranges_minmax_range_with_projection) + { + std::vector vec{3, -1, 4, -5, 2}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + auto result = etl::ranges::minmax(vec, etl::ranges::less{}, abs_proj); + CHECK_EQUAL(-1, result.min); + CHECK_EQUAL(-5, result.max); + } + + //************************************************************************* + TEST(ranges_minmax_single_element_range) + { + std::vector vec{42}; + auto result = etl::ranges::minmax(vec); + CHECK_EQUAL(42, result.min); + CHECK_EQUAL(42, result.max); + } + + //************************************************************************* + TEST(ranges_minmax_all_equal) + { + std::vector vec{7, 7, 7, 7}; + auto result = etl::ranges::minmax(vec); + CHECK_EQUAL(7, result.min); + CHECK_EQUAL(7, result.max); + } + + //************************************************************************* + TEST(ranges_minmax_negative_values) + { + std::vector vec{-3, -1, -4, -1, -5}; + auto result = etl::ranges::minmax(vec); + CHECK_EQUAL(-5, result.min); + CHECK_EQUAL(-1, result.max); + } + + //************************************************************************* + TEST(ranges_minmax_element_iterator_sentinel) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto result = etl::ranges::minmax_element(vec.begin(), vec.end()); + CHECK_EQUAL(1, *result.min); + CHECK_EQUAL(9, *result.max); + } + + //************************************************************************* + TEST(ranges_minmax_element_iterator_sentinel_with_comparator) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto result = etl::ranges::minmax_element(vec.begin(), vec.end(), std::greater{}); + CHECK_EQUAL(9, *result.min); + CHECK_EQUAL(1, *result.max); + } + + //************************************************************************* + TEST(ranges_minmax_element_iterator_sentinel_with_projection) + { + std::vector vec{3, -1, 4, -5, 2}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + auto result = etl::ranges::minmax_element(vec.begin(), vec.end(), etl::ranges::less{}, abs_proj); + CHECK_EQUAL(-1, *result.min); + CHECK_EQUAL(-5, *result.max); + } + + //************************************************************************* + TEST(ranges_minmax_element_range) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto result = etl::ranges::minmax_element(vec); + CHECK_EQUAL(1, *result.min); + CHECK_EQUAL(9, *result.max); + } + + //************************************************************************* + TEST(ranges_minmax_element_range_with_comparator) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6}; + auto result = etl::ranges::minmax_element(vec, std::greater{}); + CHECK_EQUAL(9, *result.min); + CHECK_EQUAL(1, *result.max); + } + + //************************************************************************* + TEST(ranges_minmax_element_range_with_projection) + { + std::vector vec{3, -1, 4, -5, 2}; + auto abs_proj = [](int v) { return v < 0 ? -v : v; }; + auto result = etl::ranges::minmax_element(vec, etl::ranges::less{}, abs_proj); + CHECK_EQUAL(-1, *result.min); + CHECK_EQUAL(-5, *result.max); + } + + //************************************************************************* + TEST(ranges_minmax_element_single_element) + { + std::vector vec{42}; + auto result = etl::ranges::minmax_element(vec); + CHECK_EQUAL(42, *result.min); + CHECK_EQUAL(42, *result.max); + CHECK(result.min == vec.begin()); + CHECK(result.max == vec.begin()); + } + + //************************************************************************* + TEST(ranges_minmax_element_empty_range) + { + std::vector vec{}; + auto result = etl::ranges::minmax_element(vec.begin(), vec.end()); + CHECK(result.min == vec.end()); + CHECK(result.max == vec.end()); + } + + //************************************************************************* + TEST(ranges_minmax_element_all_equal) + { + std::vector vec{7, 7, 7, 7}; + auto result = etl::ranges::minmax_element(vec); + CHECK_EQUAL(7, *result.min); + CHECK_EQUAL(7, *result.max); + CHECK(result.min == vec.begin()); + } + + //************************************************************************* + TEST(ranges_minmax_element_matches_std) + { + std::vector vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3}; + auto std_result = std::minmax_element(vec.begin(), vec.end()); + auto etl_result = etl::ranges::minmax_element(vec.begin(), vec.end()); + CHECK(std_result.first == etl_result.min); + CHECK(std_result.second == etl_result.max); + + std::vector vec2{-10, -20, -5, -15}; + auto std_result2 = std::minmax_element(vec2.begin(), vec2.end()); + auto etl_result2 = etl::ranges::minmax_element(vec2); + CHECK(std_result2.first == etl_result2.min); + CHECK(std_result2.second == etl_result2.max); + } + + //************************************************************************* + // ranges::clamp + //************************************************************************* + TEST(ranges_clamp_value_in_range) + { + CHECK_EQUAL(5, etl::ranges::clamp(5, 0, 10)); + } + + TEST(ranges_clamp_value_below_low) + { + CHECK_EQUAL(0, etl::ranges::clamp(-5, 0, 10)); + } + + TEST(ranges_clamp_value_above_high) + { + CHECK_EQUAL(10, etl::ranges::clamp(15, 0, 10)); + } + + TEST(ranges_clamp_value_equal_to_low) + { + CHECK_EQUAL(0, etl::ranges::clamp(0, 0, 10)); + } + + TEST(ranges_clamp_value_equal_to_high) + { + CHECK_EQUAL(10, etl::ranges::clamp(10, 0, 10)); + } + + TEST(ranges_clamp_with_comparator) + { + // Using greater: clamp(5, 10, 0, greater) means low=10, high=0 in reverse order + CHECK_EQUAL(5, etl::ranges::clamp(5, 10, 0, std::greater{})); + CHECK_EQUAL(10, etl::ranges::clamp(15, 10, 0, std::greater{})); + CHECK_EQUAL(0, etl::ranges::clamp(-5, 10, 0, std::greater{})); + } + + TEST(ranges_clamp_with_projection) + { + auto abs_proj = [](int x) { return x < 0 ? -x : x; }; + + // Clamp by absolute value: value=-3, low=2, high=8 + // proj(-3)=3, proj(2)=2, proj(8)=8 => 3 is in [2,8] => returns -3 + CHECK_EQUAL(-3, etl::ranges::clamp(-3, 2, 8, etl::ranges::less{}, abs_proj)); + + // proj(1)=1, proj(2)=2 => 1 < 2 => returns low=2 + CHECK_EQUAL(2, etl::ranges::clamp(1, 2, 8, etl::ranges::less{}, abs_proj)); + + // proj(-10)=10, proj(8)=8 => 10 > 8 => returns high=8 + CHECK_EQUAL(8, etl::ranges::clamp(-10, 2, 8, etl::ranges::less{}, abs_proj)); + } + + TEST(ranges_clamp_returns_reference) + { + const int value = 5; + const int low = 0; + const int high = 10; + const int& result = etl::ranges::clamp(value, low, high); + CHECK_EQUAL(&value, &result); + + const int below = -5; + const int& result_low = etl::ranges::clamp(below, low, high); + CHECK_EQUAL(&low, &result_low); + + const int above = 15; + const int& result_high = etl::ranges::clamp(above, low, high); + CHECK_EQUAL(&high, &result_high); + } + + TEST(ranges_clamp_constexpr) + { + constexpr int result1 = etl::ranges::clamp(5, 0, 10); + constexpr int result2 = etl::ranges::clamp(-5, 0, 10); + constexpr int result3 = etl::ranges::clamp(15, 0, 10); + + CHECK_EQUAL(5, result1); + CHECK_EQUAL(0, result2); + CHECK_EQUAL(10, result3); + } + + TEST(ranges_clamp_matches_std) + { + for (int v = -20; v <= 20; ++v) + { + CHECK_EQUAL(std::clamp(v, 0, 10), etl::ranges::clamp(v, 0, 10)); + } + } + + //************************************************************************* + TEST(ranges_prev_permutation_iterator) + { + std::vector vec{3, 2, 1}; + std::vector expected{3, 1, 2}; + + auto result = etl::ranges::prev_permutation(vec.begin(), vec.end()); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_prev_permutation_range) + { + std::vector vec{3, 2, 1}; + std::vector expected{3, 1, 2}; + + auto result = etl::ranges::prev_permutation(vec); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_prev_permutation_first_permutation) + { + // {1, 2, 3} is the first (smallest) permutation; prev should wrap to last and return found=false + std::vector vec{1, 2, 3}; + std::vector expected{3, 2, 1}; + + auto result = etl::ranges::prev_permutation(vec.begin(), vec.end()); + + CHECK(result.in == vec.end()); + CHECK(result.found == false); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_prev_permutation_empty) + { + std::vector vec{}; + + auto result = etl::ranges::prev_permutation(vec.begin(), vec.end()); + + CHECK(result.in == vec.end()); + CHECK(result.found == false); + } + + //************************************************************************* + TEST(ranges_prev_permutation_single_element) + { + std::vector vec{42}; + + auto result = etl::ranges::prev_permutation(vec); + + CHECK(result.in == vec.end()); + CHECK(result.found == false); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_prev_permutation_with_comparator_iterator) + { + // With greater<>, prev_permutation acts like next_permutation with less<> + std::vector vec{1, 2, 3}; + std::vector expected{1, 3, 2}; + + auto result = etl::ranges::prev_permutation(vec.begin(), vec.end(), etl::greater{}); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_prev_permutation_with_comparator_range) + { + std::vector vec{1, 2, 3}; + std::vector expected{1, 3, 2}; + + auto result = etl::ranges::prev_permutation(vec, etl::greater{}); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_prev_permutation_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {2, 20}, {1, 10}}; + + auto result = etl::ranges::prev_permutation(vec.begin(), vec.end(), etl::ranges::less{}, + [](const Item& item) { return item.key; }); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK_EQUAL(3, vec[0].key); + CHECK_EQUAL(1, vec[1].key); + CHECK_EQUAL(2, vec[2].key); + } + + //************************************************************************* + TEST(ranges_prev_permutation_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{3, 30}, {2, 20}, {1, 10}}; + + auto result = etl::ranges::prev_permutation(vec, etl::ranges::less{}, + [](const Item& item) { return item.key; }); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK_EQUAL(3, vec[0].key); + CHECK_EQUAL(1, vec[1].key); + CHECK_EQUAL(2, vec[2].key); + } + + //************************************************************************* + TEST(ranges_prev_permutation_full_cycle) + { + // Starting from last permutation {3,2,1}, calling prev_permutation repeatedly + // should visit all 6 permutations and wrap around. + std::vector vec{3, 2, 1}; + int count = 0; + bool found = true; + + do + { + ++count; + auto result = etl::ranges::prev_permutation(vec); + found = result.found; + } while (found); + + // 3! = 6 permutations + CHECK_EQUAL(6, count); + // Should wrap back to {3, 2, 1} + std::vector expected{3, 2, 1}; + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_prev_permutation_matches_std) + { + std::vector data_std{3, 2, 1}; + std::vector data_etl = data_std; + + bool std_result = std::prev_permutation(data_std.begin(), data_std.end()); + auto etl_result = etl::ranges::prev_permutation(data_etl); + + CHECK_EQUAL(std_result, etl_result.found); + bool are_equal = std::equal(data_std.begin(), data_std.end(), data_etl.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST(ranges_prev_permutation_matches_std_all_permutations) + { + std::vector data_std{4, 3, 2, 1}; + std::vector data_etl = data_std; + + bool complete = false; + while (!complete) + { + bool std_result = std::prev_permutation(data_std.begin(), data_std.end()); + auto etl_result = etl::ranges::prev_permutation(data_etl); + + CHECK_EQUAL(std_result, etl_result.found); + bool are_equal = std::equal(data_std.begin(), data_std.end(), data_etl.begin()); + CHECK(are_equal); + + complete = !std_result; + } + } + + //************************************************************************* + TEST(ranges_prev_permutation_duplicates) + { + std::vector data_std{2, 2, 1}; + std::vector data_etl = data_std; + + bool std_result = std::prev_permutation(data_std.begin(), data_std.end()); + auto etl_result = etl::ranges::prev_permutation(data_etl); + + CHECK_EQUAL(std_result, etl_result.found); + bool are_equal = std::equal(data_std.begin(), data_std.end(), data_etl.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST(ranges_prev_permutation_two_elements) + { + std::vector vec{2, 1}; + std::vector expected{1, 2}; + + auto result = etl::ranges::prev_permutation(vec); + + CHECK(result.found == true); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_next_permutation_iterator) + { + std::vector vec{1, 2, 3}; + std::vector expected{1, 3, 2}; + + auto result = etl::ranges::next_permutation(vec.begin(), vec.end()); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_next_permutation_range) + { + std::vector vec{1, 2, 3}; + std::vector expected{1, 3, 2}; + + auto result = etl::ranges::next_permutation(vec); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_next_permutation_last_permutation) + { + // {3, 2, 1} is the last permutation; next should wrap to first and return found=false + std::vector vec{3, 2, 1}; + std::vector expected{1, 2, 3}; + + auto result = etl::ranges::next_permutation(vec.begin(), vec.end()); + + CHECK(result.in == vec.end()); + CHECK(result.found == false); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_next_permutation_empty) + { + std::vector vec{}; + + auto result = etl::ranges::next_permutation(vec.begin(), vec.end()); + + CHECK(result.in == vec.end()); + CHECK(result.found == false); + } + + //************************************************************************* + TEST(ranges_next_permutation_single_element) + { + std::vector vec{42}; + + auto result = etl::ranges::next_permutation(vec); + + CHECK(result.in == vec.end()); + CHECK(result.found == false); + CHECK_EQUAL(42, vec[0]); + } + + //************************************************************************* + TEST(ranges_next_permutation_with_comparator_iterator) + { + // With greater<>, next_permutation acts like prev_permutation with less<> + std::vector vec{3, 2, 1}; + std::vector expected{3, 1, 2}; + + auto result = etl::ranges::next_permutation(vec.begin(), vec.end(), etl::greater{}); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_next_permutation_with_comparator_range) + { + std::vector vec{3, 2, 1}; + std::vector expected{3, 1, 2}; + + auto result = etl::ranges::next_permutation(vec, etl::greater{}); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_next_permutation_with_projection_iterator) + { + struct Item { int key; int value; }; + std::vector vec{{1, 10}, {2, 20}, {3, 30}}; + + auto result = etl::ranges::next_permutation(vec.begin(), vec.end(), etl::ranges::less{}, + [](const Item& item) { return item.key; }); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK_EQUAL(1, vec[0].key); + CHECK_EQUAL(3, vec[1].key); + CHECK_EQUAL(2, vec[2].key); + } + + //************************************************************************* + TEST(ranges_next_permutation_with_projection_range) + { + struct Item { int key; int value; }; + std::vector vec{{1, 10}, {2, 20}, {3, 30}}; + + auto result = etl::ranges::next_permutation(vec, etl::ranges::less{}, + [](const Item& item) { return item.key; }); + + CHECK(result.in == vec.end()); + CHECK(result.found == true); + CHECK_EQUAL(1, vec[0].key); + CHECK_EQUAL(3, vec[1].key); + CHECK_EQUAL(2, vec[2].key); + } + + //************************************************************************* + TEST(ranges_next_permutation_full_cycle) + { + // Starting from first permutation {1,2,3}, calling next_permutation repeatedly + // should visit all 6 permutations and wrap around. + std::vector vec{1, 2, 3}; + int count = 0; + bool found = true; + + do + { + ++count; + auto result = etl::ranges::next_permutation(vec); + found = result.found; + } while (found); + + // 3! = 6 permutations + CHECK_EQUAL(6, count); + // Should wrap back to {1, 2, 3} + std::vector expected{1, 2, 3}; + CHECK(vec == expected); + } + + //************************************************************************* + TEST(ranges_next_permutation_matches_std) + { + std::vector data_std{1, 2, 3}; + std::vector data_etl = data_std; + + bool std_result = std::next_permutation(data_std.begin(), data_std.end()); + auto etl_result = etl::ranges::next_permutation(data_etl); + + CHECK_EQUAL(std_result, etl_result.found); + bool are_equal = std::equal(data_std.begin(), data_std.end(), data_etl.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST(ranges_next_permutation_matches_std_all_permutations) + { + std::vector data_std{1, 2, 3, 4}; + std::vector data_etl = data_std; + + bool complete = false; + while (!complete) + { + bool std_result = std::next_permutation(data_std.begin(), data_std.end()); + auto etl_result = etl::ranges::next_permutation(data_etl); + + CHECK_EQUAL(std_result, etl_result.found); + bool are_equal = std::equal(data_std.begin(), data_std.end(), data_etl.begin()); + CHECK(are_equal); + + complete = !std_result; + } + } + + //************************************************************************* + TEST(ranges_next_permutation_duplicates) + { + std::vector data_std{1, 2, 2}; + std::vector data_etl = data_std; + + bool std_result = std::next_permutation(data_std.begin(), data_std.end()); + auto etl_result = etl::ranges::next_permutation(data_etl); + + CHECK_EQUAL(std_result, etl_result.found); + bool are_equal = std::equal(data_std.begin(), data_std.end(), data_etl.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST(ranges_next_permutation_two_elements) + { + std::vector vec{1, 2}; + std::vector expected{2, 1}; + + auto result = etl::ranges::next_permutation(vec); + + CHECK(result.found == true); + CHECK(vec == expected); + } +#endif } } + + diff --git a/test/test_functional.cpp b/test/test_functional.cpp index 5bbe6beb..a0d416c9 100644 --- a/test/test_functional.cpp +++ b/test/test_functional.cpp @@ -274,6 +274,43 @@ namespace CHECK_EQUAL(1, ra); } +#if ETL_USING_CPP11 + //************************************************************************* + TEST(test_reference_wrapper_call_operator_no_args) + { + struct functor + { + int operator()() const { return 42; } + }; + + functor f; + etl::reference_wrapper rw(f); + CHECK_EQUAL(42, rw()); + } + + //************************************************************************* + TEST(test_reference_wrapper_call_operator_with_args) + { + struct adder + { + int operator()(int a, int b) const { return a + b; } + }; + + adder f; + etl::reference_wrapper rw(f); + CHECK_EQUAL(7, rw(3, 4)); + } + + //************************************************************************* + TEST(test_reference_wrapper_call_operator_function_pointer) + { + int (*fn)(int, int) = [](int a, int b) { return a * b; }; + + etl::reference_wrapper rw(fn); + CHECK_EQUAL(12, rw(3, 4)); + } +#endif + //************************************************************************* TEST(test_plus) { @@ -449,5 +486,96 @@ namespace result = f3(mft, "Arg1", " : Arg2"); CHECK_EQUAL("Function3_Const: Arg1 : Arg2", result); } + +#if ETL_USING_CPP14 + //************************************************************************* + TEST(test_identity) + { + etl::identity i; + std::string s{"abc"}; + CHECK_EQUAL(s, i(s)); + CHECK_EQUAL(&s, &i(s)); + CHECK_EQUAL(&s, i(&s)); + } +#endif + +#if ETL_USING_CPP17 + //************************************************************************* + TEST(test_ranges_equal_to_same_type) + { + etl::ranges::equal_to eq; + + CHECK(eq(1, 1)); + CHECK(!eq(1, 2)); + CHECK(!eq(2, 1)); + } + + //************************************************************************* + TEST(test_ranges_equal_to_mixed_types) + { + etl::ranges::equal_to eq; + + // int vs long + CHECK(eq(1, 1L)); + CHECK(!eq(1, 2L)); + CHECK(eq(2L, 2)); + + // int vs short + CHECK(eq(42, short(42))); + CHECK(!eq(42, short(43))); + } + + //************************************************************************* + TEST(test_ranges_equal_to_constexpr) + { + constexpr etl::ranges::equal_to eq{}; + constexpr bool result_equal = eq(5, 5); + constexpr bool result_not_equal = eq(5, 6); + + CHECK(result_equal); + CHECK(!result_not_equal); + } + + //************************************************************************* + TEST(test_ranges_less_same_type) + { + etl::ranges::less lt; + + CHECK(lt(1, 2)); + CHECK(!lt(2, 1)); + CHECK(!lt(1, 1)); + } + + //************************************************************************* + TEST(test_ranges_less_mixed_types) + { + etl::ranges::less lt; + + // int vs long + CHECK(lt(1, 2L)); + CHECK(!lt(2, 1L)); + CHECK(!lt(1, 1L)); + CHECK(lt(1L, 2)); + + // int vs short + CHECK(lt(42, short(43))); + CHECK(!lt(42, short(42))); + CHECK(!lt(43, short(42))); + } + + //************************************************************************* + TEST(test_ranges_less_constexpr) + { + constexpr etl::ranges::less lt{}; + constexpr bool result_less = lt(5, 6); + constexpr bool result_not_less = lt(6, 5); + constexpr bool result_equal = lt(5, 5); + + CHECK(result_less); + CHECK(!result_not_less); + CHECK(!result_equal); + } +#endif + } } diff --git a/test/test_invoke.cpp b/test/test_invoke.cpp index 7d060e52..648a5b3d 100644 --- a/test/test_invoke.cpp +++ b/test/test_invoke.cpp @@ -481,4 +481,153 @@ SUITE(test_invoke) CHECK_EQUAL(1, etl::invoke(&RR::f, etl::move(rr))); CHECK_EQUAL(2, etl::invoke(&RR::g, rr)); } + + //************************************************************************* + // Tests for reference_wrapper as callable (first argument) + //************************************************************************* + + //************************************************************************* + TEST(test_reference_wrapper_free_function) + { + // reference_wrapper wrapping a function (decays to function pointer) + auto ref_fn = etl::ref(free_add); + + CHECK_EQUAL(3, etl::invoke(ref_fn, 1, 2)); + } + + //************************************************************************* + TEST(test_reference_wrapper_functor) + { + Functor functor(10); + auto ref_functor = etl::ref(functor); + + CHECK_EQUAL(12, etl::invoke(ref_functor, 2)); + } + + //************************************************************************* + TEST(test_reference_wrapper_const_functor) + { + const ConstFunctor functor(10); + auto ref_functor = etl::cref(functor); + + CHECK_EQUAL(12, etl::invoke(ref_functor, 2)); + } + + //************************************************************************* + TEST(test_reference_wrapper_lambda) + { + int capture_value = 5; + auto lambda = [capture_value](int a) { return a + capture_value; }; + auto ref_lambda = etl::ref(lambda); + + CHECK_EQUAL(6, etl::invoke(ref_lambda, 1)); + } + + //************************************************************************* + TEST(test_reference_wrapper_nothrow_functor) + { + NoThrowFunctor nothrow_functor; + auto ref_functor = etl::ref(nothrow_functor); + + CHECK_EQUAL(12, etl::invoke(ref_functor, 10)); + } + + //************************************************************************* + TEST(test_reference_wrapper_throwing_functor) + { + ThrowingFunctor throwing_functor; + auto ref_functor = etl::ref(throwing_functor); + + CHECK_THROW(etl::invoke(ref_functor, 10), int); + } + + //************************************************************************* + TEST(test_reference_wrapper_member_function_pointer) + { + // reference_wrapper wrapping a pointer-to-member-function + auto pmf = &Base::add; + auto ref_pmf = etl::ref(pmf); + + Base base(10); + + CHECK_EQUAL(11, etl::invoke(ref_pmf, base, 1)); + CHECK_EQUAL(12, etl::invoke(ref_pmf, &base, 2)); + } + + //************************************************************************* + TEST(test_reference_wrapper_const_member_function_pointer) + { + auto pmf = &Base::add_const; + auto ref_pmf = etl::ref(pmf); + + const Base const_base(20); + + CHECK_EQUAL(24, etl::invoke(ref_pmf, const_base, 3)); + CHECK_EQUAL(25, etl::invoke(ref_pmf, &const_base, 4)); + } + + //************************************************************************* + TEST(test_reference_wrapper_member_object_pointer) + { + // reference_wrapper wrapping a pointer-to-member-data + auto pmd = &MemberObj::i; + auto ref_pmd = etl::ref(pmd); + + MemberObj obj(42); + + CHECK_EQUAL(42, etl::invoke(ref_pmd, obj)); + CHECK_EQUAL(42, etl::invoke(ref_pmd, &obj)); + } + + //************************************************************************* + TEST(test_reference_wrapper_derived_member_function) + { + // reference_wrapper wrapping member pointer, invoked on derived class + auto pmf = &Base::add; + auto ref_pmf = etl::ref(pmf); + + Derived derived(10); + + CHECK_EQUAL(11, etl::invoke(ref_pmf, derived, 1)); + CHECK_EQUAL(12, etl::invoke(ref_pmf, &derived, 2)); + } + +#if ETL_USING_CPP14 + //************************************************************************* + TEST(test_reference_wrapper_identity) + { + // This is the case that triggered the original compile error: + // reference_wrapper as a callable + etl::identity id; + auto ref_id = etl::ref(id); + + CHECK_EQUAL(42, etl::invoke(ref_id, 42)); + CHECK_EQUAL(7, etl::invoke(ref_id, 7)); + } +#endif + + //************************************************************************* + TEST(test_reference_wrapper_with_reference_wrapper_arg) + { + // reference_wrapper as both callable and second argument (member pointer case) + auto pmf = &Base::add; + auto ref_pmf = etl::ref(pmf); + + Base base(10); + auto ref_base = etl::ref(base); + + CHECK_EQUAL(11, etl::invoke(ref_pmf, ref_base, 1)); + } + + //************************************************************************* + TEST(test_reference_wrapper_member_object_with_reference_wrapper_arg) + { + auto pmd = &MemberObj::i; + auto ref_pmd = etl::ref(pmd); + + MemberObj obj(99); + auto ref_obj = etl::ref(obj); + + CHECK_EQUAL(99, etl::invoke(ref_pmd, ref_obj)); + } } diff --git a/test/test_iterator.cpp b/test/test_iterator.cpp index 14f0d878..8bcb089f 100644 --- a/test/test_iterator.cpp +++ b/test/test_iterator.cpp @@ -32,6 +32,7 @@ SOFTWARE. #include #include #include +#include #include "etl/iterator.h" @@ -70,6 +71,40 @@ namespace typedef int* pointer; typedef const int* const_pointer; + // A sentinel type for non_random_iterator, used to test the + // advance(i, n, bound) overload where I != S (different types). + template + struct non_random_sentinel + { + non_random_sentinel() : ptr(nullptr) {} + non_random_sentinel(T* v) : ptr(v) {} + T* ptr; + }; + + template + bool operator==(const non_random_iterator& lhs, const non_random_sentinel& rhs) + { + return lhs.ptr == rhs.ptr; + } + + template + bool operator!=(const non_random_iterator& lhs, const non_random_sentinel& rhs) + { + return lhs.ptr != rhs.ptr; + } + + template + bool operator==(const non_random_sentinel& lhs, const non_random_iterator& rhs) + { + return rhs == lhs; + } + + template + bool operator!=(const non_random_sentinel& lhs, const non_random_iterator& rhs) + { + return rhs != lhs; + } + const size_t SIZE = 10UL; int dataA[SIZE] = { 2, 1, 4, 3, 6, 5, 8, 7, 10, 9 }; @@ -580,5 +615,780 @@ namespace // CHECK_EQUAL(expected.size(), output.size()); // CHECK(std::equal(output.begin(), output.end(), expected.begin())); //} + +#if ETL_USING_CPP17 + //************************************************************************* + TEST(test_counted_iterator) + { + std::vector v{1, 2, 3, 4}; + + etl::counted_iterator::iterator> ci0{}; + CHECK_EQUAL(0, ci0.count()); + + etl::counted_iterator ci1{v.begin(), 3}; + etl::counted_iterator ci2{v.begin(), 4}; + CHECK_EQUAL(3, ci1.count()); + + CHECK_NOT_EQUAL(ci1.count(), ci2.count()); + CHECK(!(ci1 == ci2)); + + ci2 = ci1; + + CHECK_EQUAL(ci1.count(), ci2.count()); + CHECK(ci1 == ci2); + + CHECK(ci1.base() == v.begin()); + + CHECK(ci1.count() == 3); + + ci1++; + + CHECK(ci1.count() == 2); + + ++ci1; + + CHECK(ci1.count() == 1); + + ci1--; + + CHECK(ci1.count() == 2); + --ci1; + CHECK(ci1.count() == 3); + + ci1 += 2; + CHECK(ci1.count() == 1); + ci1 -= 2; + CHECK(ci1.count() == 3); + CHECK_EQUAL(ci1[0], 1); + CHECK_EQUAL(ci1[1], 2); + CHECK_EQUAL(ci1[2], 3); + + auto ci3 = ci1 + 3; + CHECK(ci1.count() == 3); + CHECK_EQUAL(ci3.count(), 0); + auto ci4 = ci3 - 3; + CHECK(ci1.count() == 3); + CHECK_EQUAL(ci3.count(), 0); + CHECK(ci4.count() == 3); + + } + + //************************************************************************* + // etl::ranges::advance(i, n) with random access iterator + //************************************************************************* + TEST(ranges_advance_n_random_access_forward) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[0]; + etl::ranges::advance(itr, 4); + CHECK_EQUAL(4, *itr); + } + + //************************************************************************* + TEST(ranges_advance_n_random_access_backward) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[7]; + etl::ranges::advance(itr, -3); + CHECK_EQUAL(4, *itr); + } + + //************************************************************************* + TEST(ranges_advance_n_random_access_zero) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[5]; + etl::ranges::advance(itr, 0); + CHECK_EQUAL(5, *itr); + } + + //************************************************************************* + // etl::ranges::advance(i, n) with bidirectional (non-random) iterator + //************************************************************************* + TEST(ranges_advance_n_bidirectional_forward) + { + std::list data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto itr = data.begin(); + etl::ranges::advance(itr, 4); + CHECK_EQUAL(4, *itr); + } + + //************************************************************************* + TEST(ranges_advance_n_bidirectional_backward) + { + std::list data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto itr = data.end(); + etl::ranges::advance(itr, -3); + CHECK_EQUAL(7, *itr); + } + + //************************************************************************* + TEST(ranges_advance_n_bidirectional_zero) + { + std::list data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto itr = data.begin(); + etl::ranges::advance(itr, 5); + etl::ranges::advance(itr, 0); + CHECK_EQUAL(5, *itr); + } + + //************************************************************************* + TEST(ranges_advance_n_non_random_iterator_forward) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[0]); + etl::ranges::advance(itr, 6); + CHECK_EQUAL(6, *itr); + } + + //************************************************************************* + TEST(ranges_advance_n_non_random_iterator_backward) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[8]); + etl::ranges::advance(itr, -3); + CHECK_EQUAL(5, *itr); + } + + //************************************************************************* + // etl::ranges::advance(i, bound) — advance to sentinel + //************************************************************************* + TEST(ranges_advance_to_bound_random_access) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[0]; + int* bound = &data[7]; + etl::ranges::advance(itr, bound); + CHECK_EQUAL(7, *itr); + CHECK(itr == bound); + } + + //************************************************************************* + TEST(ranges_advance_to_bound_bidirectional) + { + std::list data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto itr = data.begin(); + auto bound = data.end(); + etl::ranges::advance(itr, bound); + CHECK(itr == bound); + } + + //************************************************************************* + TEST(ranges_advance_to_bound_non_random) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[0]); + non_random_iterator bound(&data[5]); + etl::ranges::advance(itr, bound); + CHECK_EQUAL(5, *itr); + } + + //************************************************************************* + TEST(ranges_advance_to_bound_already_at_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[3]; + int* bound = &data[3]; + etl::ranges::advance(itr, bound); + CHECK(itr == bound); + CHECK_EQUAL(3, *itr); + } + + //************************************************************************* + // etl::ranges::advance(i, n, bound) — advance with bound, returns remainder + //************************************************************************* + TEST(ranges_advance_n_bound_random_access_not_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[0]; + int* bound = &data[9]; + auto remaining = etl::ranges::advance(itr, 4, bound); + CHECK_EQUAL(4, *itr); + CHECK_EQUAL(0, remaining); + } + + //************************************************************************* + TEST(ranges_advance_n_bound_random_access_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[0]; + int* bound = &data[3]; + auto remaining = etl::ranges::advance(itr, 7, bound); + CHECK(itr == bound); + CHECK_EQUAL(3, *itr); + CHECK_EQUAL(4, remaining); + } + + //************************************************************************* + TEST(ranges_advance_n_bound_random_access_exact_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[0]; + int* bound = &data[5]; + auto remaining = etl::ranges::advance(itr, 5, bound); + CHECK(itr == bound); + CHECK_EQUAL(5, *itr); + CHECK_EQUAL(0, remaining); + } + + //************************************************************************* + TEST(ranges_advance_n_bound_random_access_backward_not_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[9]; + int* bound = &data[0]; + auto remaining = etl::ranges::advance(itr, -4, bound); + CHECK_EQUAL(5, *itr); + CHECK_EQUAL(0, remaining); + } + + //************************************************************************* + TEST(ranges_advance_n_bound_random_access_backward_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[5]; + int* bound = &data[2]; + auto remaining = etl::ranges::advance(itr, -7, bound); + CHECK(itr == bound); + CHECK_EQUAL(2, *itr); + CHECK_EQUAL(-4, remaining); + } + + //************************************************************************* + TEST(ranges_advance_n_bound_non_random_forward_not_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[0]); + non_random_sentinel bound(&data[9]); + auto remaining = etl::ranges::advance(itr, 4, bound); + CHECK_EQUAL(4, *itr); + CHECK_EQUAL(0, remaining); + } + + //************************************************************************* + TEST(ranges_advance_n_bound_non_random_forward_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[0]); + non_random_sentinel bound(&data[3]); + auto remaining = etl::ranges::advance(itr, 7, bound); + CHECK_EQUAL(3, *itr); + CHECK_EQUAL(4, remaining); + } + + //************************************************************************* + TEST(ranges_advance_n_bound_non_random_backward_not_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[9]); + non_random_sentinel bound(&data[0]); + auto remaining = etl::ranges::advance(itr, -3, bound); + CHECK_EQUAL(6, *itr); + CHECK_EQUAL(0, remaining); + } + + //************************************************************************* + TEST(ranges_advance_n_bound_non_random_backward_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[3]); + non_random_sentinel bound(&data[1]); + auto remaining = etl::ranges::advance(itr, -7, bound); + CHECK_EQUAL(1, *itr); + CHECK_EQUAL(-5, remaining); + } + + //************************************************************************* + TEST(ranges_advance_n_bound_zero_steps) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[5]; + int* bound = &data[8]; + auto remaining = etl::ranges::advance(itr, 0, bound); + CHECK_EQUAL(5, *itr); + CHECK_EQUAL(0, remaining); + } + + //************************************************************************* + // etl::ranges::prev(i) with random access iterator + //************************************************************************* + TEST(ranges_prev_random_access) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[5]; + int* result = etl::ranges::prev(itr); + CHECK_EQUAL(4, *result); + CHECK_EQUAL(5, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::prev(i) with bidirectional (non-random) iterator + //************************************************************************* + TEST(ranges_prev_non_random) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[5]); + non_random_iterator result = etl::ranges::prev(itr); + CHECK_EQUAL(4, *result); + CHECK_EQUAL(5, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::prev(i, n) with random access iterator — positive n + //************************************************************************* + TEST(ranges_prev_n_random_access_positive) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[7]; + int* result = etl::ranges::prev(itr, 3); + CHECK_EQUAL(4, *result); + CHECK_EQUAL(7, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::prev(i, n) with random access iterator — zero n + //************************************************************************* + TEST(ranges_prev_n_random_access_zero) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[5]; + int* result = etl::ranges::prev(itr, 0); + CHECK_EQUAL(5, *result); + } + + //************************************************************************* + // etl::ranges::prev(i, n) with random access iterator — negative n (moves forward) + //************************************************************************* + TEST(ranges_prev_n_random_access_negative) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[3]; + int* result = etl::ranges::prev(itr, -2); + CHECK_EQUAL(5, *result); + CHECK_EQUAL(3, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::prev(i, n) with bidirectional (non-random) iterator — positive n + //************************************************************************* + TEST(ranges_prev_n_non_random_positive) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[7]); + non_random_iterator result = etl::ranges::prev(itr, 4); + CHECK_EQUAL(3, *result); + CHECK_EQUAL(7, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::prev(i, n) with bidirectional (non-random) iterator — zero n + //************************************************************************* + TEST(ranges_prev_n_non_random_zero) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[5]); + non_random_iterator result = etl::ranges::prev(itr, 0); + CHECK_EQUAL(5, *result); + } + + //************************************************************************* + // etl::ranges::prev(i, n) with bidirectional (non-random) iterator — negative n (moves forward) + //************************************************************************* + TEST(ranges_prev_n_non_random_negative) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[3]); + non_random_iterator result = etl::ranges::prev(itr, -2); + CHECK_EQUAL(5, *result); + CHECK_EQUAL(3, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::prev(i, n, bound) with random access iterator — not reaching bound + //************************************************************************* + TEST(ranges_prev_n_bound_random_access_not_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[8]; + int* bound = &data[2]; + int* result = etl::ranges::prev(itr, 4, bound); + CHECK_EQUAL(4, *result); + } + + //************************************************************************* + // etl::ranges::prev(i, n, bound) with random access iterator — reaching bound + //************************************************************************* + TEST(ranges_prev_n_bound_random_access_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[5]; + int* bound = &data[3]; + int* result = etl::ranges::prev(itr, 7, bound); + CHECK_EQUAL(3, *result); + CHECK(result == bound); + } + + //************************************************************************* + // etl::ranges::prev(i, n, bound) with random access iterator — zero steps + //************************************************************************* + TEST(ranges_prev_n_bound_random_access_zero_steps) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[5]; + int* bound = &data[2]; + int* result = etl::ranges::prev(itr, 0, bound); + CHECK_EQUAL(5, *result); + } + + //************************************************************************* + // etl::ranges::prev(i, n, bound) with bidirectional (non-random) iterator — not reaching bound + //************************************************************************* + TEST(ranges_prev_n_bound_non_random_not_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[8]); + non_random_iterator bound(&data[2]); + non_random_iterator result = etl::ranges::prev(itr, 3, bound); + CHECK_EQUAL(5, *result); + } + + //************************************************************************* + // etl::ranges::prev(i, n, bound) with bidirectional (non-random) iterator — reaching bound + //************************************************************************* + TEST(ranges_prev_n_bound_non_random_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[5]); + non_random_iterator bound(&data[3]); + non_random_iterator result = etl::ranges::prev(itr, 7, bound); + CHECK_EQUAL(3, *result); + CHECK(result.ptr == bound.ptr); + } + + //************************************************************************* + // etl::ranges::next(i) with random access iterator + //************************************************************************* + TEST(ranges_next_random_access) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[5]; + int* result = etl::ranges::next(itr); + CHECK_EQUAL(6, *result); + CHECK_EQUAL(5, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::next(i) with bidirectional (non-random) iterator + //************************************************************************* + TEST(ranges_next_non_random) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[5]); + non_random_iterator result = etl::ranges::next(itr); + CHECK_EQUAL(6, *result); + CHECK_EQUAL(5, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::next(i, n) with random access iterator — positive n + //************************************************************************* + TEST(ranges_next_n_random_access_positive) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[2]; + int* result = etl::ranges::next(itr, 5); + CHECK_EQUAL(7, *result); + CHECK_EQUAL(2, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::next(i, n) with random access iterator — zero n + //************************************************************************* + TEST(ranges_next_n_random_access_zero) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[5]; + int* result = etl::ranges::next(itr, 0); + CHECK_EQUAL(5, *result); + } + + //************************************************************************* + // etl::ranges::next(i, n) with random access iterator — negative n (moves backward) + //************************************************************************* + TEST(ranges_next_n_random_access_negative) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[7]; + int* result = etl::ranges::next(itr, -3); + CHECK_EQUAL(4, *result); + CHECK_EQUAL(7, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::next(i, n) with bidirectional (non-random) iterator — positive n + //************************************************************************* + TEST(ranges_next_n_non_random_positive) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[1]); + non_random_iterator result = etl::ranges::next(itr, 4); + CHECK_EQUAL(5, *result); + CHECK_EQUAL(1, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::next(i, n) with bidirectional (non-random) iterator — zero n + //************************************************************************* + TEST(ranges_next_n_non_random_zero) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[5]); + non_random_iterator result = etl::ranges::next(itr, 0); + CHECK_EQUAL(5, *result); + } + + //************************************************************************* + // etl::ranges::next(i, n) with bidirectional (non-random) iterator — negative n (moves backward) + //************************************************************************* + TEST(ranges_next_n_non_random_negative) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[7]); + non_random_iterator result = etl::ranges::next(itr, -3); + CHECK_EQUAL(4, *result); + CHECK_EQUAL(7, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::next(i, bound) with random access iterator + //************************************************************************* + TEST(ranges_next_bound_random_access) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[0]; + int* bound = &data[7]; + int* result = etl::ranges::next(itr, bound); + CHECK_EQUAL(7, *result); + CHECK(result == bound); + CHECK_EQUAL(0, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::next(i, bound) with non-random iterator + //************************************************************************* + TEST(ranges_next_bound_non_random) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[0]); + non_random_sentinel bound(&data[5]); + non_random_iterator result = etl::ranges::next(itr, bound); + CHECK_EQUAL(5, *result); + CHECK_EQUAL(0, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::next(i, bound) already at bound + //************************************************************************* + TEST(ranges_next_bound_already_at_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[4]; + int* bound = &data[4]; + int* result = etl::ranges::next(itr, bound); + CHECK(result == bound); + CHECK_EQUAL(4, *result); + } + + //************************************************************************* + // etl::ranges::next(i, n, bound) with random access iterator — not reaching bound + //************************************************************************* + TEST(ranges_next_n_bound_random_access_not_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[0]; + int* bound = &data[9]; + int* result = etl::ranges::next(itr, 4, bound); + CHECK_EQUAL(4, *result); + CHECK_EQUAL(0, *itr); // original unchanged + } + + //************************************************************************* + // etl::ranges::next(i, n, bound) with random access iterator — reaching bound + //************************************************************************* + TEST(ranges_next_n_bound_random_access_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[0]; + int* bound = &data[3]; + int* result = etl::ranges::next(itr, 7, bound); + CHECK(result == bound); + CHECK_EQUAL(3, *result); + } + + //************************************************************************* + // etl::ranges::next(i, n, bound) with random access iterator — exact bound + //************************************************************************* + TEST(ranges_next_n_bound_random_access_exact_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[0]; + int* bound = &data[5]; + int* result = etl::ranges::next(itr, 5, bound); + CHECK(result == bound); + CHECK_EQUAL(5, *result); + } + + //************************************************************************* + // etl::ranges::next(i, n, bound) with random access iterator — backward not reaching bound + //************************************************************************* + TEST(ranges_next_n_bound_random_access_backward_not_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[9]; + int* bound = &data[0]; + int* result = etl::ranges::next(itr, -4, bound); + CHECK_EQUAL(5, *result); + } + + //************************************************************************* + // etl::ranges::next(i, n, bound) with random access iterator — backward reaching bound + //************************************************************************* + TEST(ranges_next_n_bound_random_access_backward_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[5]; + int* bound = &data[2]; + int* result = etl::ranges::next(itr, -7, bound); + CHECK(result == bound); + CHECK_EQUAL(2, *result); + } + + //************************************************************************* + // etl::ranges::next(i, n, bound) with random access iterator — zero steps + //************************************************************************* + TEST(ranges_next_n_bound_random_access_zero_steps) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* itr = &data[5]; + int* bound = &data[8]; + int* result = etl::ranges::next(itr, 0, bound); + CHECK_EQUAL(5, *result); + } + + //************************************************************************* + // etl::ranges::next(i, n, bound) with non-random iterator — forward not reaching bound + //************************************************************************* + TEST(ranges_next_n_bound_non_random_forward_not_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[0]); + non_random_sentinel bound(&data[9]); + non_random_iterator result = etl::ranges::next(itr, 4, bound); + CHECK_EQUAL(4, *result); + } + + //************************************************************************* + // etl::ranges::next(i, n, bound) with non-random iterator — forward reaching bound + //************************************************************************* + TEST(ranges_next_n_bound_non_random_forward_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[0]); + non_random_sentinel bound(&data[3]); + non_random_iterator result = etl::ranges::next(itr, 7, bound); + CHECK_EQUAL(3, *result); + } + + //************************************************************************* + // etl::ranges::next(i, n, bound) with non-random iterator — backward not reaching bound + //************************************************************************* + TEST(ranges_next_n_bound_non_random_backward_not_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[9]); + non_random_sentinel bound(&data[0]); + non_random_iterator result = etl::ranges::next(itr, -3, bound); + CHECK_EQUAL(6, *result); + } + + //************************************************************************* + // etl::ranges::next(i, n, bound) with non-random iterator — backward reaching bound + //************************************************************************* + TEST(ranges_next_n_bound_non_random_backward_reaching_bound) + { + int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + non_random_iterator itr(&data[3]); + non_random_sentinel bound(&data[1]); + non_random_iterator result = etl::ranges::next(itr, -7, bound); + CHECK_EQUAL(1, *result); + } + + TEST(test_is_range) + { + std::vector vec; + int arr[3]{}; + int i{}; + static_assert(etl::is_range_v == true, "Expected range"); + static_assert(etl::is_range_v == true, "Expected range"); + static_assert(etl::is_range_v == false, "Expected non range"); + } + +#endif } } diff --git a/test/test_memory.cpp b/test/test_memory.cpp index 59e66e3a..dd704a4a 100644 --- a/test/test_memory.cpp +++ b/test/test_memory.cpp @@ -32,6 +32,7 @@ SOFTWARE. #include "etl/list.h" #include "etl/debug_count.h" #include "etl/endianness.h" +#include "etl/span.h" #include "data.h" @@ -40,6 +41,7 @@ SOFTWARE. #include #include #include +#include #include #include #include @@ -69,9 +71,9 @@ namespace non_trivial_t test_item_non_trivial_null(""); trivial_t test_item_trivial(0xBBCCDDEEUL); - char buffer_non_trivial[sizeof(non_trivial_t) * SIZE]; - char buffer_trivial[sizeof(trivial_t) * SIZE]; - char buffer_moveable[sizeof(moveable_t) * SIZE]; + alignas(non_trivial_t) unsigned char buffer_non_trivial[sizeof(non_trivial_t) * SIZE]; + alignas(trivial_t) unsigned char buffer_trivial[sizeof(trivial_t) * SIZE]; + alignas(moveable_t) unsigned char buffer_moveable[sizeof(moveable_t) * SIZE]; non_trivial_t* output_non_trivial = reinterpret_cast(buffer_non_trivial); trivial_t* output_trivial = reinterpret_cast(buffer_trivial); @@ -184,7 +186,7 @@ namespace //************************************************************************* TEST(test_create_destroy_non_trivial) { - char n[sizeof(non_trivial_t)]; + alignas(non_trivial_t) unsigned char n[sizeof(non_trivial_t)]; non_trivial_t* pn = reinterpret_cast(n); // Non count. @@ -416,7 +418,7 @@ namespace // Count. size_t count = 0UL; - std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + std::fill(std::begin(buffer_moveable), std::end(buffer_moveable), 0); { std::array test_data_moveable = @@ -481,7 +483,7 @@ namespace // Count. size_t count = 0UL; - std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + std::fill(std::begin(buffer_moveable), std::end(buffer_moveable), 0); { std::array test_data_moveable = @@ -646,7 +648,7 @@ namespace std::string text; }; - char buffer[sizeof(Object)]; + alignas(Object) unsigned char buffer[sizeof(Object)]; Object object1; object1.text = "12345678"; @@ -672,7 +674,7 @@ namespace std::string text; }; - char buffer[sizeof(Object)]; + alignas(Object) unsigned char buffer[sizeof(Object)]; Object object1; object1.text = "12345678"; @@ -1699,5 +1701,1048 @@ namespace CHECK_EQUAL(&i, etl::to_address(pi)); CHECK_EQUAL(plist_item, etl::to_address(itr)); } + +#if ETL_USING_CPP17 + //************************************************************************* + TEST(test_ranges_uninitialized_copy_iterator_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0); + + auto result = etl::ranges::uninitialized_copy(test_data_trivial.begin(), test_data_trivial.end(), p, p + SIZE); + + bool is_equal = std::equal(output_trivial, output_trivial + SIZE, test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == test_data_trivial.end()); + CHECK(result.out == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_copy_iterator_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + auto result = etl::ranges::uninitialized_copy(test_data_non_trivial.begin(), test_data_non_trivial.end(), p, p + SIZE); + + bool is_equal = std::equal(output_non_trivial, output_non_trivial + SIZE, test_data_non_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == test_data_non_trivial.end()); + CHECK(result.out == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_copy_range_trivial) + { + trivial_t dst[SIZE] = {}; + + auto result = etl::ranges::uninitialized_copy(test_data_trivial, dst); + + bool is_equal = std::equal(std::begin(dst), std::end(dst), test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == test_data_trivial.end()); + CHECK(result.out == std::end(dst)); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_copy_range_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + std::vector src(test_data_non_trivial.begin(), test_data_non_trivial.end()); + etl::span dst(p, SIZE); + + auto result = etl::ranges::uninitialized_copy(src, dst); + + bool is_equal = std::equal(output_non_trivial, output_non_trivial + SIZE, test_data_non_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_copy_output_shorter) + { + // Output range is shorter than input; should stop at output end. + std::array small_dst = {}; + + auto result = etl::ranges::uninitialized_copy(test_data_trivial.begin(), test_data_trivial.end(), + small_dst.begin(), small_dst.end()); + + bool is_equal = std::equal(small_dst.begin(), small_dst.end(), test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == test_data_trivial.begin() + 5); + CHECK(result.out == small_dst.end()); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_copy_input_shorter) + { + // Input range is shorter than output; should stop at input end. + std::array small_src = {1, 2, 3}; + trivial_t dst[SIZE] = {}; + + auto result = etl::ranges::uninitialized_copy(small_src.begin(), small_src.end(), + std::begin(dst), std::end(dst)); + + CHECK_EQUAL(1U, dst[0]); + CHECK_EQUAL(2U, dst[1]); + CHECK_EQUAL(3U, dst[2]); + CHECK(result.in == small_src.end()); + CHECK(result.out == std::begin(dst) + 3); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_copy_empty) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + auto result = etl::ranges::uninitialized_copy(test_data_trivial.begin(), test_data_trivial.begin(), p, p + SIZE); + + CHECK(result.in == test_data_trivial.begin()); + CHECK(result.out == p); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_copy_n_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0); + + auto result = etl::ranges::uninitialized_copy_n(test_data_trivial.begin(), SIZE, p, p + SIZE); + + bool is_equal = std::equal(output_trivial, output_trivial + SIZE, test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == test_data_trivial.end()); + CHECK(result.out == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_copy_n_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + auto result = etl::ranges::uninitialized_copy_n(test_data_non_trivial.begin(), SIZE, p, p + SIZE); + + bool is_equal = std::equal(output_non_trivial, output_non_trivial + SIZE, test_data_non_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == test_data_non_trivial.end()); + CHECK(result.out == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_copy_n_output_shorter) + { + // Output range is shorter than count; should stop at output end. + std::array small_dst = {}; + + auto result = etl::ranges::uninitialized_copy_n(test_data_trivial.begin(), SIZE, + small_dst.begin(), small_dst.end()); + + bool is_equal = std::equal(small_dst.begin(), small_dst.end(), test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == test_data_trivial.begin() + 5); + CHECK(result.out == small_dst.end()); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_copy_n_count_shorter) + { + // Count is shorter than output range; should stop after count elements. + trivial_t dst[SIZE] = {}; + + auto result = etl::ranges::uninitialized_copy_n(test_data_trivial.begin(), 3, + std::begin(dst), std::end(dst)); + + bool is_equal = std::equal(std::begin(dst), std::begin(dst) + 3, test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == test_data_trivial.begin() + 3); + CHECK(result.out == std::begin(dst) + 3); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_copy_n_zero_count) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + auto result = etl::ranges::uninitialized_copy_n(test_data_trivial.begin(), 0, p, p + SIZE); + + CHECK(result.in == test_data_trivial.begin()); + CHECK(result.out == p); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_fill_iterator_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0); + + auto result = etl::ranges::uninitialized_fill(p, p + SIZE, test_item_trivial); + + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(test_item_trivial, p[i]); + } + CHECK(result == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_fill_iterator_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + auto result = etl::ranges::uninitialized_fill(p, p + SIZE, test_item_non_trivial); + + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(test_item_non_trivial, p[i]); + } + CHECK(result == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_fill_range_trivial) + { + trivial_t dst[SIZE] = {}; + + auto result = etl::ranges::uninitialized_fill(dst, test_item_trivial); + + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(test_item_trivial, dst[i]); + } + CHECK(result == std::end(dst)); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_fill_range_non_trivial) + { + alignas(non_trivial_t) unsigned char buffer[sizeof(non_trivial_t) * SIZE]; + non_trivial_t* p = reinterpret_cast(buffer); + etl::span dst(p, SIZE); + + auto result = etl::ranges::uninitialized_fill(dst, test_item_non_trivial); + + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(test_item_non_trivial, p[i]); + } + CHECK(result == dst.end()); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_fill_empty) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + auto result = etl::ranges::uninitialized_fill(p, p, test_item_trivial); + + CHECK(result == p); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_fill_n_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0); + + auto result = etl::ranges::uninitialized_fill_n(p, SIZE, test_item_trivial); + + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(test_item_trivial, p[i]); + } + CHECK(result == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_fill_n_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + auto result = etl::ranges::uninitialized_fill_n(p, SIZE, test_item_non_trivial); + + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(test_item_non_trivial, p[i]); + } + CHECK(result == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_fill_n_partial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0); + + auto result = etl::ranges::uninitialized_fill_n(p, 3, test_item_trivial); + + for (size_t i = 0; i < 3; ++i) + { + CHECK_EQUAL(test_item_trivial, p[i]); + } + CHECK(result == p + 3); + + etl::destroy(p, p + 3); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_fill_n_zero_count) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + auto result = etl::ranges::uninitialized_fill_n(p, 0, test_item_trivial); + + CHECK(result == p); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_iterator_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0); + + std::array src(test_data_trivial); + + auto result = etl::ranges::uninitialized_move(src.begin(), src.end(), p, p + SIZE); + + bool is_equal = std::equal(output_trivial, output_trivial + SIZE, test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == src.end()); + CHECK(result.out == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_iterator_non_trivial) + { + moveable_t* p = reinterpret_cast(buffer_moveable); + + std::fill(std::begin(buffer_moveable), std::end(buffer_moveable), 0); + + std::array src = + { + moveable_t(0), moveable_t(1), moveable_t(2), moveable_t(3), moveable_t(4), + moveable_t(5), moveable_t(6), moveable_t(7), moveable_t(8), moveable_t(9) + }; + + auto result = etl::ranges::uninitialized_move(src.begin(), src.end(), p, p + SIZE); + + bool is_equal = (output_moveable[0] == moveable_t(0)) && + (output_moveable[1] == moveable_t(1)) && + (output_moveable[2] == moveable_t(2)) && + (output_moveable[3] == moveable_t(3)) && + (output_moveable[4] == moveable_t(4)) && + (output_moveable[5] == moveable_t(5)) && + (output_moveable[6] == moveable_t(6)) && + (output_moveable[7] == moveable_t(7)) && + (output_moveable[8] == moveable_t(8)) && + (output_moveable[9] == moveable_t(9)); + + CHECK(is_equal); + + // Source elements should have been moved from (invalidated). + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(false, bool(src[i])); + } + + CHECK(result.in == src.end()); + CHECK(result.out == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_range_trivial) + { + trivial_t dst[SIZE] = {}; + + std::array src(test_data_trivial); + + auto result = etl::ranges::uninitialized_move(src, dst); + + bool is_equal = std::equal(std::begin(dst), std::end(dst), test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == src.end()); + CHECK(result.out == std::end(dst)); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_range_non_trivial) + { + moveable_t* p = reinterpret_cast(buffer_moveable); + + std::fill(std::begin(buffer_moveable), std::end(buffer_moveable), 0); + + std::array src = + { + moveable_t(0), moveable_t(1), moveable_t(2), moveable_t(3), moveable_t(4), + moveable_t(5), moveable_t(6), moveable_t(7), moveable_t(8), moveable_t(9) + }; + + etl::span dst(p, SIZE); + + auto result = etl::ranges::uninitialized_move(src, dst); + + bool is_equal = (output_moveable[0] == moveable_t(0)) && + (output_moveable[1] == moveable_t(1)) && + (output_moveable[2] == moveable_t(2)) && + (output_moveable[3] == moveable_t(3)) && + (output_moveable[4] == moveable_t(4)) && + (output_moveable[5] == moveable_t(5)) && + (output_moveable[6] == moveable_t(6)) && + (output_moveable[7] == moveable_t(7)) && + (output_moveable[8] == moveable_t(8)) && + (output_moveable[9] == moveable_t(9)); + + CHECK(is_equal); + + // Source elements should have been moved from (invalidated). + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(false, bool(src[i])); + } + + CHECK(result.in == src.end()); + CHECK(result.out == dst.end()); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_output_shorter) + { + // Output range is shorter than input; should stop at output end. + std::array small_dst = {}; + + std::array src(test_data_trivial); + + auto result = etl::ranges::uninitialized_move(src.begin(), src.end(), + small_dst.begin(), small_dst.end()); + + bool is_equal = std::equal(small_dst.begin(), small_dst.end(), test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == src.begin() + 5); + CHECK(result.out == small_dst.end()); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_input_shorter) + { + // Input range is shorter than output; should stop at input end. + std::array small_src = {1, 2, 3}; + trivial_t dst[SIZE] = {}; + + auto result = etl::ranges::uninitialized_move(small_src.begin(), small_src.end(), + std::begin(dst), std::end(dst)); + + CHECK_EQUAL(1U, dst[0]); + CHECK_EQUAL(2U, dst[1]); + CHECK_EQUAL(3U, dst[2]); + CHECK(result.in == small_src.end()); + CHECK(result.out == std::begin(dst) + 3); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_empty) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::array src(test_data_trivial); + + auto result = etl::ranges::uninitialized_move(src.begin(), src.begin(), p, p + SIZE); + + CHECK(result.in == src.begin()); + CHECK(result.out == p); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_n_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0); + + std::array src(test_data_trivial); + + auto result = etl::ranges::uninitialized_move_n(src.begin(), SIZE, p, p + SIZE); + + bool is_equal = std::equal(output_trivial, output_trivial + SIZE, test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == src.end()); + CHECK(result.out == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_n_non_trivial) + { + moveable_t* p = reinterpret_cast(buffer_moveable); + + std::fill(std::begin(buffer_moveable), std::end(buffer_moveable), 0); + + std::array src = + { + moveable_t(0), moveable_t(1), moveable_t(2), moveable_t(3), moveable_t(4), + moveable_t(5), moveable_t(6), moveable_t(7), moveable_t(8), moveable_t(9) + }; + + auto result = etl::ranges::uninitialized_move_n(src.begin(), SIZE, p, p + SIZE); + + bool is_equal = (output_moveable[0] == moveable_t(0)) && + (output_moveable[1] == moveable_t(1)) && + (output_moveable[2] == moveable_t(2)) && + (output_moveable[3] == moveable_t(3)) && + (output_moveable[4] == moveable_t(4)) && + (output_moveable[5] == moveable_t(5)) && + (output_moveable[6] == moveable_t(6)) && + (output_moveable[7] == moveable_t(7)) && + (output_moveable[8] == moveable_t(8)) && + (output_moveable[9] == moveable_t(9)); + + CHECK(is_equal); + + // Source elements should have been moved from (invalidated). + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(false, bool(src[i])); + } + + CHECK(result.in == src.end()); + CHECK(result.out == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_n_output_shorter) + { + // Output range is shorter than count; should stop at output end. + std::array small_dst = {}; + + std::array src(test_data_trivial); + + auto result = etl::ranges::uninitialized_move_n(src.begin(), SIZE, + small_dst.begin(), small_dst.end()); + + bool is_equal = std::equal(small_dst.begin(), small_dst.end(), test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == src.begin() + 5); + CHECK(result.out == small_dst.end()); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_n_count_shorter) + { + // Count is shorter than output range; should stop after count elements. + trivial_t dst[SIZE] = {}; + + std::array src(test_data_trivial); + + auto result = etl::ranges::uninitialized_move_n(src.begin(), 3, + std::begin(dst), std::end(dst)); + + bool is_equal = std::equal(std::begin(dst), std::begin(dst) + 3, test_data_trivial.begin()); + CHECK(is_equal); + CHECK(result.in == src.begin() + 3); + CHECK(result.out == std::begin(dst) + 3); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_move_n_zero_count) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::array src(test_data_trivial); + + auto result = etl::ranges::uninitialized_move_n(src.begin(), 0, p, p + SIZE); + + CHECK(result.in == src.begin()); + CHECK(result.out == p); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_default_construct_iterator_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFU); + unsigned char snapshot[sizeof(buffer_trivial)]; + std::memcpy(snapshot, buffer_trivial, sizeof(buffer_trivial)); + + auto result = etl::ranges::uninitialized_default_construct(p, p + SIZE); + + CHECK(result == p + SIZE); + // For trivial types default construction is a no-op; raw storage must be unchanged. + CHECK(std::memcmp(buffer_trivial, snapshot, sizeof(buffer_trivial)) == 0); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_default_construct_iterator_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + auto result = etl::ranges::uninitialized_default_construct(p, p + SIZE); + + CHECK(result == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_default_construct_range_trivial) + { + alignas(trivial_t) unsigned char buf[sizeof(trivial_t) * SIZE]; + std::fill(std::begin(buf), std::end(buf), 0xFFu); + unsigned char snapshot[sizeof(buf)]; + std::memcpy(snapshot, buf, sizeof(buf)); + trivial_t* p = reinterpret_cast(buf); + etl::span dst(p, SIZE); + + auto result = etl::ranges::uninitialized_default_construct(dst); + + // For trivial types, default construction is a no-op, but the + // returned iterator must point past the last element. + CHECK(result == dst.end()); + // Raw storage must be unchanged. + CHECK(std::memcmp(buf, snapshot, sizeof(buf)) == 0); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_default_construct_range_non_trivial) + { + alignas(non_trivial_t) unsigned char buffer[sizeof(non_trivial_t) * SIZE]; + non_trivial_t* p = reinterpret_cast(buffer); + etl::span dst(p, SIZE); + + auto result = etl::ranges::uninitialized_default_construct(dst); + + CHECK(result == dst.end()); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_default_construct_empty) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + auto result = etl::ranges::uninitialized_default_construct(p, p); + + CHECK(result == p); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_default_construct_n_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFU); + unsigned char snapshot[sizeof(buffer_trivial)]; + std::memcpy(snapshot, buffer_trivial, sizeof(buffer_trivial)); + + auto result = etl::ranges::uninitialized_default_construct_n(p, SIZE); + + CHECK(result == p + SIZE); + // For trivial types default construction is a no-op; raw storage must be unchanged. + CHECK(std::memcmp(buffer_trivial, snapshot, sizeof(buffer_trivial)) == 0); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_default_construct_n_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + auto result = etl::ranges::uninitialized_default_construct_n(p, SIZE); + + CHECK(result == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_default_construct_n_partial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFU); + unsigned char snapshot[sizeof(buffer_trivial)]; + std::memcpy(snapshot, buffer_trivial, sizeof(buffer_trivial)); + + auto result = etl::ranges::uninitialized_default_construct_n(p, 3); + + CHECK(result == p + 3); + // For trivial types default construction is a no-op; raw storage must be unchanged. + CHECK(std::memcmp(buffer_trivial, snapshot, sizeof(buffer_trivial)) == 0); + + etl::destroy(p, p + 3); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_default_construct_n_zero_count) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + auto result = etl::ranges::uninitialized_default_construct_n(p, 0); + + CHECK(result == p); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_value_construct_iterator_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFu); + + auto result = etl::ranges::uninitialized_value_construct(p, p + SIZE); + + CHECK(result == p + SIZE); + + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(trivial_t(), p[i]); + } + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_value_construct_iterator_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + auto result = etl::ranges::uninitialized_value_construct(p, p + SIZE); + + CHECK(result == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_value_construct_range_trivial) + { + alignas(trivial_t) unsigned char buf[sizeof(trivial_t) * SIZE]; + std::fill(std::begin(buf), std::end(buf), 0xFFu); + trivial_t* p = reinterpret_cast(buf); + etl::span dst(p, SIZE); + + auto result = etl::ranges::uninitialized_value_construct(dst); + + CHECK(result == dst.end()); + + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(trivial_t(), p[i]); + } + } + + //************************************************************************* + TEST(test_ranges_uninitialized_value_construct_range_non_trivial) + { + alignas(non_trivial_t) unsigned char buffer[sizeof(non_trivial_t) * SIZE]; + non_trivial_t* p = reinterpret_cast(buffer); + etl::span dst(p, SIZE); + + auto result = etl::ranges::uninitialized_value_construct(dst); + + CHECK(result == dst.end()); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_value_construct_empty) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + auto result = etl::ranges::uninitialized_value_construct(p, p); + + CHECK(result == p); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_value_construct_n_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFu); + + auto result = etl::ranges::uninitialized_value_construct_n(p, SIZE); + + CHECK(result == p + SIZE); + + for (size_t i = 0; i < SIZE; ++i) + { + CHECK_EQUAL(trivial_t(), p[i]); + } + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_value_construct_n_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + auto result = etl::ranges::uninitialized_value_construct_n(p, SIZE); + + CHECK(result == p + SIZE); + + etl::destroy(p, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_value_construct_n_partial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0xFFu); + + auto result = etl::ranges::uninitialized_value_construct_n(p, 3); + + CHECK(result == p + 3); + + for (size_t i = 0; i < 3; ++i) + { + CHECK_EQUAL(trivial_t(), p[i]); + } + + etl::destroy(p, p + 3); + } + + //************************************************************************* + TEST(test_ranges_uninitialized_value_construct_n_zero_count) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + auto result = etl::ranges::uninitialized_value_construct_n(p, 0); + + CHECK(result == p); + } + + //************************************************************************* + TEST(test_ranges_construct_at_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + etl::ranges::construct_at(p, test_item_trivial); + CHECK_EQUAL(test_item_trivial, *p); + + etl::destroy_at(p); + } + + //************************************************************************* + TEST(test_ranges_construct_at_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + etl::ranges::construct_at(p, test_item_non_trivial); + CHECK_EQUAL(test_item_non_trivial, *p); + + etl::destroy_at(p); + } + + //************************************************************************* + TEST(test_ranges_construct_at_default) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + etl::ranges::construct_at(p); + CHECK_EQUAL(trivial_t(), *p); + + etl::destroy_at(p); + } + + //************************************************************************* + TEST(test_ranges_destroy_at_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + etl::construct_at(p, test_item_trivial); + CHECK_EQUAL(test_item_trivial, *p); + + etl::ranges::destroy_at(p); + } + + //************************************************************************* + TEST(test_ranges_destroy_at_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + etl::construct_at(p, test_item_non_trivial); + CHECK_EQUAL(test_item_non_trivial, *p); + + etl::ranges::destroy_at(p); + } + + //************************************************************************* + TEST(test_ranges_destroy_iterator_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0); + + etl::uninitialized_copy(test_data_trivial.begin(), test_data_trivial.end(), p); + + auto result = etl::ranges::destroy(p, p + SIZE); + + CHECK(result == p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_destroy_iterator_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + etl::uninitialized_copy(test_data_non_trivial.begin(), test_data_non_trivial.end(), p); + + auto result = etl::ranges::destroy(p, p + SIZE); + + CHECK(result == p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_destroy_range_trivial) + { + std::array dst; + std::copy(test_data_trivial.begin(), test_data_trivial.end(), dst.begin()); + + auto result = etl::ranges::destroy(dst); + + CHECK(result == dst.end()); + } + + //************************************************************************* + TEST(test_ranges_destroy_range_non_trivial) + { + alignas(non_trivial_t) unsigned char buffer[sizeof(non_trivial_t) * SIZE]; + non_trivial_t* p = reinterpret_cast(buffer); + etl::span dst(p, SIZE); + + etl::uninitialized_copy(test_data_non_trivial.begin(), test_data_non_trivial.end(), p); + + auto result = etl::ranges::destroy(dst); + + CHECK(result == dst.end()); + } + + //************************************************************************* + TEST(test_ranges_destroy_empty) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + auto result = etl::ranges::destroy(p, p); + + CHECK(result == p); + } + + //************************************************************************* + TEST(test_ranges_destroy_n_trivial) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + std::fill(std::begin(buffer_trivial), std::end(buffer_trivial), 0); + + etl::uninitialized_copy(test_data_trivial.begin(), test_data_trivial.end(), p); + + auto result = etl::ranges::destroy_n(p, SIZE); + + CHECK(result == p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_destroy_n_non_trivial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + etl::uninitialized_copy(test_data_non_trivial.begin(), test_data_non_trivial.end(), p); + + auto result = etl::ranges::destroy_n(p, SIZE); + + CHECK(result == p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_destroy_n_partial) + { + non_trivial_t* p = reinterpret_cast(buffer_non_trivial); + + std::fill(std::begin(buffer_non_trivial), std::end(buffer_non_trivial), 0); + + etl::uninitialized_copy(test_data_non_trivial.begin(), test_data_non_trivial.end(), p); + + auto result = etl::ranges::destroy_n(p, 3); + + CHECK(result == p + 3); + + // Clean up the rest + etl::destroy(p + 3, p + SIZE); + } + + //************************************************************************* + TEST(test_ranges_destroy_n_zero_count) + { + trivial_t* p = reinterpret_cast(buffer_trivial); + + auto result = etl::ranges::destroy_n(p, 0); + + CHECK(result == p); + } +#endif } } diff --git a/test/test_ranges.cpp b/test/test_ranges.cpp new file mode 100644 index 00000000..3ef6cf96 --- /dev/null +++ b/test/test_ranges.cpp @@ -0,0 +1,5516 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2026 BMW AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "etl/ranges.h" +#include "etl/vector.h" + +#include +#include +#include +#include +#include + +#if ETL_USING_CPP17 + +// C++03 does not support move semantics as used in the ranges library +#if !defined(ETL_FORCE_TEST_CPP03_IMPLEMENTATION) + +namespace +{ + class MoveInt + { + public: + MoveInt(int v): _v{v} + { + } + + MoveInt(const MoveInt& other): _v{other._v} + { + } + + MoveInt& operator=(const MoveInt& other) + { + _v = other._v; + return *this; + } + + MoveInt(MoveInt&& other): _v{other._v} + { + other._v = 0; + } + + MoveInt& operator=(MoveInt&& other) + { + _v = other._v; + other._v = 0; + return *this; + } + + int get() const + { + return _v; + } + + bool operator==(const MoveInt& other) const + { + return _v == other._v; + } + + bool operator!=(const MoveInt& other) const + { + return !(*this == other); + } + + private: + int _v; + }; + +} + +namespace std +{ +template, Container> || + etl::is_base_of_v, Container> || + etl::is_base_of_v, Container> + , int> = 0> +std::basic_ostream& operator<<(std::basic_ostream& s, const Container& v) +{ + auto it = v.cbegin(); + auto e = v.cend(); + bool first = true; + for (; it != e; ++it) + { + if (!first) + { + s << " "; + } + s << *it; + first = false; + } + return s; +} + +std::ostringstream& operator<<(std::ostringstream& s, const etl::ivector& v) +{ + auto it = v.cbegin(); + auto e = v.cend(); + bool first = true; + for (; it != e; ++it) + { + if (!first) + { + s << " "; + } + s << it->get(); + first = false; + } + return s; +} + +template +std::ostringstream& operator<<(std::ostringstream& s, const etl::ranges::range_iterator& v) +{ + auto value{v.get()}; + s << value; + return s; +} +} + +namespace +{ + SUITE(test_ranges) + { + //************************************************************************* + // Iterators. + //************************************************************************* + TEST(test_ranges_begin) + { + etl::vector v_in{ 3, 2, 1 }; + + auto it = etl::ranges::begin(v_in); + + CHECK_EQUAL(*it, 3); + } + + TEST(test_ranges_end) + { + etl::vector v_in{ 3, 2, 1 }; + + auto it = etl::ranges::end(v_in); + + CHECK_EQUAL(it, ETL_OR_STD::end(v_in)); + } + + TEST(test_ranges_cbegin) + { + etl::vector v_in{ 3, 2, 1 }; + + auto it = etl::ranges::cbegin(v_in); + + CHECK_EQUAL(*it, 3); + } + + TEST(test_ranges_cend) + { + etl::vector v_in{ 3, 2, 1 }; + + auto it = etl::ranges::cend(v_in); + + CHECK_EQUAL(it, ETL_OR_STD::cend(v_in)); + } + + TEST(test_ranges_rbegin) + { + etl::vector v_in{ 3, 2, 1 }; + + auto it = etl::ranges::rbegin(v_in); + + CHECK_EQUAL(*it, 1); + } + + TEST(test_ranges_rend) + { + etl::vector v_in{ 3, 2, 1 }; + + auto it = etl::ranges::rend(v_in); + + CHECK_EQUAL(&(*it), &(*ETL_OR_STD::rend(v_in))); + } + + TEST(test_ranges_crbegin) + { + etl::vector v_in{ 3, 2, 1 }; + + auto it = etl::ranges::crbegin(v_in); + + CHECK_EQUAL(*it, 1); + } + + TEST(test_ranges_crend) + { + etl::vector v_in{ 3, 2, 1 }; + + auto it = etl::ranges::crend(v_in); + + CHECK(it == ETL_OR_STD::crend(v_in)); + } + + TEST(test_ranges_size) + { + etl::vector v_in{ 11, 3, 2, 1 }; + + CHECK_EQUAL(etl::ranges::size(v_in), 4); + + using size_type = decltype(etl::ranges::size(v_in)); + static_assert(etl::is_signed::value == false, "Result of size must be unsigned"); + } + + TEST(test_ranges_ssize) + { + etl::vector v_in{ 11, 3, 2, 1 }; + + CHECK_EQUAL(etl::ranges::ssize(v_in), 4); + + using signed_type = decltype(etl::ranges::ssize(v_in)); + using unsigned_type = decltype(etl::ranges::size(v_in)); + static_assert(etl::is_signed::value, "Result of ssize must be signed"); + static_assert(sizeof(signed_type) >= sizeof(unsigned_type), "Signed size type needs to be as wide as unsigned size type"); + } + + TEST(test_ranges_empty) + { + etl::vector v_in0{ 11, 3, 2, 1 }; + etl::vector v_in1{ }; + + CHECK_EQUAL(etl::ranges::empty(v_in0), false); + CHECK_EQUAL(etl::ranges::empty(v_in1), true); + } + + TEST(test_ranges_data) + { + etl::vector v_in{ 11, 3, 2, 1 }; + + CHECK_EQUAL(*etl::ranges::data(v_in), 11); + } + + TEST(test_ranges_cdata) + { + etl::vector v_in{ 11, 3, 2, 1 }; + + CHECK_EQUAL(*etl::ranges::cdata(v_in), 11); + } + + //************************************************************************* + // Range primitives. + //************************************************************************* + TEST(test_ranges_iterator_t) + { + using range_type = etl::vector; + + static_assert(etl::is_same, int*>::value, "Bad iterator type from etl::ranges::iterator_t"); + } + + TEST(test_ranges_const_iterator_t) + { + using range_type = etl::vector; + + static_assert(etl::is_same, const int*>::value, "Bad iterator type from etl::ranges::const_iterator_t"); + } + + TEST(test_ranges_sentinel_t) + { + using range_type = etl::vector; + + static_assert(etl::is_same, int*>::value, "Bad sentinel type from etl::ranges::sentinel_t"); + } + + TEST(test_ranges_const_sentinel_t) + { + using range_type = etl::vector; + + static_assert(etl::is_same, const int*>::value, "Bad sentinel type from etl::ranges::const_sentinel_t"); + } + + TEST(test_ranges_range_size_t) + { + using range_type0 = int[10]; + using range_type1 = etl::vector; + using range_type2 = etl::ranges::empty_view; + + static_assert(etl::is_same, size_t>::value, "Bad size type from etl::ranges::range_size_t"); + static_assert(etl::is_same, size_t>::value, "Bad size type from etl::ranges::range_size_t"); + static_assert(etl::is_same, size_t>::value, "Bad size type from etl::ranges::range_size_t"); + } + + TEST(test_ranges_range_difference_t) + { + using range_type0 = int[10]; + using range_type1 = etl::vector; + using range_type2 = etl::ranges::empty_view; + + static_assert(etl::is_same, ptrdiff_t>::value, "Bad size type from etl::ranges::range_difference_t"); + static_assert(etl::is_same, ptrdiff_t>::value, "Bad size type from etl::ranges::range_difference_t"); + static_assert(etl::is_same, ptrdiff_t>::value, "Bad size type from etl::ranges::range_difference_t"); + } + + TEST(test_ranges_range_value_t) + { + using range_type0 = int[10]; + using range_type1 = etl::vector; + using range_type2 = etl::ranges::empty_view; + + static_assert(etl::is_same, int>::value, "Bad size type from etl::ranges::range_value_t"); + static_assert(etl::is_same, int>::value, "Bad size type from etl::ranges::range_value_t"); + static_assert(etl::is_same, int>::value, "Bad size type from etl::ranges::range_value_t"); + } + + + TEST(test_ranges_subrange) + { + etl::vector v {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto sr = etl::ranges::subrange{v.begin(), v.end()}; + + CHECK_EQUAL(sr.begin(), v.begin()); + CHECK_EQUAL(sr.end(), v.end()); + CHECK_EQUAL(sr.empty(), false); + CHECK_EQUAL(sr.size(), 10); + + sr.advance(1); + CHECK_EQUAL(sr.size(), 9); + CHECK_EQUAL(sr[0], 1); + + sr.advance(2); + CHECK_EQUAL(sr.size(), 7); + CHECK_EQUAL(sr[0], 3); + + CHECK_EQUAL(sr.next().size(), 6); + CHECK_EQUAL(sr.next()[0], 4); + CHECK_EQUAL(sr.next(1).size(), 6); + CHECK_EQUAL(sr.next(1)[0], 4); + CHECK_EQUAL(sr.next(2).size(), 5); + CHECK_EQUAL(sr.next(2)[0], 5); + + CHECK_EQUAL(sr.prev().size(), 8); + CHECK_EQUAL(sr.prev()[0], 2); + CHECK_EQUAL(sr.prev(1).size(), 8); + CHECK_EQUAL(sr.prev(1)[0], 2); + CHECK_EQUAL(sr.prev(2).size(), 9); + CHECK_EQUAL(sr.prev(2)[0], 1); + } + + //************************************************************************* + // Range factories. + //************************************************************************* + TEST(test_ranges_empty_view) + { + auto ev = etl::ranges::empty_view{}; + + CHECK_EQUAL(ev.begin(), nullptr); + CHECK_EQUAL(ev.end(), nullptr); + CHECK_EQUAL(ev.data(), nullptr); + CHECK_EQUAL(ev.size(), 0); + CHECK_EQUAL(ev.empty(), true); + } + + TEST(test_ranges_views_empty) + { + auto e = etl::ranges::views::empty; + + CHECK_EQUAL(e.begin(), nullptr); + CHECK_EQUAL(e.end(), nullptr); + CHECK_EQUAL(e.data(), nullptr); + CHECK_EQUAL(e.size(), 0); + CHECK_EQUAL(e.empty(), true); + } + + TEST(test_ranges_single_view) + { + auto s0 = etl::ranges::single_view(12); + CHECK_EQUAL(s0.size(), 1); + + auto s = etl::ranges::single_view(23); + + CHECK_EQUAL(*s.begin(), 23); + CHECK_EQUAL(s.end(), s.begin() + 1); + CHECK_EQUAL(*s.data(), 23); + CHECK_EQUAL(s.size(), 1); + CHECK_EQUAL(s.empty(), false); + + *s.begin() = 45; + CHECK_EQUAL(*s.data(), 45); + CHECK_EQUAL(*s.begin(), 45); + } + + TEST(test_ranges_views_single) + { + auto s = etl::ranges::views::single(23); + + CHECK_EQUAL(*s.begin(), 23); + CHECK_EQUAL(s.end(), s.begin() + 1); + CHECK_EQUAL(*s.data(), 23); + CHECK_EQUAL(s.size(), 1); + CHECK_EQUAL(s.empty(), false); + + *s.begin() = 45; + CHECK_EQUAL(*s.data(), 45); + CHECK_EQUAL(*s.begin(), 45); + } + + TEST(test_ranges_iota_view) + { + auto iv = etl::ranges::iota_view(1, 7); + + int compare = 1; + for (auto i: iv) + { + CHECK_EQUAL(i, compare); + ++compare; + } + + CHECK_EQUAL(*iv.begin(), 1); + CHECK_EQUAL(iv.end(), iv.begin() + 6); + CHECK_EQUAL(iv.size(), 6); + CHECK_EQUAL(iv.empty(), false); + + CHECK_EQUAL(iv[4], 5); + + auto iv2 = etl::ranges::iota_view(3); + CHECK_EQUAL(iv2[0], 3); + CHECK_EQUAL(iv2[1], 4); + CHECK_EQUAL(iv2[2], 5); + CHECK_EQUAL(iv2[3], 6); + CHECK_EQUAL(iv2[4], 7); + + auto iv3 = etl::ranges::iota_view(); + CHECK_EQUAL(iv3.size(), 0); + CHECK_EQUAL(iv3.end(), iv3.begin()); + CHECK_EQUAL(iv3.empty(), true); + } + + TEST(test_ranges_views_iota) + { + auto iv = etl::ranges::views::iota(1, 7); + + CHECK_EQUAL(*iv.begin(), 1); + CHECK_EQUAL(iv.end(), iv.begin() + 6); + CHECK_EQUAL(iv.size(), 6); + CHECK_EQUAL(iv.empty(), false); + + CHECK_EQUAL(iv[4], 5); + } + + TEST(test_ranges_iota_view_pipe_take) + { + auto iv = etl::ranges::iota_view(3) | etl::views::take(5); + + auto i = iv.begin(); + auto j = i; + CHECK_EQUAL(*i, 3); + i++; + CHECK_EQUAL(*i, 4); + ++i; + CHECK_EQUAL(*i, 5); + + CHECK_EQUAL(i - j, 2); + + CHECK_EQUAL(iv.size(), 5); + CHECK_EQUAL(iv.front(), 3); + CHECK_EQUAL(iv.back(), 7); + CHECK_EQUAL(iv[0], 3); + CHECK_EQUAL(iv[1], 4); + CHECK_EQUAL(iv[2], 5); + CHECK_EQUAL(iv[3], 6); + CHECK_EQUAL(iv[4], 7); + } + + TEST(test_ranges_repeat_view) + { + // bounded + auto iv = etl::ranges::repeat_view(1, 7); + + for (auto i: iv) + { + CHECK_EQUAL(i, 1); + } + + CHECK_EQUAL(*iv.begin(), 1); + CHECK_EQUAL(iv.end(), iv.begin() + 7); + CHECK_EQUAL(iv.size(), 7); + CHECK_EQUAL(iv.empty(), false); + + CHECK_EQUAL(iv[4], 1); + + // unbounded + auto iv2 = etl::ranges::repeat_view(3); + CHECK_EQUAL(iv2[0], 3); + CHECK_EQUAL(iv2[1], 3); + CHECK_EQUAL(iv2[2], 3); + CHECK_EQUAL(iv2[3], 3); + CHECK_EQUAL(iv2[4], 3); + + auto iv3 = etl::ranges::repeat_view(); + CHECK_EQUAL(iv3.size(), 0); + CHECK_EQUAL(iv3.end(), iv3.begin()); + CHECK_EQUAL(iv3.empty(), true); + } + + TEST(test_ranges_views_repeat) + { + auto iv = etl::ranges::views::repeat(1, 7); + + CHECK_EQUAL(*iv.begin(), 1); + CHECK_EQUAL(iv.end(), iv.begin() + 7); + CHECK_EQUAL(iv.size(), 7); + CHECK_EQUAL(iv.empty(), false); + + CHECK_EQUAL(iv[4], 1); + } + + TEST(test_ranges_repeat_view_pipe_take) + { + auto iv = etl::ranges::repeat_view(3) | etl::views::take(5); + + auto i = iv.begin(); + CHECK_EQUAL(*i, 3); + i++; + CHECK_EQUAL(*i, 3); + ++i; + CHECK_EQUAL(*i, 3); + + CHECK_EQUAL(iv.size(), 5); + CHECK_EQUAL(iv.front(), 3); + CHECK_EQUAL(iv.back(), 3); + CHECK_EQUAL(iv[0], 3); + CHECK_EQUAL(iv[1], 3); + CHECK_EQUAL(iv[2], 3); + CHECK_EQUAL(iv[3], 3); + CHECK_EQUAL(iv[4], 3); + } + + TEST(test_ranges_repeat_view_pipe_take_bounded) + { + auto iv = etl::ranges::repeat_view(3, 30) | etl::views::take(5); + + auto i = iv.begin(); + CHECK_EQUAL(*i, 3); + i++; + CHECK_EQUAL(*i, 3); + ++i; + CHECK_EQUAL(*i, 3); + + CHECK_EQUAL(iv.size(), 5); + CHECK_EQUAL(iv.front(), 3); + CHECK_EQUAL(iv.back(), 3); + CHECK_EQUAL(iv[0], 3); + CHECK_EQUAL(iv[1], 3); + CHECK_EQUAL(iv[2], 3); + CHECK_EQUAL(iv[3], 3); + CHECK_EQUAL(iv[4], 3); + } + + TEST(test_ranges_repeat_view_pipe_take_bounded_limited) + { + auto iv = etl::ranges::repeat_view(3, 4) | etl::views::take(5); + + auto i = iv.begin(); + CHECK_EQUAL(*i, 3); + i++; + CHECK_EQUAL(*i, 3); + ++i; + CHECK_EQUAL(*i, 3); + + CHECK_EQUAL(iv.size(), 4); + CHECK_EQUAL(iv.front(), 3); + CHECK_EQUAL(iv.back(), 3); + CHECK_EQUAL(iv[0], 3); + CHECK_EQUAL(iv[1], 3); + CHECK_EQUAL(iv[2], 3); + CHECK_EQUAL(iv[3], 3); + } + + //************************************************************************* + // Range adaptors + //************************************************************************* + TEST(test_ranges_iterate_c_array) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + int v_in[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + for (int i : etl::views::filter(v_in, even)) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_iterate_plain) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + for (int i : etl::views::filter(v_in, even)) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_iterate_pipe) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + for (int i : v_in | etl::views::filter(even)) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_drop_functional) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto rv = etl::views::drop(v_in, 2); + + CHECK_EQUAL(etl::views::all(v_out_expected), rv); + CHECK_EQUAL(rv.base().base(), v_in); + CHECK_EQUAL(*rv.begin(), 2); + CHECK_EQUAL(rv.size(), 8); + + CHECK_EQUAL(*rv.cbegin(), 2); + CHECK_EQUAL(rv.empty(), false); + CHECK_EQUAL(rv, true); + CHECK_EQUAL(rv.front(), 2); + CHECK_EQUAL(rv.back(), 9); + CHECK_EQUAL(rv[7], 9); + + rv[1] = 33; + CHECK_EQUAL(rv[1], 33); + CHECK_EQUAL(v_in[3], 33); + + v_in[2] = 44; + CHECK_EQUAL(rv[0], 44); + CHECK_EQUAL(v_in[2], 44); + } + + TEST(test_ranges_iterate_pipe_drop) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto rv = v_in | etl::views::drop(2); + for (int i : rv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(rv.base().base(), v_in); + CHECK_EQUAL(*rv.begin(), 2); + CHECK_EQUAL(rv.size(), 8); + + CHECK_EQUAL(*rv.cbegin(), 2); + CHECK_EQUAL(rv.empty(), false); + CHECK_EQUAL(rv, true); + CHECK_EQUAL(rv.front(), 2); + CHECK_EQUAL(rv.back(), 9); + CHECK_EQUAL(rv[7], 9); + + rv[1] = 33; + CHECK_EQUAL(rv[1], 33); + CHECK_EQUAL(v_in[3], 33); + + v_in[2] = 44; + CHECK_EQUAL(rv[0], 44); + CHECK_EQUAL(v_in[2], 44); + } + + TEST(test_ranges_iterate_pipe_drop_out_of_bounds) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{}; + + auto rv = v_in | etl::views::drop(12); + for (int i : rv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(rv.size(), 0); + } + + TEST(test_ranges_iterate_pipe_twice) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 2, 4, 6, 8 }; + + for (int i : v_in | etl::views::filter(even) | etl::views::drop(1)) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_iterate_pipe_drop_while) + { + auto below_three = [](int i) -> bool { return i < 3; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 3, 4, 5, 6, 7, 8, 9 }; + + auto rv = v_in | etl::views::drop_while(below_three); + for (int i : rv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(rv.base().base(), v_in); + CHECK_EQUAL(*rv.begin(), 3); + CHECK_EQUAL(rv.size(), 7); + + CHECK_EQUAL(*rv.cbegin(), 3); + CHECK_EQUAL(rv.empty(), false); + CHECK_EQUAL(rv, true); + CHECK_EQUAL(rv.front(), 3); + CHECK_EQUAL(rv.back(), 9); + CHECK_EQUAL(rv[6], 9); + } + + TEST(test_ranges_take_functional) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out_expected{ 0, 1, 2, 3 }; + + auto rv = etl::views::take(v_in, 4); + + CHECK_EQUAL(etl::views::all(v_out_expected), rv); + CHECK_EQUAL(rv.base().base(), v_in); + CHECK_EQUAL(*rv.begin(), 0); + CHECK_EQUAL(rv.size(), 4); + + CHECK_EQUAL(*rv.cbegin(), 0); + CHECK_EQUAL(rv.empty(), false); + CHECK_EQUAL(rv, true); + CHECK_EQUAL(rv.front(), 0); + CHECK_EQUAL(rv.back(), 3); + CHECK_EQUAL(rv[2], 2); + + rv[2] = 33; + CHECK_EQUAL(rv[2], 33); + CHECK_EQUAL(v_in[2], 33); + + v_in[3] = 44; + CHECK_EQUAL(rv[3], 44); + CHECK_EQUAL(v_in[3], 44); + } + + TEST(test_ranges_iterate_pipe_take) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3 }; + + auto rv = v_in | etl::views::take(4); + for (int i : rv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(rv.base().base(), v_in); + CHECK_EQUAL(*rv.begin(), 0); + CHECK_EQUAL(rv.size(), 4); + + CHECK_EQUAL(*rv.cbegin(), 0); + CHECK_EQUAL(rv.empty(), false); + CHECK_EQUAL(rv, true); + CHECK_EQUAL(rv.front(), 0); + CHECK_EQUAL(rv.back(), 3); + CHECK_EQUAL(rv[2], 2); + + rv[2] = 33; + CHECK_EQUAL(rv[2], 33); + CHECK_EQUAL(v_in[2], 33); + + v_in[3] = 44; + CHECK_EQUAL(rv[3], 44); + CHECK_EQUAL(v_in[3], 44); + } + + TEST(test_ranges_iterate_pipe_take_while) + { + auto below_three = [](int i) -> bool { return i < 3; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2 }; + + auto rv = v_in | etl::views::take_while(below_three); + for (int i : rv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(rv.base().base(), v_in); + CHECK_EQUAL(*rv.begin(), 0); + CHECK_EQUAL(rv.size(), 3); + + CHECK_EQUAL(*rv.cbegin(), 0); + CHECK_EQUAL(rv.empty(), false); + CHECK_EQUAL(rv, true); + CHECK_EQUAL(rv.front(), 0); + CHECK_EQUAL(rv.back(), 2); + CHECK_EQUAL(rv[2], 2); + } + + TEST(test_ranges_reverse_view_functional) + { + etl::vector v_in{ 0, 1, 2, 3, 4 }; + etl::vector v_out; + etl::vector v_out_expected{ 4, 3, 2, 1, 0 }; + + auto rv = etl::ranges::reverse_view(v_in); + for (int i : rv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(rv.base().base(), v_in); + CHECK_EQUAL(*rv.begin(), 4); + CHECK_EQUAL(rv.size(), 5); + + CHECK_EQUAL(*rv.cbegin(), 4); + CHECK_EQUAL(rv.empty(), false); + CHECK_EQUAL(rv, true); + CHECK_EQUAL(rv.front(), 4); + CHECK_EQUAL(rv.back(), 0); + CHECK_EQUAL(rv[2], 2); + + rv[0] = 22; + CHECK_EQUAL(rv[0], 22); + CHECK_EQUAL(v_in[4], 22); + + v_in[0] = 11; + CHECK_EQUAL(rv.back(), 11); + CHECK_EQUAL(rv[4], 11); + } + + TEST(test_ranges_iterate_pipe_reverse) + { + etl::vector v_in{ 0, 1, 2, 3, 4 }; + etl::vector v_out; + etl::vector v_out_expected{ 4, 3, 2, 1, 0 }; + + auto rv = v_in | etl::views::reverse; + for (int i : rv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(rv.base().base(), v_in); + CHECK_EQUAL(*rv.begin(), 4); + CHECK_EQUAL(rv.size(), 5); + CHECK_EQUAL(rv.front(), 4); + CHECK_EQUAL(rv.back(), 0); + } + + TEST(test_ranges_iterate_pipe_reverse_reverse) + { + etl::vector v_in{ 0, 1, 2, 3, 4 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4 }; + + auto rv = v_in | etl::views::reverse | etl::views::reverse; + for (int i : rv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(rv.base(), v_in); + CHECK_EQUAL(*rv.begin(), 0); + CHECK_EQUAL(rv.size(), 5); + CHECK_EQUAL(rv.front(), 0); + CHECK_EQUAL(rv.back(), 4); + } + + TEST(test_ranges_iterate_pipe_all) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + for (int i : v_in | etl::views::all) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_iterate_pipe_ref) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto r = v_in | etl::views::ref(); + for (int i : r) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + + v_in[9] = 99; + CHECK_EQUAL(r[9], 99); + + CHECK_EQUAL(r.base(), v_in); + CHECK_EQUAL(*r.begin(), 0); + CHECK_EQUAL(r.end(), r.begin() + 10); + CHECK_EQUAL(r.empty(), false); + CHECK_EQUAL(r.size(), 10); + CHECK_EQUAL(r.data(), v_in.data()); + } + + TEST(test_ranges_iterate_pipe_owning) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto r = v_in | etl::views::owning(); + for (int i : r) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + + CHECK_EQUAL(0, v_in.size()); + + etl::ranges::owning_view> ov2; + + CHECK_NOT_EQUAL(r.base(), v_in); + CHECK_EQUAL(*r.begin(), 0); + CHECK_EQUAL(r.end(), r.begin() + 10); + CHECK_EQUAL(r.empty(), false); + CHECK_EQUAL(r.size(), 10); + CHECK_NOT_EQUAL(r.data(), v_in.data()); + + //ov2 = r; // expected: compile error! + ov2 = etl::move(r); + } + + TEST(test_ranges_iterate_pipe_to) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out_expected{ 2, 4, 6, 8 }; + + auto v_out = v_in | etl::views::filter(even) | etl::views::drop(1) | etl::ranges::to>(); + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_iterate_pipe_as_rvalue) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + + etl::vector v_out_expected_0{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + etl::vector v_out_expected_1{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + for (auto&& i : v_in | etl::views::as_rvalue) + { + v_out.emplace_back(etl::move(i)); + } + + CHECK_EQUAL(v_out_expected_0, v_in); + CHECK_EQUAL(v_out_expected_1, v_out); + } + + TEST(test_ranges_iterate_pipe_as_rvalue_functional) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + + etl::vector v_out_expected_0{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + etl::vector v_out_expected_1{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + v_out = etl::views::as_rvalue(v_in) | etl::ranges::to>(); + + CHECK_EQUAL(v_out_expected_0, v_in); + CHECK_EQUAL(v_out_expected_1, v_out); + } + + TEST(test_ranges_as_const_view_functional) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto cv = etl::ranges::as_const_view(v_in); + + // Iterator type should be const_iterator + using cv_reference = decltype(*cv.begin()); + static_assert(etl::is_const_v>, + "as_const_view iterator should dereference to const"); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(cv.size(), 10u); + CHECK_EQUAL(cv.empty(), false); + CHECK_EQUAL(cv.front(), 0); + CHECK_EQUAL(cv.back(), 9); + } + + TEST(test_ranges_as_const_view_reflects_base_changes) + { + etl::vector v_in{ 0, 1, 2, 3, 4 }; + + auto cv = etl::ranges::as_const_view(v_in); + + CHECK_EQUAL(cv.front(), 0); + CHECK_EQUAL(cv.back(), 4); + CHECK_EQUAL(cv.size(), 5u); + + v_in[0] = 99; + v_in[4] = 77; + CHECK_EQUAL(cv.front(), 99); + CHECK_EQUAL(cv.back(), 77); + } + + TEST(test_ranges_as_const_view_pipe) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto cv = v_in | etl::views::as_const(); + + using cv_reference = decltype(*cv.begin()); + static_assert(etl::is_const_v>, + "piped as_const view iterator should dereference to const"); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(cv.size(), 10u); + } + + TEST(test_ranges_views_as_const_functional) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto cv = etl::views::as_const(v_in); + + using cv_reference = decltype(*cv.begin()); + static_assert(etl::is_const_v>, + "views::as_const iterator should dereference to const"); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_as_const_view_pipe_chained_with_take) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2 }; + + auto cv = v_in | etl::views::as_const() | etl::views::take(3); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_as_const_view_pipe_chained_with_drop) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 7, 8, 9 }; + + auto cv = v_in | etl::views::as_const() | etl::views::drop(7); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_as_const_view_pipe_chained_with_reverse) + { + etl::vector v_in{ 0, 1, 2, 3, 4 }; + etl::vector v_out; + etl::vector v_out_expected{ 4, 3, 2, 1, 0 }; + + auto cv = v_in | etl::views::as_const() | etl::views::reverse; + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_as_const_view_with_std_vector) + { + std::vector v_in{ 0, 1, 2, 3, 4 }; + std::vector v_out; + std::vector v_out_expected{ 0, 1, 2, 3, 4 }; + + auto cv = etl::ranges::as_const_view(v_in); + + using cv_reference = decltype(*cv.begin()); + static_assert(etl::is_const_v>, + "as_const_view over std::vector should dereference to const"); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected.size(), v_out.size()); + for (size_t idx = 0; idx < v_out_expected.size(); ++idx) + { + CHECK_EQUAL(v_out_expected[idx], v_out[idx]); + } + } + + //************************************************************************* + /// cache_latest_view tests + //************************************************************************* + TEST(test_ranges_cache_latest_view_functional) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto cv = etl::ranges::cache_latest_view(v_in); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(cv.size(), 10u); + CHECK_EQUAL(cv.empty(), false); + } + + TEST(test_ranges_cache_latest_view_caches_value) + { + int dereference_count = 0; + auto counting_transform = [&dereference_count](int i) -> int { ++dereference_count; return i * 2; }; + + etl::vector v_in{ 1, 2, 3 }; + + auto tv = v_in | etl::views::transform(counting_transform) | etl::views::cache_latest(); + + auto it = tv.begin(); + + // First dereference should evaluate + dereference_count = 0; + int val1 = *it; + CHECK_EQUAL(1, dereference_count); + CHECK_EQUAL(2, val1); + + // Second dereference of same position should use cache (no extra transform call) + int val2 = *it; + CHECK_EQUAL(1, dereference_count); + CHECK_EQUAL(2, val2); + + // Advance and dereference + ++it; + int val3 = *it; + CHECK_EQUAL(2, dereference_count); + CHECK_EQUAL(4, val3); + } + + TEST(test_ranges_cache_latest_view_pipe) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto cv = v_in | etl::views::cache_latest(); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_views_cache_latest_functional) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto cv = etl::views::cache_latest(v_in); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_cache_latest_view_pipe_chained_with_take) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2 }; + + auto cv = v_in | etl::views::cache_latest() | etl::views::take(3); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_cache_latest_view_pipe_chained_with_transform) + { + auto square = [](int i) -> int { return i * i; }; + + etl::vector v_in{ 0, 1, 2, 3, 4 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 4, 9, 16 }; + + auto cv = v_in | etl::views::transform(square) | etl::views::cache_latest(); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_cache_latest_view_empty_range) + { + etl::vector v_in; + + auto cv = etl::ranges::cache_latest_view(v_in); + + CHECK_EQUAL(cv.empty(), true); + CHECK_EQUAL(cv.size(), 0u); + CHECK(cv.begin() == cv.end()); + } + + TEST(test_ranges_cache_latest_view_with_std_vector) + { + std::vector v_in{ 0, 1, 2, 3, 4 }; + std::vector v_out; + std::vector v_out_expected{ 0, 1, 2, 3, 4 }; + + auto cv = etl::ranges::cache_latest_view(v_in); + + for (auto i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected.size(), v_out.size()); + for (size_t idx = 0; idx < v_out_expected.size(); ++idx) + { + CHECK_EQUAL(v_out_expected[idx], v_out[idx]); + } + } + + TEST(test_ranges_iterate_transform) + { + auto square = [](int i) -> int { return i * i; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81 }; + + for (int i : v_in | etl::views::transform(square)) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + //************************************************************************* + /// transform_view tests + //************************************************************************* + TEST(test_ranges_transform_view_functional) + { + auto square = [](int i) -> int { return i * i; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81 }; + + auto tv = etl::views::transform(v_in, square); + + for (auto i : tv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_transform_view_direct_construction) + { + auto negate = [](int i) -> int { return -i; }; + + etl::vector v_in{ 1, 2, 3, 4, 5 }; + etl::vector v_out; + etl::vector v_out_expected{ -1, -2, -3, -4, -5 }; + + auto tv = etl::ranges::transform_view(etl::views::all(v_in), negate); + + for (auto i : tv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_transform_view_pipe) + { + auto triple = [](int i) -> int { return i * 3; }; + + etl::vector v_in{ 1, 2, 3, 4, 5 }; + etl::vector v_out; + etl::vector v_out_expected{ 3, 6, 9, 12, 15 }; + + auto tv = v_in | etl::views::transform(triple); + + for (auto i : tv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_transform_view_size) + { + auto identity = [](int i) -> int { return i; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto tv = v_in | etl::views::transform(identity); + + CHECK_EQUAL(10u, tv.size()); + } + + TEST(test_ranges_transform_view_empty) + { + auto identity = [](int i) -> int { return i; }; + + etl::vector v_in; + + auto tv = v_in | etl::views::transform(identity); + + CHECK_EQUAL(true, tv.empty()); + CHECK_EQUAL(0u, tv.size()); + } + + TEST(test_ranges_transform_view_non_empty) + { + auto identity = [](int i) -> int { return i; }; + + etl::vector v_in{ 42 }; + + auto tv = v_in | etl::views::transform(identity); + + CHECK_EQUAL(false, tv.empty()); + CHECK_EQUAL(1u, tv.size()); + } + + TEST(test_ranges_transform_view_front) + { + auto square = [](int i) -> int { return i * i; }; + + etl::vector v_in{ 3, 4, 5 }; + + auto tv = v_in | etl::views::transform(square); + + CHECK_EQUAL(9, tv.front()); + } + + TEST(test_ranges_transform_view_index_operator) + { + auto square = [](int i) -> int { return i * i; }; + + etl::vector v_in{ 1, 2, 3, 4, 5 }; + + auto tv = v_in | etl::views::transform(square); + + CHECK_EQUAL(1, tv[0]); + CHECK_EQUAL(4, tv[1]); + CHECK_EQUAL(9, tv[2]); + CHECK_EQUAL(16, tv[3]); + CHECK_EQUAL(25, tv[4]); + } + + TEST(test_ranges_transform_view_type_change) + { + auto to_double = [](int i) -> double { return i * 1.5; }; + + etl::vector v_in{ 2, 4, 6 }; + + auto tv = v_in | etl::views::transform(to_double); + + auto it = tv.begin(); + CHECK_CLOSE(3.0, *it, 0.001); ++it; + CHECK_CLOSE(6.0, *it, 0.001); ++it; + CHECK_CLOSE(9.0, *it, 0.001); ++it; + CHECK(it == tv.end()); + } + + TEST(test_ranges_transform_view_iterator_increment) + { + auto square = [](int i) -> int { return i * i; }; + + etl::vector v_in{ 1, 2, 3 }; + + auto tv = v_in | etl::views::transform(square); + + // Pre-increment + auto it = tv.begin(); + CHECK_EQUAL(1, *it); + ++it; + CHECK_EQUAL(4, *it); + + // Post-increment + auto it2 = tv.begin(); + auto old = it2++; + CHECK_EQUAL(1, *old); + CHECK_EQUAL(4, *it2); + } + + TEST(test_ranges_transform_view_iterator_equality) + { + auto identity = [](int i) -> int { return i; }; + + etl::vector v_in{ 1, 2, 3 }; + + auto tv = v_in | etl::views::transform(identity); + + auto b = tv.begin(); + auto e = tv.end(); + CHECK(b != e); + CHECK(b == tv.begin()); + CHECK(e == tv.end()); + } + + TEST(test_ranges_transform_view_chained_with_take) + { + auto square = [](int i) -> int { return i * i; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 4 }; + + auto tv = v_in | etl::views::transform(square) | etl::views::take(3); + + for (auto i : tv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_transform_view_chained_with_temporary) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 4 }; + + auto tv = v_in | etl::views::transform([](int i) -> int { return i * i; }) | etl::views::take(3); + + for (auto i : tv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_transform_view_chained_with_drop) + { + auto square = [](int i) -> int { return i * i; }; + + etl::vector v_in{ 0, 1, 2, 3, 4 }; + etl::vector v_out; + etl::vector v_out_expected{ 9, 16 }; + + auto tv = v_in | etl::views::transform(square) | etl::views::drop(3); + + for (auto i : tv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_transform_view_chained_transforms) + { + auto add_one = [](int i) -> int { return i + 1; }; + auto multiply = [](int i) -> int { return i * 10; }; + + etl::vector v_in{ 0, 1, 2, 3, 4 }; + etl::vector v_out; + etl::vector v_out_expected{ 10, 20, 30, 40, 50 }; + + auto tv = v_in | etl::views::transform(add_one) | etl::views::transform(multiply); + + for (auto i : tv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_transform_view_with_std_vector) + { + auto square = [](int i) -> int { return i * i; }; + + std::vector v_in{ 1, 2, 3, 4, 5 }; + std::vector v_out; + std::vector v_out_expected{ 1, 4, 9, 16, 25 }; + + auto tv = v_in | etl::views::transform(square); + + for (auto i : tv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected.size(), v_out.size()); + for (size_t idx = 0; idx < v_out_expected.size(); ++idx) + { + CHECK_EQUAL(v_out_expected[idx], v_out[idx]); + } + } + + TEST(test_ranges_transform_view_with_std_array) + { + auto square = [](int i) -> int { return i * i; }; + + std::array v_in{ 1, 2, 3, 4, 5 }; + std::vector v_out; + std::vector v_out_expected{ 1, 4, 9, 16, 25 }; + + auto tv = v_in | etl::views::transform(square); + + for (auto i : tv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected.size(), v_out.size()); + for (size_t idx = 0; idx < v_out_expected.size(); ++idx) + { + CHECK_EQUAL(v_out_expected[idx], v_out[idx]); + } + } + + TEST(test_ranges_transform_view_single_element) + { + auto negate = [](int i) -> int { return -i; }; + + etl::vector v_in{ 42 }; + etl::vector v_out; + etl::vector v_out_expected{ -42 }; + + auto tv = v_in | etl::views::transform(negate); + + for (auto i : tv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + //************************************************************************* + /// filter_view tests + //************************************************************************* + TEST(test_ranges_filter_view_functional) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + auto fv = etl::views::filter(v_in, even); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_filter_view_direct_construction) + { + auto odd = [](int i) -> bool { return 1 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 1, 3, 5, 7, 9 }; + + auto fv = etl::ranges::filter_view(etl::views::all(v_in), odd); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_filter_view_pipe) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + auto fv = v_in | etl::views::filter(even); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_filter_view_size) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto fv = v_in | etl::views::filter(even); + + CHECK_EQUAL(5u, fv.size()); + } + + TEST(test_ranges_filter_view_empty) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in; + + auto fv = v_in | etl::views::filter(even); + + CHECK_EQUAL(true, fv.empty()); + CHECK_EQUAL(0u, fv.size()); + } + + TEST(test_ranges_filter_view_non_empty) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 42 }; + + auto fv = v_in | etl::views::filter(even); + + CHECK_EQUAL(false, fv.empty()); + CHECK_EQUAL(1u, fv.size()); + } + + TEST(test_ranges_filter_view_all_filtered_out) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 1, 3, 5, 7, 9 }; + + auto fv = v_in | etl::views::filter(even); + + CHECK_EQUAL(true, fv.empty()); + CHECK_EQUAL(0u, fv.size()); + } + + TEST(test_ranges_filter_view_all_pass) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 2, 4, 6, 8 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + auto fv = v_in | etl::views::filter(even); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(5u, fv.size()); + } + + TEST(test_ranges_filter_view_front) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 1, 2, 3, 4, 5 }; + + auto fv = v_in | etl::views::filter(even); + + CHECK_EQUAL(2, fv.front()); + } + + TEST(test_ranges_filter_view_index_operator) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto fv = v_in | etl::views::filter(even); + + CHECK_EQUAL(0, fv[0]); + CHECK_EQUAL(2, fv[1]); + CHECK_EQUAL(4, fv[2]); + CHECK_EQUAL(6, fv[3]); + CHECK_EQUAL(8, fv[4]); + } + + TEST(test_ranges_filter_view_iterator_increment) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4 }; + + auto fv = v_in | etl::views::filter(even); + + // Pre-increment + auto it = fv.begin(); + CHECK_EQUAL(0, *it); + ++it; + CHECK_EQUAL(2, *it); + + // Post-increment + auto it2 = fv.begin(); + auto old = it2++; + CHECK_EQUAL(0, *old); + CHECK_EQUAL(2, *it2); + } + + TEST(test_ranges_filter_view_iterator_equality) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4 }; + + auto fv = v_in | etl::views::filter(even); + + auto b = fv.begin(); + auto e = fv.end(); + CHECK(b != e); + CHECK(b == fv.begin()); + CHECK(e == fv.end()); + } + + TEST(test_ranges_filter_view_chained_with_take) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2 }; + + auto fv = v_in | etl::views::filter(even) | etl::views::take(2); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_filter_view_chained_with_temporary) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2 }; + + auto fv = v_in | etl::views::filter([](int i) -> bool { return 0 == i % 2; }) | etl::views::take(2); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_filter_view_chained_with_drop) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 4, 6, 8 }; + + auto fv = v_in | etl::views::filter(even) | etl::views::drop(2); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_filter_view_chained_with_transform) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + auto square = [](int i) -> int { return i * i; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 4, 16, 36, 64 }; + + auto fv = v_in | etl::views::filter(even) | etl::views::transform(square); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_filter_view_with_std_vector) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + std::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + std::vector v_out; + std::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + auto fv = v_in | etl::views::filter(even); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected.size(), v_out.size()); + for (size_t idx = 0; idx < v_out_expected.size(); ++idx) + { + CHECK_EQUAL(v_out_expected[idx], v_out[idx]); + } + } + + TEST(test_ranges_filter_view_with_std_array) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + std::array v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + std::vector v_out; + std::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + auto fv = v_in | etl::views::filter(even); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected.size(), v_out.size()); + for (size_t idx = 0; idx < v_out_expected.size(); ++idx) + { + CHECK_EQUAL(v_out_expected[idx], v_out[idx]); + } + } + + TEST(test_ranges_filter_view_single_element) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 42 }; + etl::vector v_out; + etl::vector v_out_expected{ 42 }; + + auto fv = v_in | etl::views::filter(even); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_filter_view_c_array) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + int v_in[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + auto fv = etl::views::filter(v_in, even); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_filter_view_c_array_pipe) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + int v_in[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + auto fv = v_in | etl::views::filter(even); + + for (auto i : fv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_join_functional) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + + auto result = etl::views::join(v); + + using result_type = etl::vector; + result_type v_expected{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + + auto expected_view = etl::views::all(v_expected); + CHECK_EQUAL(result, expected_view); + } + + TEST(test_ranges_join_pipe) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + + auto result = v | etl::views::join; + + using result_type = etl::vector; + result_type v_expected{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + + auto expected_view = etl::views::all(v_expected); + CHECK_EQUAL(result, expected_view); + } + + TEST(test_ranges_join_degenerated_1) + { + using range_type = etl::vector; + etl::vector v{{1}, {1, 2, 3}, {1}, {7, 8, 9}, {1}}; + + auto result = v | etl::views::join; + + using result_type = etl::vector; + result_type v_expected{1, 1, 2, 3, 1, 7, 8, 9, 1}; + auto expected_view = etl::views::all(v_expected); + + CHECK_EQUAL(9, result.size()); + CHECK_EQUAL(expected_view, result); + } + + TEST(test_ranges_join_degenerated_2) + { + using range_type = etl::vector; + etl::vector v{{}, {1, 2, 3}, {}, {7, 8, 9}, {}}; + + auto rv = v | etl::views::join; + + using result_type = etl::vector; + result_type v_expected{1, 2, 3, 7, 8, 9}; + auto expected_view = etl::views::all(v_expected); + + CHECK_EQUAL(6, rv.size()); + CHECK_EQUAL(expected_view, rv); + } + + TEST(test_ranges_join_iterate) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + + using result_type = etl::vector; + auto result = v | etl::views::join | etl::ranges::to(); + + result_type result_v; + for (auto i: result) + { + result_v.emplace_back(i); + } + result_type v_expected{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + CHECK_EQUAL(result_v, v_expected); + } + + TEST(test_ranges_join_view) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + + using result_type = etl::vector; + auto result = v | etl::views::join | etl::ranges::to(); + + result_type v_expected{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + CHECK_EQUAL(result, v_expected); + } + + TEST(test_ranges_join_with_functional) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + range_type pattern{111, 222}; + + auto result = etl::views::join_with(v, pattern); + + using result_type = etl::vector; + result_type v_expected{1, 2, 3, 111, 222, 4, 5, 6, 111, 222, 7, 8, 9, 111, 222, 10, 11, 12}; + + auto expected_view = etl::views::all(v_expected); + CHECK_EQUAL(result, expected_view); + } + + TEST(test_ranges_join_with_pipe) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + range_type pattern{111, 222}; + + auto result = v | etl::views::join_with(pattern); + + using result_type = etl::vector; + result_type v_expected{1, 2, 3, 111, 222, 4, 5, 6, 111, 222, 7, 8, 9, 111, 222, 10, 11, 12}; + + auto expected_view = etl::views::all(v_expected); + CHECK_EQUAL(result, expected_view); + } + + TEST(test_ranges_join_with_degenerated_1) + { + using range_type = etl::vector; + etl::vector v{{1}, {1, 2, 3}, {1}, {7, 8, 9}, {1}}; + range_type pattern{111, 222}; + + auto result = v | etl::views::join_with(pattern); + + using result_type = etl::vector; + result_type v_expected{1, 111, 222, 1, 2, 3, 111, 222, 1, 111, 222, 7, 8, 9, 111, 222, 1}; + auto expected_view = etl::views::all(v_expected); + + CHECK_EQUAL(17, result.size()); + CHECK_EQUAL(expected_view, result); + } + + TEST(test_ranges_join_with_degenerated_2) + { + using range_type = etl::vector; + etl::vector v{{}, {1, 2, 3}, {}, {7, 8, 9}, {}}; + range_type pattern{111, 222}; + + auto rv = v | etl::views::join_with(pattern); + + using result_type = etl::vector; + result_type v_expected{111, 222, 1, 2, 3, 111, 222, 111, 222, 7, 8, 9, 111, 222}; + auto expected_view = etl::views::all(v_expected); + + CHECK_EQUAL(14, rv.size()); + CHECK_EQUAL(expected_view, rv); + } + + TEST(test_ranges_join_with_iterate) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + range_type pattern{111, 222}; + + using result_type = etl::vector; + auto result = v | etl::views::join_with(pattern) | etl::ranges::to(); + + result_type result_v; + for (auto i: result) + { + result_v.emplace_back(i); + } + result_type v_expected{1, 2, 3, 111, 222, 4, 5, 6, 111, 222, 7, 8, 9, 111, 222, 10, 11, 12}; + CHECK_EQUAL(result_v, v_expected); + } + + TEST(test_ranges_join_with_view) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + range_type pattern{111, 222}; + + using result_type = etl::vector; + auto result = v | etl::views::join_with(pattern) | etl::ranges::to(); + + result_type v_expected{1, 2, 3, 111, 222, 4, 5, 6, 111, 222, 7, 8, 9, 111, 222, 10, 11, 12}; + CHECK_EQUAL(result, v_expected); + } + + TEST(test_ranges_join_with_functional_single) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + int pattern{111}; + + auto result = etl::views::join_with(v, pattern); + + using result_type = etl::vector; + result_type v_expected{1, 2, 3, 111, 4, 5, 6, 111, 7, 8, 9, 111, 10, 11, 12}; + + auto expected_view = etl::views::all(v_expected); + CHECK_EQUAL(result, expected_view); + } + + TEST(test_ranges_join_with_functional_single_immediate) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + + auto result = etl::views::join_with(v, 111); + + using result_type = etl::vector; + result_type v_expected{1, 2, 3, 111, 4, 5, 6, 111, 7, 8, 9, 111, 10, 11, 12}; + + auto expected_view = etl::views::all(v_expected); + CHECK_EQUAL(result, expected_view); + } + + TEST(test_ranges_join_with_pipe_single) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + int pattern{111}; + + auto result = v | etl::views::join_with(pattern); + + using result_type = etl::vector; + result_type v_expected{1, 2, 3, 111, 4, 5, 6, 111, 7, 8, 9, 111, 10, 11, 12}; + + auto expected_view = etl::views::all(v_expected); + CHECK_EQUAL(result, expected_view); + } + + TEST(test_ranges_join_with_iterate_single) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + int pattern{111}; + + using result_type = etl::vector; + auto result = v | etl::views::join_with(pattern) | etl::ranges::to(); + + result_type result_v; + for (auto i: result) + { + result_v.emplace_back(i); + } + result_type v_expected{1, 2, 3, 111, 4, 5, 6, 111, 7, 8, 9, 111, 10, 11, 12}; + CHECK_EQUAL(result_v, v_expected); + } + + TEST(test_ranges_join_with_view_single) + { + using range_type = etl::vector; + etl::vector v{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}; + int pattern{111}; + + using result_type = etl::vector; + auto result = v | etl::views::join_with(pattern) | etl::ranges::to(); + + result_type v_expected{1, 2, 3, 111, 4, 5, 6, 111, 7, 8, 9, 111, 10, 11, 12}; + CHECK_EQUAL(result, v_expected); + } + + //************************************************************************* + // split_view and views::split tests + //************************************************************************* + TEST(test_ranges_split_view_int_vector) + { + etl::vector v_in{1, 2, 0, 3, 4, 0, 5}; + auto sv = etl::ranges::split_view(v_in, 0); + std::vector> expected{{1,2},{3,4},{5}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_split_view_functional_single_delim) + { + // Split {1, 2, 0, 3, 4, 0, 5, 6} by 0 => {1,2}, {3,4}, {5,6} + std::vector v_in{1, 2, 0, 3, 4, 0, 5, 6}; + auto sv = etl::ranges::split_view(v_in, 0); + std::vector> expected{{1,2},{3,4},{5,6}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_split_view_multi_pattern) + { + // Split {1, 2, 0, 0, 3, 4, 0, 0, 5} by pattern {0, 0} => {1,2}, {3,4}, {5} + std::vector v_in{1, 2, 0, 0, 3, 4, 0, 0, 5}; + std::vector delim{0, 0}; + auto sv = etl::ranges::split_view(v_in, delim); + std::vector> expected{{1,2},{3,4},{5}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_split_view_pipe) + { + // Pipe syntax: {1, 0, 2, 0, 3} | split(0) => {1}, {2}, {3} + std::vector v_in{1, 0, 2, 0, 3}; + auto sv = v_in | etl::views::split(0); + std::vector> expected{{1},{2},{3}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_split_view_empty_input) + { + // Split empty range => one empty subrange + std::vector v_in{}; + auto sv = etl::ranges::split_view(v_in, 0); + size_t count = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(actual.size(), 0U); + ++count; + } + CHECK_EQUAL(count, 1U); + } + + TEST(test_ranges_split_view_no_delim) + { + // No delimiter found => one subrange with all elements + std::vector v_in{1, 2, 3}; + auto sv = etl::ranges::split_view(v_in, 0); + size_t count = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + std::vector expected_sub{1, 2, 3}; + CHECK_EQUAL(expected_sub, actual); + ++count; + } + CHECK_EQUAL(count, 1U); + } + + TEST(test_ranges_split_view_delim_at_edges) + { + // Delimiter at start and end: {0, 1, 2, 0} => {}, {1,2}, {} + std::vector v_in{0, 1, 2, 0}; + auto sv = etl::ranges::split_view(v_in, 0); + std::vector> expected{{},{1,2},{}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_split_view_consecutive_delim) + { + // Consecutive delimiters: {1, 0, 0, 2} => {1}, {}, {2} + std::vector v_in{1, 0, 0, 2}; + auto sv = etl::ranges::split_view(v_in, 0); + std::vector> expected{{1},{},{2}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_split_view_pipe_multi_pattern) + { + // Pipe with multi-element pattern: {1, 2, 9, 9, 3, 4, 9, 9, 5} | split({9,9}) => {1,2}, {3,4}, {5} + std::vector v_in{1, 2, 9, 9, 3, 4, 9, 9, 5}; + std::vector delim{9, 9}; + auto sv = v_in | etl::views::split(delim); + std::vector> expected{{1,2},{3,4},{5}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_split_view_etl_vector) + { + // Works with etl::vector too + etl::vector v_in{1, 0, 2, 0, 3}; + auto sv = etl::ranges::split_view(v_in, 0); + std::vector> expected{{1},{2},{3}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + //************************************************************************* + // lazy_split_view and views::lazy_split tests + //************************************************************************* + TEST(test_ranges_lazy_split_view_int_vector) + { + etl::vector v_in{1, 2, 0, 3, 4, 0, 5}; + auto sv = etl::ranges::lazy_split_view(v_in, 0); + std::vector> expected{{1,2},{3,4},{5}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_lazy_split_view_functional_single_delim) + { + // Split {1, 2, 0, 3, 4, 0, 5, 6} by 0 => {1,2}, {3,4}, {5,6} + std::vector v_in{1, 2, 0, 3, 4, 0, 5, 6}; + auto sv = etl::ranges::lazy_split_view(v_in, 0); + std::vector> expected{{1,2},{3,4},{5,6}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_lazy_split_view_multi_pattern) + { + // Split {1, 2, 0, 0, 3, 4, 0, 0, 5} by pattern {0, 0} => {1,2}, {3,4}, {5} + std::vector v_in{1, 2, 0, 0, 3, 4, 0, 0, 5}; + std::vector delim{0, 0}; + auto sv = etl::ranges::lazy_split_view(v_in, delim); + std::vector> expected{{1,2},{3,4},{5}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_lazy_split_view_pipe) + { + // Pipe syntax: {1, 0, 2, 0, 3} | lazy_split(0) => {1}, {2}, {3} + std::vector v_in{1, 0, 2, 0, 3}; + auto sv = v_in | etl::views::lazy_split(0); + std::vector> expected{{1},{2},{3}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_lazy_split_view_empty_input) + { + // Split empty range => one empty subrange + std::vector v_in{}; + auto sv = etl::ranges::lazy_split_view(v_in, 0); + size_t count = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(actual.size(), 0U); + ++count; + } + CHECK_EQUAL(count, 1U); + } + + TEST(test_ranges_lazy_split_view_no_delim) + { + // No delimiter found => one subrange with all elements + std::vector v_in{1, 2, 3}; + auto sv = etl::ranges::lazy_split_view(v_in, 0); + size_t count = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + std::vector expected_sub{1, 2, 3}; + CHECK_EQUAL(expected_sub, actual); + ++count; + } + CHECK_EQUAL(count, 1U); + } + + TEST(test_ranges_lazy_split_view_delim_at_edges) + { + // Delimiter at start and end: {0, 1, 2, 0} => {}, {1,2}, {} + std::vector v_in{0, 1, 2, 0}; + auto sv = etl::ranges::lazy_split_view(v_in, 0); + std::vector> expected{{},{1,2},{}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_lazy_split_view_consecutive_delim) + { + // Consecutive delimiters: {1, 0, 0, 2} => {1}, {}, {2} + std::vector v_in{1, 0, 0, 2}; + auto sv = etl::ranges::lazy_split_view(v_in, 0); + std::vector> expected{{1},{},{2}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_lazy_split_view_pipe_multi_pattern) + { + // Pipe with multi-element pattern: {1, 2, 9, 9, 3, 4, 9, 9, 5} | lazy_split({9,9}) => {1,2}, {3,4}, {5} + std::vector v_in{1, 2, 9, 9, 3, 4, 9, 9, 5}; + std::vector delim{9, 9}; + auto sv = v_in | etl::views::lazy_split(delim); + std::vector> expected{{1,2},{3,4},{5}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_lazy_split_view_etl_vector) + { + // Works with etl::vector too + etl::vector v_in{1, 0, 2, 0, 3}; + auto sv = etl::ranges::lazy_split_view(v_in, 0); + std::vector> expected{{1},{2},{3}}; + size_t idx = 0; + for (auto sub : sv) { + std::vector actual(sub.begin(), sub.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(idx, expected.size()); + } + + TEST(test_ranges_lazy_split_view_inner_range_lazy_iteration) + { + // Verify that the inner range can be iterated element-by-element (lazily) + std::vector v_in{10, 20, 0, 30, 40, 50}; + auto sv = etl::ranges::lazy_split_view(v_in, 0); + auto outer_it = sv.begin(); + + // First segment: {10, 20} + auto inner1 = *outer_it; + auto inner_it = inner1.begin(); + CHECK_EQUAL(*inner_it, 10); + ++inner_it; + CHECK_EQUAL(*inner_it, 20); + ++inner_it; + CHECK(inner_it == inner1.end()); + + // Second segment: {30, 40, 50} + ++outer_it; + auto inner2 = *outer_it; + auto inner_it2 = inner2.begin(); + CHECK_EQUAL(*inner_it2, 30); + ++inner_it2; + CHECK_EQUAL(*inner_it2, 40); + ++inner_it2; + CHECK_EQUAL(*inner_it2, 50); + ++inner_it2; + CHECK(inner_it2 == inner2.end()); + + ++outer_it; + CHECK(outer_it == sv.end()); + } + + TEST(test_ranges_lazy_split_view_inner_range_empty_check) + { + // Inner range empty() method + std::vector v_in{0, 1, 0}; + auto sv = etl::ranges::lazy_split_view(v_in, 0); + auto it = sv.begin(); + + // First segment is empty + auto seg0 = *it; + CHECK(seg0.empty()); + + ++it; + auto seg1 = *it; + CHECK(!seg1.empty()); + + ++it; + auto seg2 = *it; + CHECK(seg2.empty()); + } + + TEST(test_counted) + { + { + std::vector vec{1, 2, 3, 4, 5}; + + auto result = etl::views::counted(vec.begin(), 3); + + CHECK_EQUAL(result.size(), 3); + CHECK_EQUAL(result[0], 1); + CHECK_EQUAL(result[1], 2); + CHECK_EQUAL(result[2], 3); + } + { + std::list list{1, 2, 3, 4, 5}; + + auto result = etl::views::counted(list.begin(), 3); + + CHECK_EQUAL(result.size(), 3); + CHECK_EQUAL(result[0], 1); + CHECK_EQUAL(result[1], 2); + CHECK_EQUAL(result[2], 3); + } + } + + TEST(test_ranges_concat_view_1) + { + std::vector vec{1, 2, 3, 4}; + + using result_type = std::vector; + auto cv = etl::views::concat(vec); + result_type result = etl::views::concat(vec) | etl::ranges::to(); + + result_type expected_result{1, 2, 3, 4}; + + CHECK_EQUAL(cv.size(), 4); + CHECK_EQUAL(result.size(), 4); + + CHECK_EQUAL(result, expected_result); + } + + TEST(test_ranges_concat_view_2) + { + std::vector vec{1, 2, 3, 4}; + std::list list{6, 7, 8, 9, 10}; + + auto cv = etl::views::concat(vec, list); + using result_type = std::vector; + result_type result = etl::views::concat(vec, list) | etl::ranges::to(); + + result_type expected_result{1, 2, 3, 4, 6, 7, 8, 9, 10}; + + CHECK_EQUAL(cv.size(), 9); + CHECK_EQUAL(result.size(), 9); + + CHECK_EQUAL(result, expected_result); + } + + TEST(test_ranges_concat_view_2_same_type) + { + std::vector vec0{1, 2, 3, 4}; + std::vector vec1{6, 7, 8, 9, 10}; + + auto cv = etl::views::concat(vec0, vec1); + + using result_type = std::vector; + result_type result = etl::views::concat(vec0, vec1) | etl::ranges::to(); + + std::vector expected_result{1, 2, 3, 4, 6, 7, 8, 9, 10}; + + CHECK_EQUAL(cv.size(), 9); + CHECK_EQUAL(result.size(), 9); + + CHECK_EQUAL(result, expected_result); + } + + TEST(test_ranges_concat_view_3) + { + std::vector vec{1, 2, 3, 4}; + std::list list{6, 7, 8, 9, 10}; + std::array arr{20, 21, 22}; + + auto cv = etl::views::concat(vec, list, arr); + + using result_type = std::vector; + result_type result = etl::views::concat(vec, list, arr) | etl::ranges::to(); + + std::vector expected_result{1, 2, 3, 4, 6, 7, 8, 9, 10, 20, 21, 22}; + + CHECK_EQUAL(cv.size(), 12); + CHECK_EQUAL(result.size(), 12); + + CHECK_EQUAL(result, expected_result); + + auto it = cv.begin(); + CHECK(it != cv.end()); + CHECK_EQUAL(*it, 1); + + ++it; + CHECK_EQUAL(*it, 2); + it++; + CHECK_EQUAL(*it, 3); + CHECK_EQUAL(it[0], 3); + CHECK_EQUAL(it[1], 4); + CHECK_EQUAL(it[2], 6); + it += 3; + CHECK_EQUAL(*it, 7); + it -= 2; + CHECK_EQUAL(*it, 4); + --it; + CHECK_EQUAL(*it, 3); + it--; + CHECK_EQUAL(*it, 2); + CHECK(it != cv.end()); + it += 8; + CHECK_EQUAL(*it, 20); + it += 2; + CHECK_EQUAL(*it, 22); + CHECK(it != cv.end()); + ++it; + CHECK(it == cv.end()); + } + + TEST(test_ranges_concat_view_3_same_type) + { + std::vector vec0{1, 2, 3, 4}; + std::vector vec1{6, 7, 8, 9, 10}; + std::vector vec2{20, 21, 22}; + + auto cv = etl::views::concat(vec0, vec1, vec2); + + using result_type = std::vector; + result_type result = etl::views::concat(vec0, vec1, vec2) | etl::ranges::to(); + + std::vector expected_result{1, 2, 3, 4, 6, 7, 8, 9, 10, 20, 21, 22}; + + CHECK_EQUAL(cv.size(), 12); + CHECK_EQUAL(result.size(), 12); + + CHECK_EQUAL(result, expected_result); + + auto it = cv.begin(); + CHECK(it != cv.end()); + CHECK_EQUAL(*it, 1); + + ++it; + CHECK_EQUAL(*it, 2); + it++; + CHECK_EQUAL(*it, 3); + CHECK_EQUAL(it[0], 3); + CHECK_EQUAL(it[1], 4); + CHECK_EQUAL(it[2], 6); + it += 3; + CHECK_EQUAL(*it, 7); + it -= 2; + CHECK_EQUAL(*it, 4); + --it; + CHECK_EQUAL(*it, 3); + it--; + CHECK_EQUAL(*it, 2); + CHECK(it != cv.end()); + it += 8; + CHECK_EQUAL(*it, 20); + it += 2; + CHECK_EQUAL(*it, 22); + CHECK(it != cv.end()); + ++it; + CHECK(it == cv.end()); + } + + TEST(test_ranges_concat_view_3_same_range) + { + std::vector vec0{1, 2, 3, 4, 5}; + + auto cv = etl::views::concat(vec0, vec0, vec0); + + using result_type = std::vector; + result_type result = etl::views::concat(vec0, vec0, vec0) | etl::ranges::to(); + + std::vector expected_result{1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5}; + + CHECK_EQUAL(cv.size(), 15); + CHECK_EQUAL(result.size(), 15); + + CHECK_EQUAL(result, expected_result); + + auto it = cv.begin(); + CHECK(it != cv.end()); + CHECK_EQUAL(*it, 1); + + ++it; + CHECK_EQUAL(*it, 2); + it++; + CHECK_EQUAL(*it, 3); + CHECK_EQUAL(it[0], 3); + CHECK_EQUAL(it[1], 4); + CHECK_EQUAL(it[2], 5); + it += 3; + CHECK_EQUAL(*it, 1); + it -= 2; + CHECK_EQUAL(*it, 4); + --it; + CHECK_EQUAL(*it, 3); + it--; + CHECK_EQUAL(*it, 2); + CHECK(it != cv.end()); + it += 11; + CHECK_EQUAL(*it, 3); + it += 2; + CHECK_EQUAL(*it, 5); + CHECK(it != cv.end()); + ++it; + CHECK(it == cv.end()); + } + + //************************************************************************* + // common_view tests + //************************************************************************* + + TEST(test_ranges_common_view_functional) + { + etl::vector v_in{ 0, 1, 2, 3, 4 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4 }; + + auto cv = etl::ranges::common_view(v_in); + for (int i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(*cv.begin(), 0); + CHECK_EQUAL(cv.size(), 5); + + CHECK_EQUAL(*cv.cbegin(), 0); + CHECK_EQUAL(cv.empty(), false); + CHECK_EQUAL(cv, true); + CHECK_EQUAL(cv.front(), 0); + CHECK_EQUAL(cv.back(), 4); + CHECK_EQUAL(cv[2], 2); + } + + TEST(test_ranges_common_view_from_view) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 3, 4, 5, 6, 7, 8, 9 }; + + auto cv = etl::ranges::common_view(etl::views::drop(v_in, 3)); + for (int i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(*cv.begin(), 3); + CHECK_EQUAL(cv.size(), 7); + CHECK_EQUAL(cv.empty(), false); + CHECK_EQUAL(cv.front(), 3); + CHECK_EQUAL(cv.back(), 9); + } + + TEST(test_ranges_common_view_pipe) + { + etl::vector v_in{ 0, 1, 2, 3, 4 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4 }; + + auto cv = v_in | etl::views::common; + for (int i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(*cv.begin(), 0); + CHECK_EQUAL(cv.size(), 5); + CHECK_EQUAL(cv.empty(), false); + CHECK_EQUAL(cv.front(), 0); + CHECK_EQUAL(cv.back(), 4); + } + + TEST(test_ranges_common_view_pipe_chain) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 4, 3, 2, 1, 0 }; + + auto cv = v_in | etl::views::take(5) | etl::views::reverse | etl::views::common; + for (int i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(cv.size(), 5); + CHECK_EQUAL(cv.front(), 4); + CHECK_EQUAL(cv.back(), 0); + } + + TEST(test_ranges_common_view_empty) + { + etl::vector v_in{}; + + auto cv = etl::ranges::common_view(v_in); + + CHECK_EQUAL(cv.size(), 0); + CHECK_EQUAL(cv.empty(), true); + CHECK_EQUAL(cv.begin(), cv.end()); + } + + TEST(test_ranges_common_view_same_begin_end_type) + { + // For a common range (begin/end same type), common_view should work as pass-through + etl::vector v_in{ 10, 20, 30 }; + + auto cv = etl::views::common(v_in); + auto it_begin = cv.begin(); + auto it_end = cv.end(); + + // Verify same type via compilation and correct values + CHECK(it_begin != it_end); + CHECK_EQUAL(*it_begin, 10); + CHECK_EQUAL(cv.size(), 3); + CHECK_EQUAL(cv.front(), 10); + CHECK_EQUAL(cv.back(), 30); + } + + TEST(test_ranges_common_view_iterate) + { + etl::vector v_in{ 1, 2, 3, 4, 5 }; + + using result_type = etl::vector; + result_type result = v_in | etl::views::common | etl::ranges::to(); + + result_type v_expected{ 1, 2, 3, 4, 5 }; + CHECK_EQUAL(result, v_expected); + } + + TEST(test_ranges_common_view_with_filter) + { + auto even = [](int i) -> bool { return 0 == i % 2; }; + + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + auto cv = v_in | etl::views::filter(even) | etl::views::common(); + for (int i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_common_view_with_transform) + { + auto times_two = [](int i) -> int { return i * 2; }; + + etl::vector v_in{ 1, 2, 3, 4, 5 }; + etl::vector v_out; + etl::vector v_out_expected{ 2, 4, 6, 8, 10 }; + + auto cv = v_in | etl::views::transform(times_two) | etl::views::common; + for (int i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + TEST(test_ranges_common_view_std_vector) + { + std::vector v_in{ 10, 20, 30, 40, 50 }; + std::vector v_out; + std::vector v_out_expected{ 10, 20, 30, 40, 50 }; + + auto cv = etl::ranges::common_view(etl::ranges::ref_view(v_in)); + for (int i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(cv.size(), 5); + CHECK_EQUAL(cv.front(), 10); + CHECK_EQUAL(cv.back(), 50); + } + + TEST(test_ranges_common_view_c_array) + { + int v_in[] = { 1, 2, 3, 4, 5 }; + etl::vector v_out; + etl::vector v_out_expected{ 1, 2, 3, 4, 5 }; + + auto cv = etl::ranges::common_view(etl::ranges::ref_view(v_in)); + for (int i : cv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(cv.size(), 5); + } + + //************************************************************************* + TEST(test_ranges_elements_of_from_lvalue) + { + std::vector v{ 1, 2, 3, 4, 5 }; + + auto eo = etl::ranges::elements_of{v}; + + // The wrapped range should refer to the same data + CHECK_EQUAL(v.size(), eo.range.size()); + CHECK_EQUAL(v[0], eo.range[0]); + CHECK_EQUAL(v[4], eo.range[4]); + } + + //************************************************************************* + TEST(test_ranges_elements_of_from_rvalue) + { + auto eo = etl::ranges::elements_of{std::vector{10, 20, 30}}; + + CHECK_EQUAL(3U, eo.range.size()); + CHECK_EQUAL(10, eo.range[0]); + CHECK_EQUAL(20, eo.range[1]); + CHECK_EQUAL(30, eo.range[2]); + } + + //************************************************************************* + TEST(test_ranges_elements_of_deduction_guide) + { + std::vector v{ 1, 2, 3 }; + + // CTAD should deduce elements_of&> + auto eo = etl::ranges::elements_of{v}; + (void)eo; + + // CTAD from rvalue should deduce elements_of&&> + auto eo2 = etl::ranges::elements_of{std::vector{4, 5, 6}}; + (void)eo2; + } + + //************************************************************************* + TEST(test_ranges_elements_of_c_array) + { + int arr[] = { 10, 20, 30, 40, 50 }; + + auto eo = etl::ranges::elements_of{arr}; + + CHECK_EQUAL(10, eo.range[0]); + CHECK_EQUAL(50, eo.range[4]); + } + + //************************************************************************* + TEST(test_ranges_elements_view_pair_first) + { + std::vector> v = {{1, 1.1}, {2, 2.2}, {3, 3.3}}; + + auto ev = etl::ranges::elements_view, 0>(etl::ranges::views::all(v)); + + auto it = ev.begin(); + CHECK_EQUAL(1, *it); ++it; + CHECK_EQUAL(2, *it); ++it; + CHECK_EQUAL(3, *it); ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_elements_view_pair_second) + { + std::vector> v = {{1, 1.1}, {2, 2.2}, {3, 3.3}}; + + auto ev = etl::ranges::elements_view, 1>(etl::ranges::views::all(v)); + + auto it = ev.begin(); + CHECK_CLOSE(1.1, *it, 0.001); ++it; + CHECK_CLOSE(2.2, *it, 0.001); ++it; + CHECK_CLOSE(3.3, *it, 0.001); ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_elements_view_tuple) + { + std::vector> v = { + etl::make_tuple(10, 1.5, 'a'), + etl::make_tuple(20, 2.5, 'b'), + etl::make_tuple(30, 3.5, 'c') + }; + + // Extract element 2 (char) + auto ev = etl::ranges::elements_view, 2>(etl::ranges::views::all(v)); + + auto it = ev.begin(); + CHECK_EQUAL('a', *it); ++it; + CHECK_EQUAL('b', *it); ++it; + CHECK_EQUAL('c', *it); ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_elements_view_size) + { + std::vector> v = {{1, 10}, {2, 20}, {3, 30}, {4, 40}}; + + auto ev = etl::ranges::elements_view, 0>(etl::ranges::views::all(v)); + + CHECK_EQUAL(4U, ev.size()); + } + + //************************************************************************* + TEST(test_ranges_elements_view_empty) + { + std::vector> v; + + auto ev = etl::ranges::elements_view, 0>(etl::ranges::views::all(v)); + + CHECK(ev.empty()); + CHECK(ev.begin() == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_views_elements_adaptor) + { + std::vector> v = {{1, 1.1}, {2, 2.2}, {3, 3.3}}; + + auto ev = etl::views::elements<0>(v); + + auto it = ev.begin(); + CHECK_EQUAL(1, *it); ++it; + CHECK_EQUAL(2, *it); ++it; + CHECK_EQUAL(3, *it); ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_views_elements_pipe) + { + std::vector> v = {{1, 1.1}, {2, 2.2}, {3, 3.3}}; + + auto ev = v | etl::views::elements<1>; + + auto it = ev.begin(); + CHECK_CLOSE(1.1, *it, 0.001); ++it; + CHECK_CLOSE(2.2, *it, 0.001); ++it; + CHECK_CLOSE(3.3, *it, 0.001); ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_views_keys) + { + std::vector> v = {{10, 1.1}, {20, 2.2}, {30, 3.3}}; + + auto ev = v | etl::views::keys; + + auto it = ev.begin(); + CHECK_EQUAL(10, *it); ++it; + CHECK_EQUAL(20, *it); ++it; + CHECK_EQUAL(30, *it); ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_views_values) + { + std::vector> v = {{10, 1.1}, {20, 2.2}, {30, 3.3}}; + + auto ev = v | etl::views::values; + + auto it = ev.begin(); + CHECK_CLOSE(1.1, *it, 0.001); ++it; + CHECK_CLOSE(2.2, *it, 0.001); ++it; + CHECK_CLOSE(3.3, *it, 0.001); ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_views_keys_direct_call) + { + std::vector> v = {{10, 1.1}, {20, 2.2}, {30, 3.3}}; + + auto ev = etl::views::keys(v); + + auto it = ev.begin(); + CHECK_EQUAL(10, *it); ++it; + CHECK_EQUAL(20, *it); ++it; + CHECK_EQUAL(30, *it); ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_views_values_direct_call) + { + std::vector> v = {{10, 1.1}, {20, 2.2}, {30, 3.3}}; + + auto ev = etl::views::values(v); + + auto it = ev.begin(); + CHECK_CLOSE(1.1, *it, 0.001); ++it; + CHECK_CLOSE(2.2, *it, 0.001); ++it; + CHECK_CLOSE(3.3, *it, 0.001); ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_elements_view_with_etl_tuple) + { + std::vector> v = { + etl::make_tuple(1, 10.0, 'x'), + etl::make_tuple(2, 20.0, 'y'), + etl::make_tuple(3, 30.0, 'z') + }; + + // Extract element 0 via pipe + auto ev0 = v | etl::views::elements<0>; + auto it0 = ev0.begin(); + CHECK_EQUAL(1, *it0); ++it0; + CHECK_EQUAL(2, *it0); ++it0; + CHECK_EQUAL(3, *it0); ++it0; + CHECK(it0 == ev0.end()); + + // Extract element 1 via pipe + auto ev1 = v | etl::views::elements<1>; + auto it1 = ev1.begin(); + CHECK_CLOSE(10.0, *it1, 0.001); ++it1; + CHECK_CLOSE(20.0, *it1, 0.001); ++it1; + CHECK_CLOSE(30.0, *it1, 0.001); ++it1; + CHECK(it1 == ev1.end()); + + // Extract element 2 via pipe + auto ev2 = v | etl::views::elements<2>; + auto it2 = ev2.begin(); + CHECK_EQUAL('x', *it2); ++it2; + CHECK_EQUAL('y', *it2); ++it2; + CHECK_EQUAL('z', *it2); ++it2; + CHECK(it2 == ev2.end()); + } + + //************************************************************************* + TEST(test_ranges_enumerate_view_basic) + { + std::vector v = {10, 20, 30}; + auto ev = etl::ranges::enumerate_view(etl::ranges::views::all(v)); + + auto it = ev.begin(); + CHECK_EQUAL(0U, etl::get<0>(*it)); + CHECK_EQUAL(10, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(1U, etl::get<0>(*it)); + CHECK_EQUAL(20, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2U, etl::get<0>(*it)); + CHECK_EQUAL(30, etl::get<1>(*it)); + ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_enumerate_view_pipe) + { + std::vector v = {"a", "b", "c"}; + auto ev = v | etl::views::enumerate; + + auto it = ev.begin(); + CHECK_EQUAL(0U, etl::get<0>(*it)); + CHECK_EQUAL(std::string("a"), etl::get<1>(*it)); + ++it; + CHECK_EQUAL(1U, etl::get<0>(*it)); + CHECK_EQUAL(std::string("b"), etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2U, etl::get<0>(*it)); + CHECK_EQUAL(std::string("c"), etl::get<1>(*it)); + ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_enumerate_view_empty) + { + std::vector v; + auto ev = v | etl::views::enumerate; + + CHECK(ev.begin() == ev.end()); + CHECK_EQUAL(0U, ev.size()); + } + + //************************************************************************* + TEST(test_ranges_enumerate_view_size) + { + std::vector v = {1, 2, 3, 4, 5}; + auto ev = v | etl::views::enumerate; + + CHECK_EQUAL(5U, ev.size()); + } + + //************************************************************************* + TEST(test_ranges_enumerate_view_range_for) + { + std::vector v = {100, 200, 300}; + auto ev = v | etl::views::enumerate; + + size_t expected_index = 0; + int expected_values[] = {100, 200, 300}; + + for (auto&& [idx, val] : ev) + { + CHECK_EQUAL(expected_index, idx); + CHECK_EQUAL(expected_values[expected_index], val); + ++expected_index; + } + + CHECK_EQUAL(3U, expected_index); + } + + //************************************************************************* + TEST(test_ranges_enumerate_view_chained_with_take) + { + std::vector v = {10, 20, 30, 40, 50}; + auto ev = v | etl::views::enumerate | etl::views::take(3); + + auto it = ev.begin(); + CHECK_EQUAL(0U, etl::get<0>(*it)); + CHECK_EQUAL(10, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(1U, etl::get<0>(*it)); + CHECK_EQUAL(20, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2U, etl::get<0>(*it)); + CHECK_EQUAL(30, etl::get<1>(*it)); + ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_enumerate_view_single_element) + { + std::vector v = {42}; + auto ev = v | etl::views::enumerate; + + auto it = ev.begin(); + CHECK_EQUAL(0U, etl::get<0>(*it)); + CHECK_EQUAL(42, etl::get<1>(*it)); + ++it; + CHECK(it == ev.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_view_basic) + { + std::vector v1 = {1, 2, 3}; + std::vector v2 = {"a", "b", "c"}; + + auto zv = etl::views::zip(v1, v2); + + auto it = zv.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL(std::string("a"), etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL(std::string("b"), etl::get<1>(*it)); + ++it; + CHECK_EQUAL(3, etl::get<0>(*it)); + CHECK_EQUAL(std::string("c"), etl::get<1>(*it)); + ++it; + CHECK(it == zv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_view_different_lengths) + { + std::vector v1 = {1, 2, 3, 4, 5}; + std::vector v2 = {10.0, 20.0, 30.0}; + + auto zv = etl::views::zip(v1, v2); + + CHECK_EQUAL(3U, zv.size()); + + auto it = zv.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_CLOSE(10.0, etl::get<1>(*it), 0.01); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_CLOSE(20.0, etl::get<1>(*it), 0.01); + ++it; + CHECK_EQUAL(3, etl::get<0>(*it)); + CHECK_CLOSE(30.0, etl::get<1>(*it), 0.01); + ++it; + CHECK(it == zv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_view_three_ranges) + { + std::vector v1 = {1, 2, 3}; + std::vector v2 = {1.5, 2.5, 3.5}; + std::array v3 = {'x', 'y', 'z'}; + + auto zv = etl::views::zip(v1, v2, v3); + + CHECK_EQUAL(3U, zv.size()); + + auto it = zv.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_CLOSE(1.5, etl::get<1>(*it), 0.01); + CHECK_EQUAL('x', etl::get<2>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_CLOSE(2.5, etl::get<1>(*it), 0.01); + CHECK_EQUAL('y', etl::get<2>(*it)); + ++it; + CHECK_EQUAL(3, etl::get<0>(*it)); + CHECK_CLOSE(3.5, etl::get<1>(*it), 0.01); + CHECK_EQUAL('z', etl::get<2>(*it)); + ++it; + CHECK(it == zv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_view_empty) + { + std::vector v1; + std::vector v2 = {1.0, 2.0}; + + auto zv = etl::views::zip(v1, v2); + + CHECK_EQUAL(0U, zv.size()); + CHECK(zv.begin() == zv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_view_single_range) + { + std::vector v1 = {10, 20, 30}; + + auto zv = etl::views::zip(v1); + + CHECK_EQUAL(3U, zv.size()); + + auto it = zv.begin(); + CHECK_EQUAL(10, etl::get<0>(*it)); + ++it; + CHECK_EQUAL(20, etl::get<0>(*it)); + ++it; + CHECK_EQUAL(30, etl::get<0>(*it)); + ++it; + CHECK(it == zv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_view_range_for) + { + std::vector v1 = {1, 2, 3}; + std::vector v2 = {10, 20, 30}; + + auto zv = etl::views::zip(v1, v2); + + int expected_first[] = {1, 2, 3}; + int expected_second[] = {10, 20, 30}; + size_t index = 0; + + for (auto&& [a, b] : zv) + { + CHECK_EQUAL(expected_first[index], a); + CHECK_EQUAL(expected_second[index], b); + ++index; + } + + CHECK_EQUAL(3U, index); + } + + //************************************************************************* + TEST(test_ranges_zip_view_with_list) + { + std::vector v1 = {1, 2, 3, 4}; + std::list l1 = {100, 200, 300}; + + auto zv = etl::views::zip(v1, l1); + + CHECK_EQUAL(3U, zv.size()); + + auto it = zv.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL(100, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL(200, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(3, etl::get<0>(*it)); + CHECK_EQUAL(300, etl::get<1>(*it)); + ++it; + CHECK(it == zv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_view_chained_with_take) + { + std::vector v1 = {1, 2, 3, 4, 5}; + std::vector v2 = {10, 20, 30, 40, 50}; + + auto zv = etl::views::zip(v1, v2) | etl::views::take(2); + + auto it = zv.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL(10, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL(20, etl::get<1>(*it)); + ++it; + CHECK(it == zv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_transform_view_basic) + { + std::vector v1 = {1, 2, 3}; + std::vector v2 = {10, 20, 30}; + + auto ztv = etl::views::zip_transform([](int a, int b) { return a + b; }, v1, v2); + + auto it = ztv.begin(); + CHECK_EQUAL(11, *it); + ++it; + CHECK_EQUAL(22, *it); + ++it; + CHECK_EQUAL(33, *it); + ++it; + CHECK(it == ztv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_transform_view_different_lengths) + { + std::vector v1 = {1, 2, 3, 4, 5}; + std::vector v2 = {10.5, 20.5, 30.5}; + + auto ztv = etl::views::zip_transform([](int a, double b) { return a + b; }, v1, v2); + + CHECK_EQUAL(3U, ztv.size()); + + auto it = ztv.begin(); + CHECK_CLOSE(11.5, *it, 0.01); + ++it; + CHECK_CLOSE(22.5, *it, 0.01); + ++it; + CHECK_CLOSE(33.5, *it, 0.01); + ++it; + CHECK(it == ztv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_transform_view_three_ranges) + { + std::vector v1 = {1, 2, 3}; + std::vector v2 = {10, 20, 30}; + std::vector v3 = {100, 200, 300}; + + auto ztv = etl::views::zip_transform([](int a, int b, int c) { return a + b + c; }, v1, v2, v3); + + CHECK_EQUAL(3U, ztv.size()); + + auto it = ztv.begin(); + CHECK_EQUAL(111, *it); + ++it; + CHECK_EQUAL(222, *it); + ++it; + CHECK_EQUAL(333, *it); + ++it; + CHECK(it == ztv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_transform_view_empty) + { + std::vector v1; + std::vector v2 = {1, 2, 3}; + + auto ztv = etl::views::zip_transform([](int a, int b) { return a * b; }, v1, v2); + + CHECK_EQUAL(0U, ztv.size()); + CHECK(ztv.begin() == ztv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_transform_view_single_range) + { + std::vector v1 = {10, 20, 30}; + + auto ztv = etl::views::zip_transform([](int a) { return a * 2; }, v1); + + CHECK_EQUAL(3U, ztv.size()); + + auto it = ztv.begin(); + CHECK_EQUAL(20, *it); + ++it; + CHECK_EQUAL(40, *it); + ++it; + CHECK_EQUAL(60, *it); + ++it; + CHECK(it == ztv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_transform_view_range_for) + { + std::vector v1 = {1, 2, 3}; + std::vector v2 = {10, 20, 30}; + + auto ztv = etl::views::zip_transform([](int a, int b) { return a * b; }, v1, v2); + + int expected[] = {10, 40, 90}; + size_t index = 0; + + for (auto val : ztv) + { + CHECK_EQUAL(expected[index], val); + ++index; + } + + CHECK_EQUAL(3U, index); + } + + //************************************************************************* + TEST(test_ranges_zip_transform_view_with_list) + { + std::vector v1 = {1, 2, 3, 4}; + std::list l1 = {100, 200, 300}; + + auto ztv = etl::views::zip_transform([](int a, int b) { return a + b; }, v1, l1); + + CHECK_EQUAL(3U, ztv.size()); + + auto it = ztv.begin(); + CHECK_EQUAL(101, *it); + ++it; + CHECK_EQUAL(202, *it); + ++it; + CHECK_EQUAL(303, *it); + ++it; + CHECK(it == ztv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_transform_view_chained_with_take) + { + std::vector v1 = {1, 2, 3, 4, 5}; + std::vector v2 = {10, 20, 30, 40, 50}; + + auto ztv = etl::views::zip_transform([](int a, int b) { return a + b; }, v1, v2) | etl::views::take(2); + + auto it = ztv.begin(); + CHECK_EQUAL(11, *it); + ++it; + CHECK_EQUAL(22, *it); + ++it; + CHECK(it == ztv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_transform_view_string_concat) + { + std::vector v1 = {"hello", "good", "nice"}; + std::vector v2 = {" world", " morning", " day"}; + + auto ztv = etl::views::zip_transform([](const std::string& a, const std::string& b) { return a + b; }, v1, v2); + + auto it = ztv.begin(); + CHECK_EQUAL(std::string("hello world"), *it); + ++it; + CHECK_EQUAL(std::string("good morning"), *it); + ++it; + CHECK_EQUAL(std::string("nice day"), *it); + ++it; + CHECK(it == ztv.end()); + } + + //************************************************************************* + TEST(test_ranges_zip_transform_view_returns_different_type) + { + std::vector v1 = {1, 2, 3}; + std::vector v2 = {4, 5, 6}; + + auto ztv = etl::views::zip_transform([](int a, int b) -> double { return static_cast(a) / b; }, v1, v2); + + auto it = ztv.begin(); + CHECK_CLOSE(0.25, *it, 0.01); + ++it; + CHECK_CLOSE(0.4, *it, 0.01); + ++it; + CHECK_CLOSE(0.5, *it, 0.01); + ++it; + CHECK(it == ztv.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_basic) + { + std::vector v = {1, 2, 3, 4, 5}; + + auto av = etl::views::adjacent<2>(v); + + CHECK_EQUAL(4U, av.size()); + + auto it = av.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL(2, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL(3, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(3, etl::get<0>(*it)); + CHECK_EQUAL(4, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(4, etl::get<0>(*it)); + CHECK_EQUAL(5, etl::get<1>(*it)); + ++it; + CHECK(it == av.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_triple) + { + std::vector v = {10, 20, 30, 40, 50}; + + auto av = etl::views::adjacent<3>(v); + + CHECK_EQUAL(3U, av.size()); + + auto it = av.begin(); + CHECK_EQUAL(10, etl::get<0>(*it)); + CHECK_EQUAL(20, etl::get<1>(*it)); + CHECK_EQUAL(30, etl::get<2>(*it)); + ++it; + CHECK_EQUAL(20, etl::get<0>(*it)); + CHECK_EQUAL(30, etl::get<1>(*it)); + CHECK_EQUAL(40, etl::get<2>(*it)); + ++it; + CHECK_EQUAL(30, etl::get<0>(*it)); + CHECK_EQUAL(40, etl::get<1>(*it)); + CHECK_EQUAL(50, etl::get<2>(*it)); + ++it; + CHECK(it == av.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_single_window) + { + std::vector v = {1}; + + auto av = etl::views::adjacent<1>(v); + + CHECK_EQUAL(1U, av.size()); + + auto it = av.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + ++it; + CHECK(it == av.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_window_equals_range_size) + { + std::vector v = {1, 2, 3}; + + auto av = etl::views::adjacent<3>(v); + + CHECK_EQUAL(1U, av.size()); + + auto it = av.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL(2, etl::get<1>(*it)); + CHECK_EQUAL(3, etl::get<2>(*it)); + ++it; + CHECK(it == av.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_window_larger_than_range) + { + std::vector v = {1, 2}; + + auto av = etl::views::adjacent<5>(v); + + CHECK_EQUAL(0U, av.size()); + CHECK(av.begin() == av.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_empty_range) + { + std::vector v; + + auto av = etl::views::adjacent<2>(v); + + CHECK_EQUAL(0U, av.size()); + CHECK(av.begin() == av.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_range_for) + { + std::vector v = {1, 2, 3, 4}; + + auto av = etl::views::adjacent<2>(v); + + std::vector firsts; + std::vector seconds; + + for (auto [a, b] : av) + { + firsts.push_back(a); + seconds.push_back(b); + } + + CHECK_EQUAL(3U, firsts.size()); + CHECK_EQUAL(1, firsts[0]); + CHECK_EQUAL(2, firsts[1]); + CHECK_EQUAL(3, firsts[2]); + CHECK_EQUAL(2, seconds[0]); + CHECK_EQUAL(3, seconds[1]); + CHECK_EQUAL(4, seconds[2]); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_pairwise) + { + std::vector v = {1, 2, 3, 4}; + + auto av = etl::views::pairwise(v); + + CHECK_EQUAL(3U, av.size()); + + auto it = av.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL(2, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL(3, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(3, etl::get<0>(*it)); + CHECK_EQUAL(4, etl::get<1>(*it)); + ++it; + CHECK(it == av.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_pipe) + { + std::vector v = {10, 20, 30, 40, 50}; + + auto av = v | etl::views::adjacent<2>(); + + CHECK_EQUAL(4U, av.size()); + + auto it = av.begin(); + CHECK_EQUAL(10, etl::get<0>(*it)); + CHECK_EQUAL(20, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(20, etl::get<0>(*it)); + CHECK_EQUAL(30, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(30, etl::get<0>(*it)); + CHECK_EQUAL(40, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(40, etl::get<0>(*it)); + CHECK_EQUAL(50, etl::get<1>(*it)); + ++it; + CHECK(it == av.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_chained_with_take) + { + std::vector v = {1, 2, 3, 4, 5}; + + auto av = v | etl::views::adjacent<2>() | etl::views::take(2); + + auto it = av.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL(2, etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL(3, etl::get<1>(*it)); + ++it; + CHECK(it == av.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_with_strings) + { + std::vector v = {"a", "b", "c", "d"}; + + auto av = etl::views::adjacent<2>(v); + + CHECK_EQUAL(3U, av.size()); + + auto it = av.begin(); + CHECK_EQUAL(std::string("a"), etl::get<0>(*it)); + CHECK_EQUAL(std::string("b"), etl::get<1>(*it)); + ++it; + CHECK_EQUAL(std::string("b"), etl::get<0>(*it)); + CHECK_EQUAL(std::string("c"), etl::get<1>(*it)); + ++it; + CHECK_EQUAL(std::string("c"), etl::get<0>(*it)); + CHECK_EQUAL(std::string("d"), etl::get<1>(*it)); + ++it; + CHECK(it == av.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_view_quad) + { + std::vector v = {1, 2, 3, 4, 5, 6}; + + auto av = etl::views::adjacent<4>(v); + + CHECK_EQUAL(3U, av.size()); + + auto it = av.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL(2, etl::get<1>(*it)); + CHECK_EQUAL(3, etl::get<2>(*it)); + CHECK_EQUAL(4, etl::get<3>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL(3, etl::get<1>(*it)); + CHECK_EQUAL(4, etl::get<2>(*it)); + CHECK_EQUAL(5, etl::get<3>(*it)); + ++it; + CHECK_EQUAL(3, etl::get<0>(*it)); + CHECK_EQUAL(4, etl::get<1>(*it)); + CHECK_EQUAL(5, etl::get<2>(*it)); + CHECK_EQUAL(6, etl::get<3>(*it)); + ++it; + CHECK(it == av.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_basic_sum) + { + std::vector v = {1, 2, 3, 4, 5}; + + auto atv = etl::views::adjacent_transform<2>(v, [](int a, int b) { return a + b; }); + + CHECK_EQUAL(4U, atv.size()); + + auto it = atv.begin(); + CHECK_EQUAL(3, *it); // 1+2 + ++it; + CHECK_EQUAL(5, *it); // 2+3 + ++it; + CHECK_EQUAL(7, *it); // 3+4 + ++it; + CHECK_EQUAL(9, *it); // 4+5 + ++it; + CHECK(it == atv.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_triple_product) + { + std::vector v = {1, 2, 3, 4, 5}; + + auto atv = etl::views::adjacent_transform<3>(v, [](int a, int b, int c) { return a * b * c; }); + + CHECK_EQUAL(3U, atv.size()); + + auto it = atv.begin(); + CHECK_EQUAL(6, *it); // 1*2*3 + ++it; + CHECK_EQUAL(24, *it); // 2*3*4 + ++it; + CHECK_EQUAL(60, *it); // 3*4*5 + ++it; + CHECK(it == atv.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_single_window) + { + std::vector v = {10, 20, 30}; + + // Window size 1: function applied to each element individually + auto atv = etl::views::adjacent_transform<1>(v, [](int a) { return a * 2; }); + + CHECK_EQUAL(3U, atv.size()); + + auto it = atv.begin(); + CHECK_EQUAL(20, *it); + ++it; + CHECK_EQUAL(40, *it); + ++it; + CHECK_EQUAL(60, *it); + ++it; + CHECK(it == atv.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_window_equals_range_size) + { + std::vector v = {1, 2, 3}; + + auto atv = etl::views::adjacent_transform<3>(v, [](int a, int b, int c) { return a + b + c; }); + + CHECK_EQUAL(1U, atv.size()); + + auto it = atv.begin(); + CHECK_EQUAL(6, *it); // 1+2+3 + ++it; + CHECK(it == atv.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_window_larger_than_range) + { + std::vector v = {1, 2}; + + auto atv = etl::views::adjacent_transform<3>(v, [](int a, int b, int c) { return a + b + c; }); + + CHECK_EQUAL(0U, atv.size()); + CHECK(atv.begin() == atv.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_empty_range) + { + std::vector v = {}; + + auto atv = etl::views::adjacent_transform<2>(v, [](int a, int b) { return a + b; }); + + CHECK_EQUAL(0U, atv.size()); + CHECK(atv.begin() == atv.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_range_for) + { + std::vector v = {1, 2, 3, 4, 5}; + std::vector expected = {3, 5, 7, 9}; + + auto atv = etl::views::adjacent_transform<2>(v, [](int a, int b) { return a + b; }); + + std::vector result; + for (auto val : atv) + { + result.push_back(val); + } + + CHECK_EQUAL(expected.size(), result.size()); + for (size_t i = 0; i < expected.size(); ++i) + { + CHECK_EQUAL(expected[i], result[i]); + } + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_pairwise_transform) + { + std::vector v = {10, 20, 30, 40}; + + auto atv = etl::views::pairwise_transform(v, [](int a, int b) { return b - a; }); + + CHECK_EQUAL(3U, atv.size()); + + auto it = atv.begin(); + CHECK_EQUAL(10, *it); // 20-10 + ++it; + CHECK_EQUAL(10, *it); // 30-20 + ++it; + CHECK_EQUAL(10, *it); // 40-30 + ++it; + CHECK(it == atv.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_pipe) + { + std::vector v = {1, 2, 3, 4, 5}; + + auto sum = [](int a, int b) { return a + b; }; + auto atv = v | etl::views::adjacent_transform<2>(sum); + + CHECK_EQUAL(4U, atv.size()); + + auto it = atv.begin(); + CHECK_EQUAL(3, *it); // 1+2 + ++it; + CHECK_EQUAL(5, *it); // 2+3 + ++it; + CHECK_EQUAL(7, *it); // 3+4 + ++it; + CHECK_EQUAL(9, *it); // 4+5 + ++it; + CHECK(it == atv.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_chained_with_take) + { + std::vector v = {1, 2, 3, 4, 5}; + + auto sum = [](int a, int b) { return a + b; }; + auto atv = v | etl::views::adjacent_transform<2>(sum) | etl::views::take(2); + + auto it = atv.begin(); + CHECK_EQUAL(3, *it); // 1+2 + ++it; + CHECK_EQUAL(5, *it); // 2+3 + ++it; + CHECK(it == atv.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_different_return_type) + { + std::vector v = {1, 2, 3, 4}; + + // Returns double instead of int + auto avg = [](int a, int b) { return (a + b) / 2.0; }; + auto atv = etl::views::adjacent_transform<2>(v, avg); + + CHECK_EQUAL(3U, atv.size()); + + auto it = atv.begin(); + CHECK_CLOSE(1.5, *it, 0.001); // (1+2)/2.0 + ++it; + CHECK_CLOSE(2.5, *it, 0.001); // (2+3)/2.0 + ++it; + CHECK_CLOSE(3.5, *it, 0.001); // (3+4)/2.0 + ++it; + CHECK(it == atv.end()); + } + + //************************************************************************* + TEST(test_ranges_adjacent_transform_view_pairwise_transform_pipe) + { + std::vector v = {5, 10, 15, 20}; + + auto diff = [](int a, int b) { return b - a; }; + auto atv = v | etl::views::pairwise_transform(diff); + + CHECK_EQUAL(3U, atv.size()); + + auto it = atv.begin(); + CHECK_EQUAL(5, *it); + ++it; + CHECK_EQUAL(5, *it); + ++it; + CHECK_EQUAL(5, *it); + ++it; + CHECK(it == atv.end()); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_basic) + { + std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto cv = etl::ranges::chunk_view(v, 3); + + std::vector> expected{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_remainder) + { + // Range size not evenly divisible by chunk size + std::vector v = {1, 2, 3, 4, 5, 6, 7}; + auto cv = etl::ranges::chunk_view(v, 3); + + std::vector> expected{{1, 2, 3}, {4, 5, 6}, {7}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_single_element_chunks) + { + std::vector v = {10, 20, 30}; + auto cv = etl::ranges::chunk_view(v, 1); + + std::vector> expected{{10}, {20}, {30}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_chunk_larger_than_range) + { + std::vector v = {1, 2, 3}; + auto cv = etl::ranges::chunk_view(v, 10); + + size_t count = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + std::vector expected{1, 2, 3}; + CHECK_EQUAL(expected, actual); + ++count; + } + CHECK_EQUAL(1U, count); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_empty_range) + { + std::vector v; + auto cv = etl::ranges::chunk_view(v, 3); + + size_t count = 0; + for (auto chunk : cv) + { + (void)chunk; + ++count; + } + CHECK_EQUAL(0U, count); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_pipe) + { + std::vector v = {1, 2, 3, 4, 5, 6}; + auto cv = v | etl::views::chunk(2); + + std::vector> expected{{1, 2}, {3, 4}, {5, 6}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_pipe_with_remainder) + { + std::vector v = {1, 2, 3, 4, 5}; + auto cv = v | etl::views::chunk(2); + + std::vector> expected{{1, 2}, {3, 4}, {5}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_functional_call) + { + std::vector v = {1, 2, 3, 4, 5, 6}; + auto cv = etl::views::chunk(v, 3); + + std::vector> expected{{1, 2, 3}, {4, 5, 6}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_etl_vector) + { + etl::vector v = {1, 2, 3, 4, 5, 6, 7, 8}; + auto cv = etl::ranges::chunk_view(v, 3); + + std::vector> expected{{1, 2, 3}, {4, 5, 6}, {7, 8}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_array) + { + std::array a = {10, 20, 30, 40, 50, 60}; + auto cv = etl::ranges::chunk_view(a, 2); + + std::vector> expected{{10, 20}, {30, 40}, {50, 60}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_pipe_chain_take_chunk) + { + // Take first 6, then chunk by 2 + std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto cv = v | etl::views::take(6) | etl::views::chunk(2); + + std::vector> expected{{1, 2}, {3, 4}, {5, 6}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_view_pipe_chain_drop_chunk) + { + // Drop first 2, then chunk by 3 + std::vector v = {1, 2, 3, 4, 5, 6, 7, 8}; + auto cv = v | etl::views::drop(2) | etl::views::chunk(3); + + std::vector> expected{{3, 4, 5}, {6, 7, 8}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + // slide_view tests + //************************************************************************* + + //************************************************************************* + TEST(test_ranges_slide_view_basic) + { + std::vector v = {1, 2, 3, 4, 5}; + auto sv = etl::ranges::slide_view(v, 3); + + std::vector> expected{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}}; + size_t idx = 0; + for (auto window : sv) + { + std::vector actual(window.begin(), window.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_slide_view_window_size_1) + { + std::vector v = {10, 20, 30}; + auto sv = etl::ranges::slide_view(v, 1); + + std::vector> expected{{10}, {20}, {30}}; + size_t idx = 0; + for (auto window : sv) + { + std::vector actual(window.begin(), window.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_slide_view_window_equals_range) + { + std::vector v = {1, 2, 3}; + auto sv = etl::ranges::slide_view(v, 3); + + size_t count = 0; + for (auto window : sv) + { + std::vector actual(window.begin(), window.end()); + std::vector expected{1, 2, 3}; + CHECK_EQUAL(expected, actual); + ++count; + } + CHECK_EQUAL(1U, count); + } + + //************************************************************************* + TEST(test_ranges_slide_view_window_larger_than_range) + { + std::vector v = {1, 2, 3}; + auto sv = etl::ranges::slide_view(v, 5); + + size_t count = 0; + for (auto window : sv) + { + (void)window; + ++count; + } + CHECK_EQUAL(0U, count); + } + + //************************************************************************* + TEST(test_ranges_slide_view_empty_range) + { + std::vector v; + auto sv = etl::ranges::slide_view(v, 3); + + size_t count = 0; + for (auto window : sv) + { + (void)window; + ++count; + } + CHECK_EQUAL(0U, count); + } + + //************************************************************************* + TEST(test_ranges_slide_view_size) + { + std::vector v = {1, 2, 3, 4, 5}; + + CHECK_EQUAL(3U, etl::ranges::slide_view(v, 3).size()); + CHECK_EQUAL(5U, etl::ranges::slide_view(v, 1).size()); + CHECK_EQUAL(1U, etl::ranges::slide_view(v, 5).size()); + CHECK_EQUAL(0U, etl::ranges::slide_view(v, 6).size()); + } + + //************************************************************************* + TEST(test_ranges_slide_view_pipe) + { + std::vector v = {1, 2, 3, 4, 5}; + auto sv = v | etl::views::slide(3); + + std::vector> expected{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}}; + size_t idx = 0; + for (auto window : sv) + { + std::vector actual(window.begin(), window.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_slide_view_functional_call) + { + std::vector v = {1, 2, 3, 4, 5}; + auto sv = etl::views::slide(v, 2); + + std::vector> expected{{1, 2}, {2, 3}, {3, 4}, {4, 5}}; + size_t idx = 0; + for (auto window : sv) + { + std::vector actual(window.begin(), window.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_slide_view_etl_vector) + { + etl::vector v = {1, 2, 3, 4, 5, 6}; + auto sv = etl::ranges::slide_view(v, 4); + + std::vector> expected{{1, 2, 3, 4}, {2, 3, 4, 5}, {3, 4, 5, 6}}; + size_t idx = 0; + for (auto window : sv) + { + std::vector actual(window.begin(), window.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_slide_view_array) + { + std::array a = {10, 20, 30, 40, 50}; + auto sv = etl::ranges::slide_view(a, 2); + + std::vector> expected{{10, 20}, {20, 30}, {30, 40}, {40, 50}}; + size_t idx = 0; + for (auto window : sv) + { + std::vector actual(window.begin(), window.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_slide_view_pipe_chain_take_slide) + { + // Take first 5, then slide with window 3 + std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto sv = v | etl::views::take(5) | etl::views::slide(3); + + std::vector> expected{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}}; + size_t idx = 0; + for (auto window : sv) + { + std::vector actual(window.begin(), window.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_slide_view_pipe_chain_drop_slide) + { + // Drop first 2, then slide with window 3 + std::vector v = {1, 2, 3, 4, 5, 6, 7}; + auto sv = v | etl::views::drop(2) | etl::views::slide(3); + + std::vector> expected{{3, 4, 5}, {4, 5, 6}, {5, 6, 7}}; + size_t idx = 0; + for (auto window : sv) + { + std::vector actual(window.begin(), window.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + // chunk_by_view tests + //************************************************************************* + + //************************************************************************* + TEST(test_ranges_chunk_by_view_basic) + { + // Group consecutive equal elements + std::vector v = {1, 1, 2, 2, 2, 3, 3, 1}; + auto cv = etl::ranges::chunk_by_view(v, [](int a, int b) { return a == b; }); + + std::vector> expected{{1, 1}, {2, 2, 2}, {3, 3}, {1}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_by_view_less_than) + { + // Split where values stop being strictly increasing + std::vector v = {1, 2, 3, 1, 2, 1}; + auto cv = etl::ranges::chunk_by_view(v, [](int a, int b) { return a < b; }); + + std::vector> expected{{1, 2, 3}, {1, 2}, {1}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_by_view_single_element) + { + std::vector v = {42}; + auto cv = etl::ranges::chunk_by_view(v, [](int a, int b) { return a == b; }); + + size_t count = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + std::vector expected{42}; + CHECK_EQUAL(expected, actual); + ++count; + } + CHECK_EQUAL(1U, count); + } + + //************************************************************************* + TEST(test_ranges_chunk_by_view_empty_range) + { + std::vector v; + auto cv = etl::ranges::chunk_by_view(v, [](int a, int b) { return a == b; }); + + size_t count = 0; + for (auto chunk : cv) + { + (void)chunk; + ++count; + } + CHECK_EQUAL(0U, count); + } + + //************************************************************************* + TEST(test_ranges_chunk_by_view_all_same) + { + // All elements satisfy predicate => single chunk + std::vector v = {5, 5, 5, 5}; + auto cv = etl::ranges::chunk_by_view(v, [](int a, int b) { return a == b; }); + + size_t count = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + std::vector expected{5, 5, 5, 5}; + CHECK_EQUAL(expected, actual); + ++count; + } + CHECK_EQUAL(1U, count); + } + + //************************************************************************* + TEST(test_ranges_chunk_by_view_all_different) + { + // No adjacent pair satisfies predicate => each element is its own chunk + std::vector v = {1, 2, 1, 2, 1}; + auto cv = etl::ranges::chunk_by_view(v, [](int a, int b) { return a == b; }); + + std::vector> expected{{1}, {2}, {1}, {2}, {1}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_by_view_pipe) + { + std::vector v = {1, 1, 2, 2, 3}; + auto cv = v | etl::views::chunk_by([](int a, int b) { return a == b; }); + + std::vector> expected{{1, 1}, {2, 2}, {3}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_by_view_functional_call) + { + std::vector v = {1, 2, 3, 1, 2}; + auto cv = etl::views::chunk_by(v, [](int a, int b) { return a < b; }); + + std::vector> expected{{1, 2, 3}, {1, 2}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_by_view_etl_vector) + { + etl::vector v = {1, 1, 2, 3, 3}; + auto cv = etl::ranges::chunk_by_view(v, [](int a, int b) { return a == b; }); + + std::vector> expected{{1, 1}, {2}, {3, 3}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_by_view_array) + { + std::array a = {1, 1, 2, 2, 3, 3}; + auto cv = etl::ranges::chunk_by_view(a, [](int x, int y) { return x == y; }); + + std::vector> expected{{1, 1}, {2, 2}, {3, 3}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_by_view_pipe_chain_take_chunk_by) + { + // Take first 5, then chunk_by equal + std::vector v = {1, 1, 2, 2, 3, 3, 4}; + auto cv = v | etl::views::take(5) | etl::views::chunk_by([](int a, int b) { return a == b; }); + + std::vector> expected{{1, 1}, {2, 2}, {3}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + TEST(test_ranges_chunk_by_view_pipe_chain_drop_chunk_by) + { + // Drop first 2, then chunk_by strictly increasing + std::vector v = {1, 1, 2, 3, 1, 2}; + auto cv = v | etl::views::drop(2) | etl::views::chunk_by([](int a, int b) { return a < b; }); + + std::vector> expected{{2, 3}, {1, 2}}; + size_t idx = 0; + for (auto chunk : cv) + { + std::vector actual(chunk.begin(), chunk.end()); + CHECK_EQUAL(expected[idx], actual); + ++idx; + } + CHECK_EQUAL(expected.size(), idx); + } + + //************************************************************************* + // stride_view tests + //************************************************************************* + + //************************************************************************* + TEST(test_ranges_stride_view_basic) + { + std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto sv = etl::ranges::stride_view(v, 3); + + std::vector expected{1, 4, 7}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_stride_of_one) + { + std::vector v = {10, 20, 30}; + auto sv = etl::ranges::stride_view(v, 1); + + std::vector expected{10, 20, 30}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_stride_of_two) + { + std::vector v = {1, 2, 3, 4, 5, 6}; + auto sv = etl::ranges::stride_view(v, 2); + + std::vector expected{1, 3, 5}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_stride_larger_than_range) + { + std::vector v = {1, 2, 3}; + auto sv = etl::ranges::stride_view(v, 10); + + std::vector expected{1}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_stride_equals_range_size) + { + std::vector v = {1, 2, 3}; + auto sv = etl::ranges::stride_view(v, 3); + + std::vector expected{1}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_empty_range) + { + std::vector v; + auto sv = etl::ranges::stride_view(v, 3); + + size_t count = 0; + for (auto val : sv) + { + (void)val; + ++count; + } + CHECK_EQUAL(0U, count); + } + + //************************************************************************* + TEST(test_ranges_stride_view_single_element) + { + std::vector v = {42}; + auto sv = etl::ranges::stride_view(v, 5); + + std::vector expected{42}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_pipe) + { + std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto sv = v | etl::views::stride(3); + + std::vector expected{1, 4, 7}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_pipe_stride_of_two) + { + std::vector v = {10, 20, 30, 40, 50}; + auto sv = v | etl::views::stride(2); + + std::vector expected{10, 30, 50}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_functional_call) + { + std::vector v = {1, 2, 3, 4, 5, 6}; + auto sv = etl::views::stride(v, 2); + + std::vector expected{1, 3, 5}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_etl_vector) + { + etl::vector v = {1, 2, 3, 4, 5, 6, 7, 8}; + auto sv = etl::ranges::stride_view(v, 3); + + std::vector expected{1, 4, 7}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_array) + { + std::array a = {10, 20, 30, 40, 50, 60}; + auto sv = etl::ranges::stride_view(a, 2); + + std::vector expected{10, 30, 50}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_not_evenly_divisible) + { + std::vector v = {1, 2, 3, 4, 5, 6, 7}; + auto sv = etl::ranges::stride_view(v, 3); + + std::vector expected{1, 4, 7}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_pipe_chain_take_stride) + { + // Take first 6, then stride by 2 + std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto sv = v | etl::views::take(6) | etl::views::stride(2); + + std::vector expected{1, 3, 5}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_pipe_chain_drop_stride) + { + // Drop first 2, then stride by 3 + std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto sv = v | etl::views::drop(2) | etl::views::stride(3); + + std::vector expected{3, 6, 9}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_pipe_chain_stride_transform) + { + // Stride by 2, then transform (multiply by 10) + std::vector v = {1, 2, 3, 4, 5}; + auto mul10 = [](int x) { return x * 10; }; + auto sv = v | etl::views::stride(2) | etl::views::transform(mul10); + + std::vector expected{10, 30, 50}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_stride_view_pipe_chain_filter_stride) + { + // Filter even, then stride by 2 + std::vector v = {1, 2, 3, 4, 5, 6, 7, 8}; + auto is_even = [](int x) { return x % 2 == 0; }; + auto sv = v | etl::views::filter(is_even) | etl::views::stride(2); + + std::vector expected{2, 6}; + std::vector actual; + for (auto val : sv) + { + actual.push_back(val); + } + CHECK_EQUAL(expected, actual); + } + + //************************************************************************* + TEST(test_ranges_cartesian_product_view_basic) + { + std::vector v1 = {1, 2}; + std::vector v2 = {"a", "b", "c"}; + + auto cp = etl::views::cartesian_product(v1, v2); + + auto it = cp.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL(std::string("a"), etl::get<1>(*it)); + ++it; + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL(std::string("b"), etl::get<1>(*it)); + ++it; + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL(std::string("c"), etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL(std::string("a"), etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL(std::string("b"), etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL(std::string("c"), etl::get<1>(*it)); + ++it; + CHECK(it == cp.end()); + } + + //************************************************************************* + TEST(test_ranges_cartesian_product_view_size) + { + std::vector v1 = {1, 2, 3}; + std::vector v2 = {10, 20}; + + auto cp = etl::views::cartesian_product(v1, v2); + + CHECK_EQUAL(6U, cp.size()); + } + + //************************************************************************* + TEST(test_ranges_cartesian_product_view_three_ranges) + { + std::vector v1 = {1, 2}; + std::vector v2 = {'a', 'b'}; + std::vector v3 = {0.5}; + + auto cp = etl::views::cartesian_product(v1, v2, v3); + + CHECK_EQUAL(4U, cp.size()); + + std::vector> expected = { + {1, 'a', 0.5}, + {1, 'b', 0.5}, + {2, 'a', 0.5}, + {2, 'b', 0.5} + }; + + size_t idx = 0; + for (auto val : cp) + { + CHECK_EQUAL(etl::get<0>(expected[idx]), etl::get<0>(val)); + CHECK_EQUAL(etl::get<1>(expected[idx]), etl::get<1>(val)); + CHECK_CLOSE(etl::get<2>(expected[idx]), etl::get<2>(val), 0.01); + ++idx; + } + CHECK_EQUAL(4U, idx); + } + + //************************************************************************* + TEST(test_ranges_cartesian_product_view_empty_range) + { + std::vector v1 = {1, 2, 3}; + std::vector v2; + + auto cp = etl::views::cartesian_product(v1, v2); + + CHECK_EQUAL(0U, cp.size()); + CHECK(cp.begin() == cp.end()); + } + + //************************************************************************* + TEST(test_ranges_cartesian_product_view_single_range) + { + std::vector v1 = {10, 20, 30}; + + auto cp = etl::views::cartesian_product(v1); + + CHECK_EQUAL(3U, cp.size()); + + auto it = cp.begin(); + CHECK_EQUAL(10, etl::get<0>(*it)); + ++it; + CHECK_EQUAL(20, etl::get<0>(*it)); + ++it; + CHECK_EQUAL(30, etl::get<0>(*it)); + ++it; + CHECK(it == cp.end()); + } + + //************************************************************************* + TEST(test_ranges_cartesian_product_view_range_for) + { + std::vector v1 = {1, 2}; + std::vector v2 = {10, 20}; + + auto cp = etl::views::cartesian_product(v1, v2); + + std::vector> result; + for (auto val : cp) + { + result.push_back(val); + } + + CHECK_EQUAL(4U, result.size()); + CHECK_EQUAL(1, etl::get<0>(result[0])); + CHECK_EQUAL(10, etl::get<1>(result[0])); + CHECK_EQUAL(1, etl::get<0>(result[1])); + CHECK_EQUAL(20, etl::get<1>(result[1])); + CHECK_EQUAL(2, etl::get<0>(result[2])); + CHECK_EQUAL(10, etl::get<1>(result[2])); + CHECK_EQUAL(2, etl::get<0>(result[3])); + CHECK_EQUAL(20, etl::get<1>(result[3])); + } + + //************************************************************************* + TEST(test_ranges_cartesian_product_view_with_array) + { + std::array a1 = {1, 2}; + std::array a2 = {'x', 'y', 'z'}; + + auto cp = etl::views::cartesian_product(a1, a2); + + CHECK_EQUAL(6U, cp.size()); + + auto it = cp.begin(); + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL('x', etl::get<1>(*it)); + ++it; + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL('y', etl::get<1>(*it)); + ++it; + CHECK_EQUAL(1, etl::get<0>(*it)); + CHECK_EQUAL('z', etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL('x', etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL('y', etl::get<1>(*it)); + ++it; + CHECK_EQUAL(2, etl::get<0>(*it)); + CHECK_EQUAL('z', etl::get<1>(*it)); + ++it; + CHECK(it == cp.end()); + } + + //************************************************************************* + TEST(test_ranges_cartesian_product_view_chained_with_take) + { + std::vector v1 = {1, 2, 3}; + std::vector v2 = {10, 20, 30}; + + auto cp = etl::views::cartesian_product(v1, v2) | etl::views::take(4); + + std::vector> result; + for (auto val : cp) + { + result.push_back(val); + } + + CHECK_EQUAL(4U, result.size()); + CHECK_EQUAL(1, etl::get<0>(result[0])); + CHECK_EQUAL(10, etl::get<1>(result[0])); + CHECK_EQUAL(1, etl::get<0>(result[1])); + CHECK_EQUAL(20, etl::get<1>(result[1])); + CHECK_EQUAL(1, etl::get<0>(result[2])); + CHECK_EQUAL(30, etl::get<1>(result[2])); + CHECK_EQUAL(2, etl::get<0>(result[3])); + CHECK_EQUAL(10, etl::get<1>(result[3])); + } + + //************************************************************************* + TEST(test_ranges_cartesian_product_view_single_elements) + { + std::vector v1 = {42}; + std::vector v2 = {'z'}; + + auto cp = etl::views::cartesian_product(v1, v2); + + CHECK_EQUAL(1U, cp.size()); + + auto it = cp.begin(); + CHECK_EQUAL(42, etl::get<0>(*it)); + CHECK_EQUAL('z', etl::get<1>(*it)); + ++it; + CHECK(it == cp.end()); + } + + //************************************************************************* + TEST(test_ranges_cartesian_product_view_first_range_empty) + { + std::vector v1; + std::vector v2 = {1, 2, 3}; + + auto cp = etl::views::cartesian_product(v1, v2); + + CHECK_EQUAL(0U, cp.size()); + CHECK(cp.begin() == cp.end()); + } + + //************************************************************************* + /// to_input_view tests + //************************************************************************* + TEST(test_ranges_to_input_view_functional) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto iv = etl::ranges::to_input_view(v_in); + + // Iterator category should be input_iterator_tag + using iv_iterator = decltype(iv.begin()); + using iv_category = typename etl::iterator_traits::iterator_category; + static_assert(etl::is_same_v, + "to_input_view iterator should have input_iterator_tag"); + + for (auto i : iv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(iv.size(), 10u); + CHECK_EQUAL(iv.empty(), false); + } + + //************************************************************************* + TEST(test_ranges_to_input_view_empty_range) + { + etl::vector v_in; + + auto iv = etl::ranges::to_input_view(v_in); + + CHECK_EQUAL(iv.size(), 0u); + CHECK_EQUAL(iv.empty(), true); + CHECK(iv.begin() == iv.end()); + } + + //************************************************************************* + TEST(test_ranges_to_input_view_reflects_base_changes) + { + etl::vector v_in{ 0, 1, 2, 3, 4 }; + + auto iv = etl::ranges::to_input_view(v_in); + + CHECK_EQUAL(iv.front(), 0); + CHECK_EQUAL(iv.size(), 5u); + + v_in[0] = 99; + CHECK_EQUAL(iv.front(), 99); + } + + //************************************************************************* + TEST(test_ranges_to_input_view_pipe) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto iv = v_in | etl::views::to_input(); + + using iv_iterator = decltype(iv.begin()); + using iv_category = typename etl::iterator_traits::iterator_category; + static_assert(etl::is_same_v, + "piped to_input view iterator should have input_iterator_tag"); + + for (auto i : iv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + CHECK_EQUAL(iv.size(), 10u); + } + + //************************************************************************* + TEST(test_ranges_views_to_input_functional) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto iv = etl::views::to_input(v_in); + + using iv_iterator = decltype(iv.begin()); + using iv_category = typename etl::iterator_traits::iterator_category; + static_assert(etl::is_same_v, + "views::to_input iterator should have input_iterator_tag"); + + for (auto i : iv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + //************************************************************************* + TEST(test_ranges_to_input_view_pipe_chained_with_take) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 1, 2 }; + + auto iv = v_in | etl::views::to_input() | etl::views::take(3); + + for (auto i : iv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + //************************************************************************* + TEST(test_ranges_to_input_view_pipe_chained_with_drop) + { + etl::vector v_in{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + etl::vector v_out; + etl::vector v_out_expected{ 7, 8, 9 }; + + auto iv = v_in | etl::views::to_input() | etl::views::drop(7); + + for (auto i : iv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + //************************************************************************* + TEST(test_ranges_to_input_view_pipe_chained_with_transform) + { + etl::vector v_in{ 0, 1, 2, 3, 4 }; + etl::vector v_out; + etl::vector v_out_expected{ 0, 2, 4, 6, 8 }; + + auto doubler = [](int i) { return i * 2; }; + auto iv = v_in | etl::views::to_input() | etl::views::transform(doubler); + + for (auto i : iv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected, v_out); + } + + //************************************************************************* + TEST(test_ranges_to_input_view_with_std_vector) + { + std::vector v_in{ 0, 1, 2, 3, 4 }; + std::vector v_out; + std::vector v_out_expected{ 0, 1, 2, 3, 4 }; + + auto iv = etl::ranges::to_input_view(v_in); + + using iv_iterator = decltype(iv.begin()); + using iv_category = typename etl::iterator_traits::iterator_category; + static_assert(etl::is_same_v, + "to_input_view over std::vector should have input_iterator_tag"); + + for (auto i : iv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(v_out_expected.size(), v_out.size()); + for (size_t idx = 0; idx < v_out_expected.size(); ++idx) + { + CHECK_EQUAL(v_out_expected[idx], v_out[idx]); + } + } + + //************************************************************************* + TEST(test_ranges_to_input_view_downgrades_from_random_access) + { + // std::vector has random_access_iterator; verify to_input downgrades it + std::vector v_in{ 10, 20, 30 }; + + using original_category = typename etl::iterator_traits::iterator>::iterator_category; + static_assert(etl::is_same_v, + "std::vector iterator should be random_access"); + + auto iv = etl::ranges::to_input_view(v_in); + + using iv_iterator = decltype(iv.begin()); + using iv_category = typename etl::iterator_traits::iterator_category; + static_assert(etl::is_same_v, + "to_input_view should downgrade to input_iterator_tag"); + + etl::vector v_out; + for (auto i : iv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(3u, v_out.size()); + CHECK_EQUAL(10, v_out[0]); + CHECK_EQUAL(20, v_out[1]); + CHECK_EQUAL(30, v_out[2]); + } + + //************************************************************************* + TEST(test_ranges_to_input_view_downgrades_from_bidirectional) + { + // std::list has bidirectional_iterator; verify to_input downgrades it + std::list l_in{ 10, 20, 30 }; + + using original_category = typename etl::iterator_traits::iterator>::iterator_category; + static_assert(etl::is_same_v, + "std::list iterator should be bidirectional"); + + auto iv = etl::ranges::to_input_view(l_in); + + using iv_iterator = decltype(iv.begin()); + using iv_category = typename etl::iterator_traits::iterator_category; + static_assert(etl::is_same_v, + "to_input_view should downgrade to input_iterator_tag"); + + etl::vector v_out; + for (auto i : iv) + { + v_out.push_back(i); + } + + CHECK_EQUAL(3u, v_out.size()); + CHECK_EQUAL(10, v_out[0]); + CHECK_EQUAL(20, v_out[1]); + CHECK_EQUAL(30, v_out[2]); + } + } +} + +#endif +#endif \ No newline at end of file diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 2b069673..6163a5db 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3642,6 +3642,7 @@ + @@ -3813,6 +3814,7 @@ + @@ -11347,6 +11349,7 @@ true true + diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index e78247d6..e773d752 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -2381,6 +2381,9 @@ Tests\Maths + + Tests\Misc + Tests\Errors