From a8ebe338f80ee25fcc79d53cdbc8140bf208fae1 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 6 Mar 2026 11:11:46 +0100 Subject: [PATCH 01/31] Fix etl::rotate (#1327) Per the C++ standard, std::rotate returns first + (last - middle): * When first == middle, return last * When middle == last, return first --- include/etl/algorithm.h | 28 +++++++++-- test/iterators_for_unit_tests.h | 6 +++ test/test_algorithm.cpp | 87 +++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 4 deletions(-) diff --git a/include/etl/algorithm.h b/include/etl/algorithm.h index 74fbb8ed..8ee795a8 100644 --- a/include/etl/algorithm.h +++ b/include/etl/algorithm.h @@ -1193,7 +1193,12 @@ namespace etl typename etl::enable_if::value, TIterator>::type rotate_general(TIterator first, TIterator middle, TIterator last) { - if (first == middle || middle == last) + if (first == middle) + { + return last; + } + + if (middle == last) { return first; } @@ -1242,7 +1247,12 @@ namespace etl typename etl::enable_if::value, TIterator>::type rotate_general(TIterator first, TIterator middle, TIterator last) { - if (first == middle || middle == last) + if (first == middle) + { + return last; + } + + if (middle == last) { return first; } @@ -1292,7 +1302,12 @@ namespace etl typename etl::enable_if::value, TIterator>::type rotate_general(TIterator first, TIterator middle, TIterator last) { - if (first == middle || middle == last) + if (first == middle) + { + return last; + } + + if (middle == last) { return first; } @@ -1314,7 +1329,12 @@ namespace etl typename etl::enable_if::value, TIterator>::type rotate_general(TIterator first, TIterator middle, TIterator last) { - if (first == middle || middle == last) + if (first == middle) + { + return last; + } + + if (middle == last) { return first; } diff --git a/test/iterators_for_unit_tests.h b/test/iterators_for_unit_tests.h index 16085f87..82d28c6c 100644 --- a/test/iterators_for_unit_tests.h +++ b/test/iterators_for_unit_tests.h @@ -113,6 +113,12 @@ struct non_random_iterator : public etl::iterator +bool operator ==(const non_random_iterator& lhs, const non_random_iterator& rhs) +{ + return lhs.ptr == rhs.ptr; +} + template bool operator !=(const non_random_iterator& lhs, const non_random_iterator& rhs) { diff --git a/test/test_algorithm.cpp b/test/test_algorithm.cpp index abdab9d5..4dd01e9f 100644 --- a/test/test_algorithm.cpp +++ b/test/test_algorithm.cpp @@ -1236,6 +1236,93 @@ namespace } } + //************************************************************************* + TEST(rotate_return_value) + { + // Verify that etl::rotate returns the same iterator as std::rotate + // in all cases, including the degenerate first==middle and middle==last cases. + std::vector initial_data = { 1, 2, 3, 4, 5 }; + + for (size_t i = 0UL; i <= initial_data.size(); ++i) + { + std::vector data1(initial_data); + std::vector data2(initial_data); + + auto std_result = std::rotate(data1.data(), data1.data() + i, data1.data() + data1.size()); + auto etl_result = etl::rotate(data2.data(), data2.data() + i, data2.data() + data2.size()); + + // Check that the return value offset matches + ptrdiff_t std_offset = std_result - data1.data(); + ptrdiff_t etl_offset = etl_result - data2.data(); + CHECK_EQUAL(std_offset, etl_offset); + } + + // Explicitly test first == middle (empty left half): should return last + { + std::vector data = { 1, 2, 3 }; + auto result = etl::rotate(data.data(), data.data(), data.data() + data.size()); + CHECK(result == data.data() + data.size()); + } + + // Explicitly test middle == last (empty right half): should return first + { + std::vector data = { 1, 2, 3 }; + auto result = etl::rotate(data.data(), data.data() + data.size(), data.data() + data.size()); + CHECK(result == data.data()); + } + } + + //************************************************************************* + TEST(rotate_return_value_non_random_iterator) + { + // Verify that etl::rotate returns the correct iterator when called with + // non-random (bidirectional) iterators, exercising rotate_general for + // bidirectional iterators rather than the random-access overload. + std::vector initial_data = { 1, 2, 3, 4, 5 }; + + for (size_t i = 0UL; i <= initial_data.size(); ++i) + { + std::vector data1(initial_data); + std::vector data2(initial_data); + + auto std_result = std::rotate(data1.data(), data1.data() + i, data1.data() + data1.size()); + + non_random_iterator nr_first(data2.data()); + non_random_iterator nr_middle(data2.data() + i); + non_random_iterator nr_last(data2.data() + data2.size()); + auto etl_result = etl::rotate(nr_first, nr_middle, nr_last); + + // Check that the data was rotated correctly + bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2)); + CHECK(isEqual); + + // Check that the return value offset matches + ptrdiff_t std_offset = std_result - data1.data(); + ptrdiff_t etl_offset = etl_result.ptr - data2.data(); + CHECK_EQUAL(std_offset, etl_offset); + } + + // Explicitly test first == middle (empty left half): should return last + { + std::vector data = { 1, 2, 3 }; + non_random_iterator nr_first(data.data()); + non_random_iterator nr_middle(data.data()); + non_random_iterator nr_last(data.data() + data.size()); + auto result = etl::rotate(nr_first, nr_middle, nr_last); + CHECK(result.ptr == data.data() + data.size()); + } + + // Explicitly test middle == last (empty right half): should return first + { + std::vector data = { 1, 2, 3 }; + non_random_iterator nr_first(data.data()); + non_random_iterator nr_middle(data.data() + data.size()); + non_random_iterator nr_last(data.data() + data.size()); + auto result = etl::rotate(nr_first, nr_middle, nr_last); + CHECK(result.ptr == data.data()); + } + } + //************************************************************************* TEST(any_of) { From f96510bd79dca27c7a9445b56d3625d85e219843 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 2 Mar 2026 09:59:08 +0000 Subject: [PATCH 02/31] Added missing files from VS2022 project --- test/vs2022/etl.vcxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 6396b761..2b069673 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3552,6 +3552,7 @@ + @@ -10362,6 +10363,7 @@ + From 4ad6126ec5fdd7c27ed359ee09de2482e922bebb Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 6 Mar 2026 21:08:31 +0100 Subject: [PATCH 03/31] Fix greater_equal and less_equal (#1331) --- include/etl/functional.h | 10 +++--- test/test_functional.cpp | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/include/etl/functional.h b/include/etl/functional.h index 0f0172ec..73b1685b 100644 --- a/include/etl/functional.h +++ b/include/etl/functional.h @@ -224,9 +224,9 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(!(static_cast(rhs) < static_cast(lhs))) { - return !(static_cast(lhs) < static_cast(rhs)); + return !(static_cast(rhs) < static_cast(lhs)); } }; #endif @@ -250,7 +250,7 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(rhs) < static_cast(lhs)) { return static_cast(rhs) < static_cast(lhs); } @@ -276,9 +276,9 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(!(static_cast(lhs) < static_cast(rhs))) { - return static_cast(rhs) < static_cast(lhs); + return !(static_cast(lhs) < static_cast(rhs)); } }; #endif diff --git a/test/test_functional.cpp b/test/test_functional.cpp index 1204d330..5bbe6beb 100644 --- a/test/test_functional.cpp +++ b/test/test_functional.cpp @@ -85,6 +85,24 @@ namespace mutable std::string result; }; +#if ETL_USING_CPP11 + // Lightweight type used to verify transparent heterogeneous comparison. + // Only operator<(int, Wrapper) is defined; operator<(Wrapper, int) is + // intentionally absent. less_equal is implemented as + // !(rhs < lhs), so less_equal{}(Wrapper, int) needs + // operator<(int, Wrapper) which IS provided. + struct Wrapper + { + int value; + constexpr explicit Wrapper(int v) : value(v) {} + }; + + // int < Wrapper -- defined + constexpr bool operator<(int lhs, const Wrapper& rhs) { return lhs < rhs.value; } + + // Wrapper < int -- intentionally NOT defined +#endif + SUITE(test_functional) { //************************************************************************* @@ -101,6 +119,32 @@ namespace CHECK((compare>(1, 2))); CHECK(!(compare>(2, 1))); CHECK((compare>(1, 1))); + +#if ETL_USING_CPP11 + CHECK((compare>(1, 2))); + CHECK(!(compare>(2, 1))); + CHECK((compare>(1, 1))); +#endif + } + + //************************************************************************* + TEST(test_less_equal_void_heterogeneous) + { +#if ETL_USING_CPP11 + // less_equal{}(lhs, rhs) is !(rhs < lhs). + // With only operator<(int, Wrapper) defined, we can call + // less_equal{}(Wrapper, int) because the implementation + // evaluates !(int < Wrapper). + + // Wrapper(1) <= 2 → !(2 < Wrapper(1)) → !(2 < 1) → !false → true + CHECK((etl::less_equal{}(Wrapper(1), 2))); + + // Wrapper(2) <= 1 → !(1 < Wrapper(2)) → !(1 < 2) → !true → false + CHECK(!(etl::less_equal{}(Wrapper(2), 1))); + + // Wrapper(3) <= 3 → !(3 < Wrapper(3)) → !(3 < 3) → !false → true + CHECK((etl::less_equal{}(Wrapper(3), 3))); +#endif } //************************************************************************* @@ -117,6 +161,32 @@ namespace CHECK(!(compare>(1, 2))); CHECK((compare>(2, 1))); CHECK((compare>(1, 1))); + +#if ETL_USING_CPP11 + CHECK(!(compare>(1, 2))); + CHECK((compare>(2, 1))); + CHECK((compare>(1, 1))); +#endif + } + + //************************************************************************* + TEST(test_greater_equal_void_heterogeneous) + { +#if ETL_USING_CPP11 + // greater_equal{}(lhs, rhs) is !(lhs < rhs). + // With only operator<(int, Wrapper) defined, we can call + // greater_equal{}(int, Wrapper) because the implementation + // evaluates !(int < Wrapper). + + // 2 >= Wrapper(1) → !(2 < Wrapper(1)) → !(2 < 1) → !false → true + CHECK((etl::greater_equal{}(2, Wrapper(1)))); + + // 1 >= Wrapper(2) → !(1 < Wrapper(2)) → !(1 < 2) → !true → false + CHECK(!(etl::greater_equal{}(1, Wrapper(2)))); + + // 3 >= Wrapper(3) → !(3 < Wrapper(3)) → !(3 < 3) → !false → true + CHECK((etl::greater_equal{}(3, Wrapper(3)))); +#endif } //************************************************************************* From 7329efc7dfde1116de974899558be5345ec0e5c2 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 6 Mar 2026 21:29:58 +0100 Subject: [PATCH 04/31] Align comparison operators (#1330) In functional.h, the comparison operators for equal_to and not_equal_to mismatch between the actual comparison execution and the type inference for the return type. This change adjusts it by using the same operator==() in the return type inference as used in the comparison execution. Co-authored-by: John Wellbelove --- include/etl/functional.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/etl/functional.h b/include/etl/functional.h index 73b1685b..3c1c55b4 100644 --- a/include/etl/functional.h +++ b/include/etl/functional.h @@ -303,7 +303,7 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) == static_cast(rhs)) { return static_cast(lhs) == static_cast(rhs); } @@ -329,7 +329,7 @@ namespace etl typedef int is_transparent; template - constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(static_cast(lhs) < static_cast(rhs)) + constexpr auto operator()(T1&& lhs, T2&& rhs) const -> decltype(!(static_cast(lhs) == static_cast(rhs))) { return !(static_cast(lhs) == static_cast(rhs)); } From 67ecc8e11e2be245c15f03b231108b1adbd5882e Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 6 Mar 2026 22:34:54 +0100 Subject: [PATCH 05/31] Add missing tests (#1321) * Add missing tests * Typo fixes --------- Co-authored-by: John Wellbelove --- include/etl/algorithm.h | 2 +- include/etl/array.h | 6 +++--- include/etl/functional.h | 2 +- include/etl/numeric.h | 2 +- test/CMakeLists.txt | 9 +++++++++ test/test_etl_assert.cpp | 2 ++ 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/include/etl/algorithm.h b/include/etl/algorithm.h index 8ee795a8..7317889c 100644 --- a/include/etl/algorithm.h +++ b/include/etl/algorithm.h @@ -34,7 +34,7 @@ SOFTWARE. #define ETL_ALGORITHM_INCLUDED ///\defgroup algorithm algorithm -/// Including reverse engineered algorithms from C++ 0x11, 0x14, 0x17 +/// Including reverse engineered algorithms from C++11, 14, 17 /// Additional new variants of certain algorithms. ///\ingroup utilities diff --git a/include/etl/array.h b/include/etl/array.h index f1624209..1f2f5044 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -46,7 +46,7 @@ SOFTWARE. #include ///\defgroup array array -/// A replacement for std::array if you haven't got C++0x11. +/// A replacement for std::array if you haven't got C++11. ///\ingroup containers namespace etl @@ -81,7 +81,7 @@ namespace etl //*************************************************************************** ///\ingroup array - /// A replacement for std::array if you haven't got C++0x11. + /// A replacement for std::array if you haven't got C++11. //*************************************************************************** template class array @@ -646,7 +646,7 @@ namespace etl //*************************************************************************** ///\ingroup array - /// A replacement for std::array if you haven't got C++0x11. + /// A replacement for std::array if you haven't got C++11. /// Specialisation for zero sized array. //*************************************************************************** template diff --git a/include/etl/functional.h b/include/etl/functional.h index 3c1c55b4..e313325e 100644 --- a/include/etl/functional.h +++ b/include/etl/functional.h @@ -43,7 +43,7 @@ SOFTWARE. namespace etl { //*************************************************************************** - /// A definition of reference_wrapper for those that don't have C++ 0x11 support. + /// A definition of reference_wrapper for those that don't have C++11 support. ///\ingroup reference //*************************************************************************** template diff --git a/include/etl/numeric.h b/include/etl/numeric.h index dbb43696..3283b338 100644 --- a/include/etl/numeric.h +++ b/include/etl/numeric.h @@ -47,7 +47,7 @@ namespace etl { //*************************************************************************** /// iota - /// Reverse engineered version of std::iota for non C++ 0x11 compilers. + /// Reverse engineered version of std::iota for non C++11 compilers. /// Fills a range of elements with sequentially increasing values starting with value. ///\param first An iterator to the first position to fill. ///\param last An iterator to the last + 1 position. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2fdfc71c..2bec7847 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -86,6 +86,7 @@ add_executable(etl_tests test_closure.cpp test_closure_constexpr.cpp test_compare.cpp + test_concepts.cpp test_constant.cpp test_const_map.cpp test_const_map_constexpr.cpp @@ -172,6 +173,7 @@ add_executable(etl_tests test_endian.cpp test_enum_type.cpp test_error_handler.cpp + test_etl_assert.cpp test_etl_traits.cpp test_exception.cpp test_expected.cpp @@ -183,6 +185,7 @@ add_executable(etl_tests test_flat_multiset.cpp test_flat_set.cpp test_fnv_1.cpp + test_format.cpp test_format_spec.cpp test_forward_list.cpp test_forward_list_shared_pool.cpp @@ -194,10 +197,12 @@ add_executable(etl_tests test_hash.cpp test_hfsm.cpp test_hfsm_recurse_to_inner_state_on_start.cpp + test_hfsm_transition_on_enter.cpp test_histogram.cpp test_index_of_type.cpp test_indirect_vector.cpp test_indirect_vector_external_buffer.cpp + test_inplace_function.cpp test_instance_count.cpp test_integral_limits.cpp test_intrusive_forward_list.cpp @@ -206,7 +211,9 @@ add_executable(etl_tests test_intrusive_queue.cpp test_intrusive_stack.cpp test_invert.cpp + test_invoke.cpp test_io_port.cpp + test_is_invocable.cpp test_iterator.cpp test_jenkins.cpp test_largest.cpp @@ -259,6 +266,7 @@ add_executable(etl_tests test_pool.cpp test_pool_external_buffer.cpp test_priority_queue.cpp + test_print.cpp test_pseudo_moving_average.cpp test_quantize.cpp test_queue.cpp @@ -286,6 +294,7 @@ add_executable(etl_tests test_scaled_rounding.cpp test_set.cpp test_shared_message.cpp + test_signal.cpp test_singleton.cpp test_singleton_base.cpp test_smallest.cpp diff --git a/test/test_etl_assert.cpp b/test/test_etl_assert.cpp index e927a0b2..26fcced4 100644 --- a/test/test_etl_assert.cpp +++ b/test/test_etl_assert.cpp @@ -34,6 +34,8 @@ SOFTWARE. #include "etl/error_handler.h" #include "etl/exception.h" +#include + namespace { class TestException1 : public etl::exception From 8ce59792fd4667c33f61e749f3397a565514fc4d Mon Sep 17 00:00:00 2001 From: Niu Zhihong Date: Sat, 7 Mar 2026 06:04:32 +0800 Subject: [PATCH 06/31] Add ETL_FORMAT_NO_FLOATING_POINT control macro for etl::format (#1329) When ETL_FORMAT_NO_FLOATING_POINT is defined, all floating-point formatting support (float, double, long double) is excluded from etl::format. This reduces code size on targets that do not require floating-point formatting. Guarded sections: - #include - float/double/long double in supported_format_types variant - float/double/long double constructors in basic_format_arg - format_floating_* functions and format_aligned_floating - formatter, formatter, formatter - Floating-point test cases in test_format.cpp Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus Co-authored-by: John Wellbelove --- include/etl/format.h | 14 ++++++++++++++ include/etl/platform.h | 11 +++++++++++ test/test_format.cpp | 2 ++ 3 files changed, 27 insertions(+) diff --git a/include/etl/format.h b/include/etl/format.h index 3268f43b..7812f9ab 100644 --- a/include/etl/format.h +++ b/include/etl/format.h @@ -48,7 +48,9 @@ SOFTWARE. #include "variant.h" #include "visitor.h" +#if ETL_USING_FORMAT_FLOATING_POINT #include +#endif #if ETL_USING_CPP11 @@ -138,9 +140,11 @@ namespace etl unsigned int, long long int, unsigned long long int, +#if ETL_USING_FORMAT_FLOATING_POINT float, double, long double, +#endif const char*, etl::string_view, const void* @@ -302,6 +306,7 @@ namespace etl { } +#if ETL_USING_FORMAT_FLOATING_POINT basic_format_arg(const float v) : data(v) { @@ -316,6 +321,7 @@ namespace etl : data(v) { } +#endif basic_format_arg(const etl::string_view v) : data(v) @@ -1039,6 +1045,7 @@ namespace etl format_plain_num(it, value, spec, width); } +#if ETL_USING_FORMAT_FLOATING_POINT template void format_floating_default(OutputIt& it, T value, const format_spec_t& spec) { @@ -1214,6 +1221,7 @@ namespace etl private_format::format_sequence(it, "."); private_format::format_plain_num(it, fractional_int, spec, fractional_decimals); } +#endif class dummy_assign_to { @@ -1336,6 +1344,7 @@ namespace etl size_t count; }; +#if ETL_USING_FORMAT_FLOATING_POINT template void format_floating_g(OutputIt& it, T value, const format_spec_t& spec) { @@ -1409,6 +1418,7 @@ namespace etl } } } +#endif template struct format_visitor @@ -1490,6 +1500,7 @@ namespace etl return it; } +#if ETL_USING_FORMAT_FLOATING_POINT template typename format_context::iterator format_aligned_floating(Float arg, format_context& fmt_ctx) { @@ -1534,6 +1545,7 @@ namespace etl private_format::fill(it, suffix_size, fmt_ctx.format_spec.fill); return it; } +#endif template void format_string_view(OutputIt& it, etl::string_view arg, const format_spec_t& spec) @@ -2072,6 +2084,7 @@ namespace etl } }; +#if ETL_USING_FORMAT_FLOATING_POINT template<> struct formatter { @@ -2119,6 +2132,7 @@ namespace etl return private_format::format_aligned_floating(arg, fmt_ctx); } }; +#endif template<> struct formatter diff --git a/include/etl/platform.h b/include/etl/platform.h index 6db7c305..a1ae4029 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -154,6 +154,16 @@ SOFTWARE. #define ETL_NOT_USING_WIDE_CHARACTERS 0 #endif +//************************************* +// Helper macro for ETL_FORMAT_NO_FLOATING_POINT. +#if defined(ETL_FORMAT_NO_FLOATING_POINT) + #define ETL_USING_FORMAT_FLOATING_POINT 0 + #define ETL_NOT_USING_FORMAT_FLOATING_POINT 1 +#else + #define ETL_USING_FORMAT_FLOATING_POINT 1 + #define ETL_NOT_USING_FORMAT_FLOATING_POINT 0 +#endif + //************************************* // Figure out things about the compiler, if haven't already done so in etl_profile.h #include "profiles/determine_compiler_version.h" @@ -651,6 +661,7 @@ namespace etl static ETL_CONSTANT bool using_exceptions = (ETL_USING_EXCEPTIONS == 1); static ETL_CONSTANT bool using_libc_wchar_h = (ETL_USING_LIBC_WCHAR_H == 1); static ETL_CONSTANT bool using_std_exception = (ETL_USING_STD_EXCEPTION == 1); + static ETL_CONSTANT bool using_format_floating_point = (ETL_USING_FORMAT_FLOATING_POINT == 1); // Has... static ETL_CONSTANT bool has_initializer_list = (ETL_HAS_INITIALIZER_LIST == 1); diff --git a/test/test_format.cpp b/test/test_format.cpp index a6c25eab..3d3338fa 100644 --- a/test/test_format.cpp +++ b/test/test_format.cpp @@ -197,6 +197,7 @@ namespace CHECK_EQUAL("-6759414", test_format(s, "{}", static_cast(-6759414))); } +#if ETL_USING_FORMAT_FLOATING_POINT //************************************************************************* TEST(test_format_float) { @@ -266,6 +267,7 @@ namespace CHECK_EQUAL("0x1.92a738p-5", test_format(s, "{:a}", 0.0000015f)); CHECK_EQUAL("0x1.6345785d8ap+e", test_format(s, "{:a}", 100000000000000000.l)); } +#endif //************************************************************************* TEST(test_format_char_array) From 521df8ee19bf0b84b82086c2441c1c2f0c463380 Mon Sep 17 00:00:00 2001 From: Timon Zijnge <47081647+tzijnge@users.noreply.github.com> Date: Sat, 7 Mar 2026 09:13:00 +0100 Subject: [PATCH 07/31] Manchester documentation (#1325) * manchester * Added manchester code and test * manchester * Formatting and added missing file * manchester * Some functions can only be constexpr since C++14 * manchester * Manchester decode and some refactoring * manchester * Added some missing typenames * manchester * constexpr void function not allowed in C++11 * manchester * condition on static_assert tests * manchester * revert CMakeLists.txt * Using ETL_STATIC_ASSERT * Some cleanup * manchester * Added static_assert message * manchester * Added compile time tests * manchester * Added invert manchester * Some refactoring * manchester * Disable test for now * Move ETL_NODISCARD before static * manchester * Test for valid_span * manchester * Remove redundant (?) storage specifiers for template specializations. Storage specifier already given in base template * manchester * refactoring to get rid of specialized template functions in template class * manchester * cleanup * manchester * Added documentation comments * Some refactoring * manchester * introducing namespace detail_manchester * manchester * Some refactoring * Update tests * manchester * Some refactoring * Removed possible undefined behavior by refactoring encode_span * constexpr version of encode_span * Static assertion for rare case where code doesn't work because CHAR_BIT is not the same as the number of bits in uint_least8_t * manchester * renamed valid to is_valid * manchester * renamed is_valid_span to is_valid * Using etl exceptions in ETL_ASSERT * manchester * Removed _fast functions * merged encode_in_place with encode and decode_in_place with decode * removed _span to create normal overloads of encode and decode for span * Some renaming and minor refactoring * manchester * Fix build issues * manchester * Conditionally compile manchester_decoded * Update test_manchester.cpp Removed redundant semicolon * #1258 Manchester coding * Formatting * consistency: hex literals with lower case 0x * #1258 Manchester coding * Moved copyright to top of file * Make constexpr encode/decode span functions equal for little and big endian platforms * #1258 Manchester coding * Added missing include * Added missing 8bit/64bit guards * Fixed is_valid for big endian platforms * #1258 Manchester coding * private memcpy alias * #1258 Manchester coding * Review comments * #1258 Manchester coding * Cleanup * Fix build error * #1258 Manchester coding * Add manchester documentation * #1258 Manchester coding * Preparation for GitHub pages * #1324 Manchester documentation * Some small fixes --------- Co-authored-by: Timon Zijnge --- docs/_config.yml | 6 ++ docs/index.md | 7 ++ docs/manchester.md | 258 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 271 insertions(+) create mode 100644 docs/_config.yml create mode 100644 docs/index.md create mode 100644 docs/manchester.md diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 00000000..6fac68f0 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,6 @@ +plugins: + - jekyll-relative-links +relative_links: + enabled: true +include: + - manchester.md diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..23e43ac5 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,7 @@ +--- +title: ETL documentation +--- + +## Pages + +* [Manchester](manchester.md) diff --git a/docs/manchester.md b/docs/manchester.md new file mode 100644 index 00000000..06c0cf7d --- /dev/null +++ b/docs/manchester.md @@ -0,0 +1,258 @@ +--- +title: Manchester encoding and decoding +--- + +Efficient Manchester encoding and decoding of data. The Manchester code represents a data bit as a sequence of a 'high' and a 'low' value. In software this translates to a conversion from one to two bits, or in a practical situation, from `n` bytes to `n*2` bytes. + +## See also + +[Manchester code](https://en.wikipedia.org/wiki/Manchester_code) + +## Features + +- Normal and inverted Manchester encoding +- Support for multiple encoding chunk sizes: 8-bit, 16-bit and 32-bit +- Span-based operations or chunk-based operations +- Constexpr functions for compile-time encoding/decoding (8-bit chunk size only) +- Validation of encoded data + +## Algorithm background + +To encode the value `0b11001100` we must first duplicate all bits to create the value `0b1111000011110000`. We then perform an XOR of this value with the constant `0b1010101010101010` (`0xAAAA`) to obtain the Manchester coded value of `0b1010010110100101`. We have now replaced each `1` bit with the sequence `10` and each `0` bit with the sequence `01`. + +### 2. Bit duplication + +Bit duplication is achieved with the following steps. This is also called binary interleaving. The example shows encoding of an 8-bit value. + +| Step | High Byte | Low Byte | Operation | +|------|--------------------|--------------------|----------------------------| +| 0 | `_ _ _ _ _ _ _ _` | `A B C D E F G H` | input value (i) | +| 1 | `_ _ _ _ A B C D` | `_ _ _ _ E F G H` | `(i \| (i << 4)) & 0x0F0F` | +| 2 | `_ _ A B _ _ C D` | `_ _ E F _ _ G H` | `(i \| (i << 2)) & 0x3333` | +| 3 | `_ A _ B _ C _ D` | `_ E _ F _ G _ H` | `(i \| (i << 1)) & 0x5555` | +| 4 | `A A B B C C D D` | `E E F F G G H H` | `(i \| (i << 1))` | + +This process can be easily extended to 16-bit or 32-bit values by adding additional steps to the bit duplication. + +### 3. Manchester Decoding + +Manchester decoding is done in a similar, but reversed way. + +### 4. Error Detection + +Error detection in Manchester coded data is done by comparing 2 neighboring bits. If they are +equal, then there is an error in the encoded input data. + +Comparing all 8 bit pairs in a 16-bit word is done as follows. + +| Step | Binary Value | Operation | Description | +|------|--------------|-------------------|-----------------------------------------------------------------------------------------------| +| 1 | `11011000` | Original | First bit pair (lsb, 00) is invalid. Last bit pair is also invalid. Other bit pairs are valid | +| 2 | `01101100` | Shift right by 1 | Shift the original value right by one bit | +| 3 | `10110100` | XOR | XOR the original with the shifted value | +| 4 | `01010101` | Mask with 0x55 | Apply mask to isolate bit pairs | +| 5 | `00010100` | Result | If result is not equal to 0x55, there was an error in the input | + +## Analysis + +Most traditional ways to Manchester encode data consist of a loop over all bits and a nested if-statement to check the value of the current bit. This approach does not scale well to increasing number of bits. The algorithm implemented here contains no conditional code and scales well. Doubling the number of processed bits per step (the chunk size) adds a single row to the bit duplication table. Because of the lack of loops and conditional code, this algorithm is likely to perform better than traditional ones on simple processors or when compiler optimization is disabled. On modern, powerful processors with caches and advanced optimization possibilities this algorithm may not show much benefit. In any case, the performance of the algorithm depends heavily on the processor type, compiler and compiler (optimization) settings. + +## API Reference + +### Classes + +Classes `etl::manchester` and `etl::manchester_inverted` contain static functions for encoding, decoding and validity checking. It is not necessary to instantiate objects of these classes. + +#### etl::manchester + +```cpp +typedef manchester_base manchester; +``` + +Manchester encoder using normal encoding (no inversion). + +#### etl::manchester_inverted + +```cpp +typedef manchester_base manchester_inverted; +``` + +Manchester encoder using inverted encoding. + +### Encoding Functions + +#### Encode single value + +```cpp +template +static ETL_CONSTEXPR14 typename encoded::type encode(TDecoded decoded) +``` + +Encodes a single value using Manchester encoding. + +**Parameters:** + +- `decoded`: The value to encode (`uint8_t`, `uint16_t`, or `uint32_t`) + +**Returns:** + +- The Manchester encoded value (twice the bit width of input) + +**Example:** + +```cpp +uint16_t encoded = etl::manchester::encode(0x55); +``` + +#### Encode range + +```cpp +template +static ETL_CONSTEXPR14 void encode(etl::span decoded, + etl::span encoded) +``` + +Encodes a span of data using the specified chunk size. + +**Parameters:** + +- `decoded`: Source data to encode +- `encoded`: Destination for encoded data (must be twice the size of `decoded`) + +**Template Parameters:** + +- `TChunk`: Chunk size for encoding (`uint8_t`, `uint16_t` or `uint32_t`) + +**Example:** + +```cpp +std::array data = {0x12, 0x34, 0x56, 0x78}; +std::array encoded_data1{}; +std::array encoded_data2{}; + +// Encode with TChunk == uint8_t +etl::manchester::encode(data, encoded_data1); + +// Encode with TChunk == uint32_t +etl::manchester::encode(data, encoded_data2); +``` + +### Decoding Functions + +#### Decode single value + +```cpp +template +static ETL_CONSTEXPR14 typename decoded::type decode(TEncoded encoded) +``` + +Decodes a single Manchester encoded value. + +**Parameters:** + +- `encoded`: The encoded value to decode (`uint16_t`, `uint32_t`, or `uint64_t`) + +**Returns:** + +- The Manchester decoded value (half the bit width of input) + +**Example:** + +```cpp +uint8_t decoded = etl::manchester::decode(0x5A5A); +``` + +#### Decode range + +```cpp +template ::type> +static ETL_CONSTEXPR14 void decode(etl::span encoded, + etl::span decoded) +``` + +Decodes a span of Manchester encoded data. + +**Parameters:** + +- `encoded`: Source data to decode +- `decoded`: Destination for decoded data (must be half the size of `encoded`) + +**Template Parameters:** + +- `TChunk`: Chunk type for decoding (`uint16_t`, `uint32_t`, or `uint64_t`) + +**Example:** + +```cpp +std::array encoded = {/* ... */}; +std::array decoded1 {}; +std::array decoded2 {}; + +// Decode with TChunk == uint16_t +etl::manchester::decode(encoded, decoded1); + +// Decode with TChunk == uint64_t +etl::manchester::decode(encoded, decoded2); +``` + +### Validation Functions + +#### Single value + +```cpp +template +static ETL_CONSTEXPR14 bool is_valid(TChunk encoded) +``` + +Validates that a single value contains valid Manchester encoded data. + +**Parameters:** + +- `encoded`: The encoded value to validate + +**Returns:** + +- `true` if the value contains valid Manchester encoded data, `false` otherwise + +**Example:** + +```cpp +bool valid = etl::manchester::is_valid(0x5A5A); +``` + +#### Range + +```cpp +static ETL_CONSTEXPR14 bool is_valid(etl::span encoded) +``` + +Validates that a range contains valid Manchester encoded data. + +**Parameters:** + +- `encoded`: The range of encoded data to validate + +**Returns:** + +- `true` if all data is valid Manchester encoding, `false` otherwise + +**Example:** + +```cpp +std::array encoded_data = {/* ... */}; +bool valid = etl::manchester::is_valid(encoded_data); +``` + +## Supported Types + +### Input/chunk types for encoding + +- `uint8_t` → `uint16_t` (if 8-bit types are supported) +- `uint16_t` → `uint32_t` +- `uint32_t` → `uint64_t` (if 64-bit types are supported) + +### Input/chunk types for decoding + +- `uint16_t` → `uint8_t` (if 8-bit types are supported) +- `uint32_t` → `uint16_t` +- `uint64_t` → `uint32_t` (if 64-bit types are supported) From 4a8c167a31ad612693f0ef79f4ee925bc527bca5 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Tue, 10 Mar 2026 21:39:13 +0100 Subject: [PATCH 08/31] Changes from review of algorithm.h on development branch (#1340) * Add missing constexpr in algorithm.h * Fix call of nth_element 2nd argument (nth) was missing * Replace partition point with O(log(N)) algorithm The C++ standard defines O(log(N)) calls of predicate as the complexity of partition_point(). The old algorithm was linear. * Use predicate in calculation of is_permutation consistently In case of predicate not equal_to, the calculation previously returned wron results * Omit swap in selection_sort if iterators are equal * Use difference_type in rotate_general() instead of int * Typo fix in algorithm.h * Simplifications in algorithm.h Application of plain refactoring by keeping semantics * Guard against past-end iterator in etl::rotate() And fix scope of rotate_right_by_one for etl::rotate() * Support empty ranges in selection_sort * Add tests for swap_ranges * Add tests for binary_search * Add tests for find_end * Add tests for accumulate * Add tests for move_s * Added tests for is_heap and sort_heap * Remove early exit for empty input * Add adjacent_find * Add unique * Add unique_copy * Add merge * Add inplace_merge * Add partial_sort * Add partial_sort_copy --- include/etl/algorithm.h | 682 +++++++++--- test/test_algorithm.cpp | 2222 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 2735 insertions(+), 169 deletions(-) diff --git a/include/etl/algorithm.h b/include/etl/algorithm.h index 7317889c..cb38f8c3 100644 --- a/include/etl/algorithm.h +++ b/include/etl/algorithm.h @@ -507,6 +507,7 @@ namespace etl //*************************************************************************** template ETL_NODISCARD + ETL_CONSTEXPR14 bool binary_search(TIterator first, TIterator last, const T& value, Compare compare) { first = etl::lower_bound(first, last, value, compare); @@ -516,6 +517,7 @@ namespace etl template ETL_NODISCARD + ETL_CONSTEXPR14 bool binary_search(TIterator first, TIterator last, const T& value) { typedef etl::less::value_type> compare; @@ -949,7 +951,7 @@ namespace etl { // Push Heap Helper template - void push_heap(TIterator first, TDistance value_index, TDistance top_index, TValue value, TCompare compare) + ETL_CONSTEXPR14 void push_heap(TIterator first, TDistance value_index, TDistance top_index, TValue value, TCompare compare) { TDistance parent = (value_index - 1) / 2; @@ -965,7 +967,7 @@ namespace etl // Adjust Heap Helper template - void adjust_heap(TIterator first, TDistance value_index, TDistance length, TValue value, TCompare compare) + ETL_CONSTEXPR14 void adjust_heap(TIterator first, TDistance value_index, TDistance length, TValue value, TCompare compare) { TDistance top_index = value_index; TDistance child2nd = (2 * value_index) + 2; @@ -993,7 +995,7 @@ namespace etl // Is Heap Helper template - bool is_heap(const TIterator first, const TDistance n, TCompare compare) + ETL_CONSTEXPR14 bool is_heap(const TIterator first, const TDistance n, TCompare compare) { TDistance parent = 0; @@ -1016,6 +1018,7 @@ namespace etl // Pop Heap template + ETL_CONSTEXPR14 void pop_heap(TIterator first, TIterator last, TCompare compare) { typedef typename etl::iterator_traits::value_type value_t; @@ -1029,6 +1032,7 @@ namespace etl // Pop Heap template + ETL_CONSTEXPR14 void pop_heap(TIterator first, TIterator last) { typedef etl::less::value_type> compare; @@ -1038,6 +1042,7 @@ namespace etl // Push Heap template + ETL_CONSTEXPR14 void push_heap(TIterator first, TIterator last, TCompare compare) { typedef typename etl::iterator_traits::difference_type difference_t; @@ -1048,6 +1053,7 @@ namespace etl // Push Heap template + ETL_CONSTEXPR14 void push_heap(TIterator first, TIterator last) { typedef etl::less::value_type> compare; @@ -1057,6 +1063,7 @@ namespace etl // Make Heap template + ETL_CONSTEXPR14 void make_heap(TIterator first, TIterator last, TCompare compare) { typedef typename etl::iterator_traits::difference_type difference_t; @@ -1084,6 +1091,7 @@ namespace etl // Make Heap template + ETL_CONSTEXPR14 void make_heap(TIterator first, TIterator last) { typedef etl::less::value_type> compare; @@ -1094,6 +1102,7 @@ namespace etl // Is Heap template ETL_NODISCARD + ETL_CONSTEXPR14 bool is_heap(TIterator first, TIterator last) { typedef etl::less::value_type> compare; @@ -1104,6 +1113,7 @@ namespace etl // Is Heap template ETL_NODISCARD + ETL_CONSTEXPR14 bool is_heap(TIterator first, TIterator last, TCompare compare) { return private_heap::is_heap(first, last - first, compare); @@ -1111,6 +1121,7 @@ namespace etl // Sort Heap template + ETL_CONSTEXPR14 void sort_heap(TIterator first, TIterator last) { while (first != last) @@ -1122,6 +1133,7 @@ namespace etl // Sort Heap template + ETL_CONSTEXPR14 void sort_heap(TIterator first, TIterator last, TCompare compare) { while (first != last) @@ -1131,6 +1143,119 @@ namespace etl } } + //*************************************************************************** + /// partial_sort + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + void partial_sort(TIterator first, TIterator middle, TIterator last, TCompare compare) + { + if (first == middle) + { + return; + } + + typedef typename etl::iterator_traits::value_type value_t; + typedef typename etl::iterator_traits::difference_type difference_t; + + etl::make_heap(first, middle, compare); + + for (TIterator i = middle; i != last; ++i) + { + if (compare(*i, *first)) + { + value_t value = ETL_MOVE(*i); + *i = ETL_MOVE(*first); + + private_heap::adjust_heap(first, difference_t(0), difference_t(middle - first), ETL_MOVE(value), compare); + } + } + + etl::sort_heap(first, middle, compare); + } + + //*************************************************************************** + /// partial_sort + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + void partial_sort(TIterator first, TIterator middle, TIterator last) + { + typedef etl::less::value_type> compare; + + etl::partial_sort(first, middle, last, compare()); + } + + //*************************************************************************** + /// partial_sort_copy + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + TRandomAccessIterator partial_sort_copy(TInputIterator first, + TInputIterator last, + TRandomAccessIterator d_first, + TRandomAccessIterator d_last, + TCompare compare) + { + typedef typename etl::iterator_traits::value_type value_t; + typedef typename etl::iterator_traits::difference_type difference_t; + + TRandomAccessIterator result = d_first; + + // Fill the destination range + while ((first != last) && (result != d_last)) + { + *result = *first; + ++result; + ++first; + } + + if (result == d_first) + { + return result; + } + + // Build a max-heap over the destination range + etl::make_heap(d_first, result, compare); + + // Process remaining input elements + for (TInputIterator i = first; i != last; ++i) + { + if (compare(*i, *d_first)) + { + value_t value = *i; + private_heap::adjust_heap(d_first, difference_t(0), difference_t(result - d_first), ETL_MOVE(value), compare); + } + } + + etl::sort_heap(d_first, result, compare); + + return result; + } + + //*************************************************************************** + /// partial_sort_copy + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + TRandomAccessIterator partial_sort_copy(TInputIterator first, + TInputIterator last, + TRandomAccessIterator d_first, + TRandomAccessIterator d_last) + { + typedef etl::less::value_type> compare; + + return etl::partial_sort_copy(first, last, d_first, d_last, compare()); + } + //*************************************************************************** // Search //*************************************************************************** @@ -1185,7 +1310,6 @@ namespace etl //*************************************************************************** namespace private_algorithm { -#if ETL_USING_CPP11 //********************************* // For random access iterators template @@ -1204,21 +1328,21 @@ namespace etl } typedef typename etl::iterator_traits::value_type value_type; + typedef typename etl::iterator_traits::difference_type difference_type; - int n = last - first; - int m = middle - first; - int gcd_nm = (n == 0 || m == 0) ? n + m : etl::gcd(n, m); - + difference_type n = last - first; + difference_type m = middle - first; + difference_type gcd_nm = (n == 0 || m == 0) ? n + m : etl::gcd(n, m); TIterator result = first + (last - middle); - for (int i = 0; i < gcd_nm; i++) + for (difference_type i = 0; i < gcd_nm; i++) { value_type temp = ETL_MOVE(*(first + i)); - int j = i; + difference_type j = i; while (true) { - int k = j + m; + difference_type k = j + m; if (k >= n) { @@ -1239,61 +1363,6 @@ namespace etl return result; } -#else - //********************************* - // For random access iterators - template - ETL_CONSTEXPR14 - typename etl::enable_if::value, TIterator>::type - rotate_general(TIterator first, TIterator middle, TIterator last) - { - if (first == middle) - { - return last; - } - - if (middle == last) - { - return first; - } - - typedef typename etl::iterator_traits::value_type value_type; - - int n = last - first; - int m = middle - first; - int gcd_nm = (n == 0 || m == 0) ? n + m : etl::gcd(n, m); - - TIterator result = first + (last - middle); - - for (int i = 0; i < gcd_nm; i++) - { - value_type temp = *(first + i); - int j = i; - - while (true) - { - int k = j + m; - - if (k >= n) - { - k = k - n; - } - - if (k == i) - { - break; - } - - *(first + j) = *(first + k); - j = k; - } - - *(first + j) = temp; - } - - return result; - } -#endif //********************************* // For bidirectional iterators @@ -1410,26 +1479,29 @@ namespace etl ETL_CONSTEXPR14 TIterator rotate(TIterator first, TIterator middle, TIterator last) { + if (first == middle) + { + return last; + } + if (middle == last) + { + return first; + } + if (etl::next(first) == middle) { return private_algorithm::rotate_left_by_one(first, last); } +#if ETL_USING_CPP20 if (etl::next(middle) == last) { -#if ETL_USING_CPP20 - if ETL_IF_CONSTEXPR(etl::is_forward_iterator::value) - { - return private_algorithm::rotate_general(first, middle, last); - } - else + if ETL_IF_CONSTEXPR(etl::is_bidirectional_iterator_concept::value) { return private_algorithm::rotate_right_by_one(first, last); } -#else - return private_algorithm::rotate_general(first, middle, last); -#endif } +#endif return private_algorithm::rotate_general(first, middle, last); } @@ -1661,35 +1733,6 @@ namespace etl return compare(b, a) ? ETL_OR_STD::pair(b, a) : ETL_OR_STD::pair(a, b); } - //*************************************************************************** - /// is_sorted_until - ///\ingroup algorithm - /// - //*************************************************************************** - template - ETL_NODISCARD - ETL_CONSTEXPR14 - TIterator is_sorted_until(TIterator begin, - TIterator end) - { - if (begin != end) - { - TIterator next = begin; - - while (++next != end) - { - if (*next < *begin) - { - return next; - } - - ++begin; - } - } - - return end; - } - //*************************************************************************** /// is_sorted_until ///\ingroup algorithm @@ -1720,6 +1763,22 @@ namespace etl return end; } + //*************************************************************************** + /// is_sorted_until + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + TIterator is_sorted_until(TIterator begin, + TIterator end) + { + typedef etl::less::value_type> compare; + + return etl::is_sorted_until(begin, end, compare()); + } + //*************************************************************************** /// is_sorted ///\ingroup algorithm @@ -1788,22 +1847,9 @@ namespace etl TIterator is_unique_sorted_until(TIterator begin, TIterator end) { - if (begin != end) - { - TIterator next = begin; + typedef etl::less::value_type> compare; - while (++next != end) - { - if (!(*begin < *next)) - { - return next; - } - - ++begin; - } - } - - return end; + return etl::is_unique_sorted_until(begin, end, compare()); } //*************************************************************************** @@ -1858,6 +1904,51 @@ namespace etl return end; } + //*************************************************************************** + /// adjacent_find + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + TIterator adjacent_find(TIterator first, TIterator last, TBinaryPredicate predicate) + { + if (first != last) + { + TIterator next = first; + ++next; + + while (next != last) + { + if (predicate(*first, *next)) + { + return first; + } + + ++first; + ++next; + } + } + + return last; + } + + //*************************************************************************** + /// adjacent_find + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + TIterator adjacent_find(TIterator first, TIterator last) + { + typedef etl::equal_to::value_type> predicate; + + return etl::adjacent_find(first, last, predicate()); + } + //*************************************************************************** /// is_permutation ///\ingroup algorithm @@ -1916,9 +2007,9 @@ namespace etl { if (i == etl::find_if(begin1, i, etl::bind1st(predicate, *i))) { - size_t n = etl::count(begin2, end2, *i); + size_t n = etl::count_if(begin2, end2, etl::bind1st(predicate, *i)); - if (n == 0 || size_t(etl::count(i, end1, *i)) != n) + if (n == 0 || size_t(etl::count_if(i, end1, etl::bind1st(predicate, *i))) != n) { return false; } @@ -1942,18 +2033,20 @@ namespace etl TIterator2 begin2, TIterator2 end2) { - if (begin1 != end1) + if (etl::distance(begin1, end1) != etl::distance(begin2, end2)) { - for (TIterator1 i = begin1; i != end1; ++i) - { - if (i == etl::find(begin1, i, *i)) - { - size_t n = etl::count(begin2, end2, *i); + return false; + } - if (n == 0 || size_t(etl::count(i, end1, *i)) != n) - { - return false; - } + for (TIterator1 i = begin1; i != end1; ++i) + { + if (i == etl::find(begin1, i, *i)) + { + size_t n = etl::count(begin2, end2, *i); + + if (n == 0 || size_t(etl::count(i, end1, *i)) != n) + { + return false; } } } @@ -1968,24 +2061,27 @@ namespace etl //*************************************************************************** template ETL_NODISCARD + ETL_CONSTEXPR14 bool is_permutation(TIterator1 begin1, TIterator1 end1, TIterator2 begin2, TIterator2 end2, TBinaryPredicate predicate) { - if (begin1 != end1) + if (etl::distance(begin1, end1) != etl::distance(begin2, end2)) { - for (TIterator1 i = begin1; i != end1; ++i) - { - if (i == etl::find_if(begin1, i, etl::bind1st(predicate, *i))) - { - size_t n = etl::count(begin2, end2, *i); + return false; + } - if (n == 0 || size_t(etl::count(i, end1, *i)) != n) - { - return false; - } + for (TIterator1 i = begin1; i != end1; ++i) + { + if (i == etl::find_if(begin1, i, etl::bind1st(predicate, *i))) + { + size_t n = etl::count_if(begin2, end2, etl::bind1st(predicate, *i)); + + if (n == 0 || size_t(etl::count_if(i, end1, etl::bind1st(predicate, *i))) != n) + { + return false; } } } @@ -2040,14 +2136,22 @@ namespace etl TIterator end, TUnaryPredicate predicate) { - while (begin != end) - { - if (!predicate(*begin)) - { - return begin; - } + typedef typename etl::iterator_traits::difference_type difference_t; - ++begin; + // binary search on a partitioned range + for (difference_t length = etl::distance(begin, end); 0 < length; ) + { + difference_t half = length / 2; + TIterator middle = etl::next(begin, half); + if (predicate(*middle)) + { + begin = etl::next(middle); + length -= (half + 1); + } + else + { + length = half; + } } return begin; @@ -2364,6 +2468,253 @@ namespace etl return first; } + + //*************************************************************************** + /// unique + /// see https://en.cppreference.com/w/cpp/algorithm/unique + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TIterator unique(TIterator first, TIterator last) + { + if (first == last) + { + return last; + } + + TIterator result = first; + + while (++first != last) + { + if (!(*result == *first) && (++result != first)) + { + *result = ETL_MOVE(*first); + } + } + + return ++result; + } + + //*************************************************************************** + /// unique + /// see https://en.cppreference.com/w/cpp/algorithm/unique + /// predicate overload to determine equality. + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TIterator unique(TIterator first, TIterator last, TBinaryPredicate predicate) + { + if (first == last) + { + return last; + } + + TIterator result = first; + + while (++first != last) + { + if (!predicate(*result, *first) && (++result != first)) + { + *result = ETL_MOVE(*first); + } + } + + return ++result; + } + + //*************************************************************************** + /// unique_copy + /// see https://en.cppreference.com/w/cpp/algorithm/unique_copy + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TOutputIterator unique_copy(TInputIterator first, + TInputIterator last, + TOutputIterator d_first) + { + if (first == last) + { + return d_first; + } + + typename etl::iterator_traits::value_type prev = *first; + *d_first = prev; + + while (++first != last) + { + if (!(prev == *first)) + { + prev = *first; + *(++d_first) = prev; + } + } + + return ++d_first; + } + + //*************************************************************************** + /// unique_copy + /// see https://en.cppreference.com/w/cpp/algorithm/unique_copy + /// predicate overload to determine equality. + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TOutputIterator unique_copy(TInputIterator first, + TInputIterator last, + TOutputIterator d_first, + TBinaryPredicate predicate) + { + if (first == last) + { + return d_first; + } + + typename etl::iterator_traits::value_type prev = *first; + *d_first = prev; + + while (++first != last) + { + if (!predicate(prev, *first)) + { + prev = *first; + *(++d_first) = prev; + } + } + + return ++d_first; + } + + //*************************************************************************** + /// merge + /// Merges two sorted ranges into one sorted range. + /// see https://en.cppreference.com/w/cpp/algorithm/merge + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TOutputIterator merge(TInputIterator1 first1, TInputIterator1 last1, + TInputIterator2 first2, TInputIterator2 last2, + TOutputIterator d_first, + TCompare compare) + { + while ((first1 != last1) && (first2 != last2)) + { + if (compare(*first2, *first1)) + { + *d_first = *first2; + ++first2; + } + else + { + *d_first = *first1; + ++first1; + } + ++d_first; + } + + d_first = etl::copy(first1, last1, d_first); + d_first = etl::copy(first2, last2, d_first); + + return d_first; + } + + //*************************************************************************** + /// merge + /// Merges two sorted ranges into one sorted range. + /// Uses operator< for comparison. + /// see https://en.cppreference.com/w/cpp/algorithm/merge + ///\ingroup algorithm + //*************************************************************************** + template + ETL_CONSTEXPR14 + TOutputIterator merge(TInputIterator1 first1, TInputIterator1 last1, + TInputIterator2 first2, TInputIterator2 last2, + TOutputIterator d_first) + { + typedef etl::less::value_type> compare; + + return etl::merge(first1, last1, first2, last2, d_first, compare()); + } + + //*************************************************************************** + /// inplace_merge + /// Merges two consecutive sorted ranges [first, middle) and [middle, last) + /// into one sorted range [first, last) in-place. + /// Uses an iterative rotate-based algorithm that requires no additional + /// memory, no recursion and no explicit stack, making it safe for deeply + /// embedded targets with constrained stack sizes. + /// Complexity: O(N log N) comparisons, O(N log N) element moves. + /// see https://en.cppreference.com/w/cpp/algorithm/inplace_merge + ///\ingroup algorithm + //*************************************************************************** + template + void inplace_merge(TBidirectionalIterator first, + TBidirectionalIterator middle, + TBidirectionalIterator last, + TCompare compare) + { + typedef typename etl::iterator_traits::difference_type difference_type; + + difference_type len1 = etl::distance(first, middle); + difference_type len2 = etl::distance(middle, last); + + while ((len1 != 0) && (len2 != 0)) + { + // Find where the first element of the right half belongs in the left half. + // All elements in [first, cut1) are <= *middle, so they are already in place. + TBidirectionalIterator cut1 = etl::upper_bound(first, middle, *middle, compare); + difference_type prefix = etl::distance(first, cut1); + len1 -= prefix; + + // If the entire left half is <= *middle, we are done. + if (len1 == 0) + { + return; + } + + // Advance first past the already-placed prefix. + first = cut1; + + // Find where the first element of the (remaining) left half belongs in + // the right half. All elements in [middle, cut2) are < *first, so they + // need to be moved before *first. + TBidirectionalIterator cut2 = etl::lower_bound(middle, last, *first, compare); + difference_type run = etl::distance(middle, cut2); + len2 -= run; + + // Rotate the block [first, middle, cut2) so that [middle, cut2) moves + // before [first, middle). After the rotate the elements from + // [middle, cut2) (length = run) now occupy [first, first + run) and + // are in their final position. + etl::rotate(first, middle, cut2); + + // Advance past the block we just placed. + etl::advance(first, run); + middle = cut2; + } + } + + //*************************************************************************** + /// inplace_merge + /// Merges two consecutive sorted ranges [first, middle) and [middle, last) + /// into one sorted range [first, last) in-place. + /// Uses operator< for comparison. + /// see https://en.cppreference.com/w/cpp/algorithm/inplace_merge + ///\ingroup algorithm + //*************************************************************************** + template + void inplace_merge(TBidirectionalIterator first, + TBidirectionalIterator middle, + TBidirectionalIterator last) + { + typedef etl::less::value_type> compare; + + etl::inplace_merge(first, middle, last, compare()); + } } //***************************************************************************** @@ -3160,6 +3511,11 @@ namespace etl ETL_CONSTEXPR20 void selection_sort(TIterator first, TIterator last, TCompare compare) { + if (first == last) + { + return; + } + TIterator min; const TIterator ilast = private_algorithm::get_before_last(first, last); const TIterator jlast = last; @@ -3180,7 +3536,10 @@ namespace etl } using ETL_OR_STD::swap; // Allow ADL - swap(*i, *min); + if (min != i) + { + swap(*i, *min); + } } } @@ -3204,11 +3563,7 @@ namespace etl ETL_CONSTEXPR14 void heap_sort(TIterator first, TIterator last, TCompare compare) { - if (!etl::is_heap(first, last, compare)) - { - etl::make_heap(first, last, compare); - } - + etl::make_heap(first, last, compare); etl::sort_heap(first, last, compare); } @@ -3220,11 +3575,7 @@ namespace etl ETL_CONSTEXPR14 void heap_sort(TIterator first, TIterator last) { - if (!etl::is_heap(first, last)) - { - etl::make_heap(first, last); - } - + etl::make_heap(first, last); etl::sort_heap(first, last); } @@ -3268,7 +3619,7 @@ namespace etl #endif //*************************************************************************** - /// Returns the maximum value. + /// Returns the minimum value. //*************************************************************************** #if ETL_USING_CPP11 template @@ -3464,14 +3815,7 @@ namespace etl { typedef typename etl::iterator_traits::value_type value_type; - TIterator pivot = last; // Maybe find a better pivot choice? - value_type pivot_value = *pivot; - - // Swap the pivot with the last, if necessary. - if (pivot != last) - { - swap(*pivot, *last); - } + value_type pivot_value = ETL_MOVE(*last); TIterator i = first; @@ -3570,7 +3914,7 @@ namespace etl { typedef etl::less::value_type> compare_t; - nth_element(first, last, compare_t()); + nth_element(first, nth, last, compare_t()); } #endif } diff --git a/test/test_algorithm.cpp b/test/test_algorithm.cpp index 4dd01e9f..1429a977 100644 --- a/test/test_algorithm.cpp +++ b/test/test_algorithm.cpp @@ -747,6 +747,62 @@ namespace } } + //************************************************************************* + TEST(binary_search_random_iterator) + { + for (int i = 0; i < 11; ++i) + { + bool expected = std::binary_search(std::begin(dataS), std::end(dataS), i); + bool result = etl::binary_search(std::begin(dataS), std::end(dataS), i); + + CHECK_EQUAL(expected, result); + } + } + + //************************************************************************* + TEST(binary_search_with_compare) + { + for (int i = 0; i < 11; ++i) + { + bool expected = std::binary_search(std::begin(dataS), std::end(dataS), i, etl::less()); + bool result = etl::binary_search(std::begin(dataS), std::end(dataS), i, etl::less()); + + CHECK_EQUAL(expected, result); + } + } + + //************************************************************************* + TEST(binary_search_duplicates) + { + for (int i = 0; i < 11; ++i) + { + bool expected = std::binary_search(std::begin(dataEQ), std::end(dataEQ), i); + bool result = etl::binary_search(std::begin(dataEQ), std::end(dataEQ), i); + + CHECK_EQUAL(expected, result); + } + } + + //************************************************************************* + TEST(binary_search_empty_range) + { + int empty[] = { 0 }; + + bool result = etl::binary_search(std::begin(empty), std::begin(empty), 1); + + CHECK_EQUAL(false, result); + } + + //************************************************************************* + TEST(binary_search_single_element) + { + int single[] = { 5 }; + + CHECK_EQUAL(true, etl::binary_search(std::begin(single), std::end(single), 5)); + CHECK_EQUAL(false, etl::binary_search(std::begin(single), std::end(single), 3)); + CHECK_EQUAL(false, etl::binary_search(std::begin(single), std::end(single), 7)); + } + //************************************************************************* TEST(fill_non_char) { @@ -795,6 +851,144 @@ namespace CHECK_EQUAL(1, b); } + //************************************************************************* + TEST(swap_ranges_pod_pointer) + { + int data1[] = { 1, 2, 3, 4, 5 }; + int data2[] = { 6, 7, 8, 9, 10 }; + + int expected1[] = { 6, 7, 8, 9, 10 }; + int expected2[] = { 1, 2, 3, 4, 5 }; + + int* result = etl::swap_ranges(std::begin(data1), std::end(data1), std::begin(data2)); + + CHECK_EQUAL(std::end(data2), result); + + bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1)); + CHECK(isEqual1); + + bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2)); + CHECK(isEqual2); + } + + //************************************************************************* + TEST(swap_ranges_non_pod_pointer) + { + Data data1[] = { Data(1, 2), Data(3, 4), Data(5, 6) }; + Data data2[] = { Data(7, 8), Data(9, 10), Data(11, 12) }; + + Data expected1[] = { Data(7, 8), Data(9, 10), Data(11, 12) }; + Data expected2[] = { Data(1, 2), Data(3, 4), Data(5, 6) }; + + Data* result = etl::swap_ranges(std::begin(data1), std::end(data1), std::begin(data2)); + + CHECK_EQUAL(std::end(data2), result); + + bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1)); + CHECK(isEqual1); + + bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2)); + CHECK(isEqual2); + } + + //************************************************************************* + TEST(swap_ranges_non_random_iterator) + { + List data1 = { 1, 2, 3, 4, 5 }; + List data2 = { 6, 7, 8, 9, 10 }; + + List expected1 = { 6, 7, 8, 9, 10 }; + List expected2 = { 1, 2, 3, 4, 5 }; + + List::iterator result = etl::swap_ranges(data1.begin(), data1.end(), data2.begin()); + + CHECK(data2.end() == result); + + bool isEqual1 = std::equal(data1.begin(), data1.end(), expected1.begin()); + CHECK(isEqual1); + + bool isEqual2 = std::equal(data2.begin(), data2.end(), expected2.begin()); + CHECK(isEqual2); + } + + //************************************************************************* + TEST(swap_ranges_empty_range) + { + int data1[] = { 1, 2, 3 }; + int data2[] = { 4, 5, 6 }; + + int expected1[] = { 1, 2, 3 }; + int expected2[] = { 4, 5, 6 }; + + int* result = etl::swap_ranges(std::begin(data1), std::begin(data1), std::begin(data2)); + + CHECK_EQUAL(std::begin(data2), result); + + bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1)); + CHECK(isEqual1); + + bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2)); + CHECK(isEqual2); + } + + //************************************************************************* + TEST(swap_ranges_partial_range) + { + int data1[] = { 1, 2, 3, 4, 5 }; + int data2[] = { 6, 7, 8, 9, 10 }; + + int expected1[] = { 6, 7, 8, 4, 5 }; + int expected2[] = { 1, 2, 3, 9, 10 }; + + int* result = etl::swap_ranges(std::begin(data1), std::begin(data1) + 3, std::begin(data2)); + + CHECK_EQUAL(std::begin(data2) + 3, result); + + bool isEqual1 = std::equal(std::begin(data1), std::end(data1), std::begin(expected1)); + CHECK(isEqual1); + + bool isEqual2 = std::equal(std::begin(data2), std::end(data2), std::begin(expected2)); + CHECK(isEqual2); + } + + //************************************************************************* + TEST(swap_ranges_same_data) + { + int data1[] = { 1, 2, 3, 4, 5 }; + int expected[] = { 1, 2, 3, 4, 5 }; + + etl::swap_ranges(std::begin(data1), std::end(data1), std::begin(data1)); + + bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(expected)); + CHECK(isEqual); + } + + //************************************************************************* + TEST(swap_ranges_matches_std) + { + int data1_std[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int data2_std[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + int data1_etl[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int data2_etl[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; + + int* pstl = std::swap_ranges(std::begin(data1_std), std::end(data1_std), std::begin(data2_std)); + int* petl = etl::swap_ranges(std::begin(data1_etl), std::end(data1_etl), std::begin(data2_etl)); + + using difference_type_t = std::iterator_traits::difference_type; + + difference_type_t dstl = std::distance(data2_std, pstl); + difference_type_t detl = std::distance(data2_etl, petl); + + CHECK_EQUAL(dstl, detl); + + bool isEqual1 = std::equal(std::begin(data1_std), std::end(data1_std), std::begin(data1_etl)); + CHECK(isEqual1); + + bool isEqual2 = std::equal(std::begin(data2_std), std::end(data2_std), std::begin(data2_etl)); + CHECK(isEqual2); + } + //************************************************************************* TEST(equal) { @@ -854,6 +1048,314 @@ namespace CHECK(itr1 == itr2); } + //************************************************************************* + TEST(find_end_default) + { + int data[] = { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 }; + int pattern[] = { 1, 2, 3 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_predicate) + { + int data[] = { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 }; + int pattern[] = { 1, 2, 3 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern), std::equal_to()); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern), std::equal_to()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_single_occurrence) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int pattern[] = { 5, 6, 7 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_no_match) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int pattern[] = { 11, 12 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_empty_sequence) + { + int data[] = { 1, 2, 3, 4, 5 }; + int pattern[] = { 0 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::begin(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::begin(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_pattern_at_end) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7 }; + int pattern[] = { 5, 6, 7 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_pattern_at_start) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7 }; + int pattern[] = { 1, 2, 3 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_entire_range_matches) + { + int data[] = { 1, 2, 3, 4, 5 }; + int pattern[] = { 1, 2, 3, 4, 5 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_overlapping_occurrences) + { + int data[] = { 1, 1, 1, 1, 1 }; + int pattern[] = { 1, 1 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_single_element_pattern) + { + int data[] = { 1, 2, 3, 2, 5, 2, 7 }; + int pattern[] = { 2 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(find_end_non_random_iterator) + { + int data_array[] = { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 }; + int pattern_array[] = { 1, 2, 3 }; + + List data(std::begin(data_array), std::end(data_array)); + List pattern(std::begin(pattern_array), std::end(pattern_array)); + + List::iterator expected = std::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to()); + List::iterator result = etl::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to()); + + CHECK(expected == result); + } + + //************************************************************************* + TEST(find_end_non_random_iterator_predicate) + { + int data_array[] = { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 }; + int pattern_array[] = { 1, 2, 3 }; + + List data(std::begin(data_array), std::end(data_array)); + List pattern(std::begin(pattern_array), std::end(pattern_array)); + + List::iterator expected = std::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to()); + List::iterator result = etl::find_end(data.begin(), data.end(), pattern.begin(), pattern.end(), std::equal_to()); + + CHECK(expected == result); + } + + //************************************************************************* + TEST(find_end_pattern_longer_than_data) + { + int data[] = { 1, 2, 3 }; + int pattern[] = { 1, 2, 3, 4, 5 }; + + int* expected = std::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + int* result = etl::find_end(std::begin(data), std::end(data), std::begin(pattern), std::end(pattern)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_default) + { + int data[] = { 1, 2, 3, 3, 4, 5 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_predicate) + { + int data[] = { 1, 2, 3, 3, 4, 5 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data), std::equal_to()); + int* result = etl::adjacent_find(std::begin(data), std::end(data), std::equal_to()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_no_match) + { + int data[] = { 1, 2, 3, 4, 5, 6 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_at_beginning) + { + int data[] = { 1, 1, 2, 3, 4, 5 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_at_end) + { + int data[] = { 1, 2, 3, 4, 5, 5 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_multiple_pairs) + { + int data[] = { 1, 1, 2, 2, 3, 3 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_single_element) + { + int data[] = { 1 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_empty_range) + { + int data[] = { 1 }; + + int* expected = std::adjacent_find(std::begin(data), std::begin(data)); + int* result = etl::adjacent_find(std::begin(data), std::begin(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_all_same) + { + int data[] = { 5, 5, 5, 5, 5 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data)); + int* result = etl::adjacent_find(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_predicate_less) + { + int data[] = { 5, 4, 3, 2, 1 }; + + int* expected = std::adjacent_find(std::begin(data), std::end(data), std::greater()); + int* result = etl::adjacent_find(std::begin(data), std::end(data), std::greater()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(adjacent_find_non_random_iterator) + { + int data_array[] = { 1, 2, 3, 3, 4, 5 }; + List data(std::begin(data_array), std::end(data_array)); + + List::iterator expected = std::adjacent_find(data.begin(), data.end()); + List::iterator result = etl::adjacent_find(data.begin(), data.end()); + + CHECK(expected == result); + } + + //************************************************************************* + TEST(adjacent_find_non_random_iterator_predicate) + { + int data_array[] = { 1, 2, 3, 3, 4, 5 }; + List data(std::begin(data_array), std::end(data_array)); + + List::iterator expected = std::adjacent_find(data.begin(), data.end(), std::equal_to()); + List::iterator result = etl::adjacent_find(data.begin(), data.end(), std::equal_to()); + + CHECK(expected == result); + } + + //************************************************************************* + TEST(adjacent_find_non_random_iterator_no_match) + { + int data_array[] = { 1, 2, 3, 4, 5, 6 }; + List data(std::begin(data_array), std::end(data_array)); + + List::iterator expected = std::adjacent_find(data.begin(), data.end()); + List::iterator result = etl::adjacent_find(data.begin(), data.end()); + + CHECK(expected == result); + } + //************************************************************************* TEST(heap) { @@ -1021,6 +1523,663 @@ namespace CHECK(isEqual); } + //************************************************************************* + TEST(is_heap_default_true) + { + std::vector data = { 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + std::make_heap(data.begin(), data.end()); + + bool expected = std::is_heap(data.begin(), data.end()); + bool result = etl::is_heap(data.begin(), data.end()); + + CHECK_EQUAL(expected, result); + CHECK(result); + } + + //************************************************************************* + TEST(is_heap_default_false) + { + std::vector data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + bool expected = std::is_heap(data.begin(), data.end()); + bool result = etl::is_heap(data.begin(), data.end()); + + CHECK_EQUAL(expected, result); + CHECK(!result); + } + + //************************************************************************* + TEST(is_heap_compare_true) + { + std::vector data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + std::make_heap(data.begin(), data.end(), Greater()); + + bool expected = std::is_heap(data.begin(), data.end(), Greater()); + bool result = etl::is_heap(data.begin(), data.end(), Greater()); + + CHECK_EQUAL(expected, result); + CHECK(result); + } + + //************************************************************************* + TEST(is_heap_compare_false) + { + std::vector data = { 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + + bool expected = std::is_heap(data.begin(), data.end(), Greater()); + bool result = etl::is_heap(data.begin(), data.end(), Greater()); + + CHECK_EQUAL(expected, result); + CHECK(!result); + } + + //************************************************************************* + TEST(is_heap_empty) + { + std::vector data; + + bool expected = std::is_heap(data.begin(), data.end()); + bool result = etl::is_heap(data.begin(), data.end()); + + CHECK_EQUAL(expected, result); + CHECK(result); + } + + //************************************************************************* + TEST(is_heap_single_element) + { + std::vector data = { 42 }; + + bool expected = std::is_heap(data.begin(), data.end()); + bool result = etl::is_heap(data.begin(), data.end()); + + CHECK_EQUAL(expected, result); + CHECK(result); + } + + //************************************************************************* + TEST(is_heap_two_elements) + { + std::vector data1 = { 5, 3 }; + std::vector data2 = { 3, 5 }; + + CHECK_EQUAL(std::is_heap(data1.begin(), data1.end()), etl::is_heap(data1.begin(), data1.end())); + CHECK_EQUAL(std::is_heap(data2.begin(), data2.end()), etl::is_heap(data2.begin(), data2.end())); + } + + //************************************************************************* + TEST(is_heap_pointer) + { + int data[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + + bool expected = std::is_heap(std::begin(data), std::end(data)); + bool result = etl::is_heap(std::begin(data), std::end(data)); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(is_heap_after_make_heap) + { + // Test all permutations of a small dataset + std::vector data = { 1, 2, 3, 4, 5 }; + + do + { + std::vector test_data(data); + etl::make_heap(test_data.begin(), test_data.end()); + CHECK(etl::is_heap(test_data.begin(), test_data.end())); + } while (std::next_permutation(data.begin(), data.end())); + } + + //************************************************************************* + TEST(sort_heap_default) + { + std::vector data1 = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector data2(data1); + + std::make_heap(data1.begin(), data1.end()); + etl::make_heap(data2.begin(), data2.end()); + + std::sort_heap(data1.begin(), data1.end()); + etl::sort_heap(data2.begin(), data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin()); + CHECK(isEqual); + + // Verify sorted ascending + CHECK(std::is_sorted(data2.begin(), data2.end())); + } + + //************************************************************************* + TEST(sort_heap_compare) + { + std::vector data1 = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector data2(data1); + + std::make_heap(data1.begin(), data1.end(), Greater()); + etl::make_heap(data2.begin(), data2.end(), Greater()); + + std::sort_heap(data1.begin(), data1.end(), Greater()); + etl::sort_heap(data2.begin(), data2.end(), Greater()); + + bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin()); + CHECK(isEqual); + + // Verify sorted descending + CHECK(std::is_sorted(data2.begin(), data2.end(), Greater())); + } + + //************************************************************************* + TEST(sort_heap_empty) + { + std::vector data; + + etl::sort_heap(data.begin(), data.end()); + + CHECK(data.empty()); + } + + //************************************************************************* + TEST(sort_heap_single_element) + { + std::vector data = { 42 }; + + etl::sort_heap(data.begin(), data.end()); + + CHECK_EQUAL(1U, data.size()); + CHECK_EQUAL(42, data[0]); + } + + //************************************************************************* + TEST(sort_heap_pointer) + { + int data1[] = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + int data2[] = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + + std::make_heap(std::begin(data1), std::end(data1)); + etl::make_heap(std::begin(data2), std::end(data2)); + + std::sort_heap(std::begin(data1), std::end(data1)); + etl::sort_heap(std::begin(data2), std::end(data2)); + + bool isEqual = std::equal(std::begin(data1), std::end(data1), std::begin(data2)); + CHECK(isEqual); + } + + //************************************************************************* + TEST(sort_heap_all_permutations) + { + std::vector initial = { 1, 2, 3, 4, 5 }; + + do + { + std::vector data1(initial); + std::vector data2(initial); + + std::make_heap(data1.begin(), data1.end()); + etl::make_heap(data2.begin(), data2.end()); + + std::sort_heap(data1.begin(), data1.end()); + etl::sort_heap(data2.begin(), data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin()); + CHECK(isEqual); + } while (std::next_permutation(initial.begin(), initial.end())); + } + + //************************************************************************* + TEST(sort_heap_duplicates) + { + std::vector data1 = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5 }; + std::vector data2(data1); + + std::make_heap(data1.begin(), data1.end()); + etl::make_heap(data2.begin(), data2.end()); + + std::sort_heap(data1.begin(), data1.end()); + etl::sort_heap(data2.begin(), data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST(partial_sort_default) + { + std::vector data1 = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 5, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end()); + + // The first 5 elements should be sorted and match std + bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin()); + CHECK(isEqual); + + // Verify sorted range + CHECK(std::is_sorted(data2.begin(), data2.begin() + 5)); + } + + //************************************************************************* + TEST(partial_sort_compare) + { + std::vector data1 = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 5, data1.end(), Greater()); + etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end(), Greater()); + + // The first 5 elements should be sorted descending and match std + bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin()); + CHECK(isEqual); + + // Verify sorted range (descending) + CHECK(std::is_sorted(data2.begin(), data2.begin() + 5, Greater())); + } + + //************************************************************************* + TEST(partial_sort_empty) + { + std::vector data; + + etl::partial_sort(data.begin(), data.begin(), data.end()); + + CHECK(data.empty()); + } + + //************************************************************************* + TEST(partial_sort_middle_equals_first) + { + std::vector data = { 5, 3, 8, 1, 9 }; + std::vector original(data); + + etl::partial_sort(data.begin(), data.begin(), data.end()); + + // Nothing should change when middle == first + bool isEqual = std::equal(data.begin(), data.end(), original.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST(partial_sort_middle_equals_last) + { + std::vector data1 = { 5, 3, 8, 1, 9 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.end(), data1.end()); + etl::partial_sort(data2.begin(), data2.end(), data2.end()); + + // Full sort + bool isEqual = std::equal(data1.begin(), data1.end(), data2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(data2.begin(), data2.end())); + } + + //************************************************************************* + TEST(partial_sort_single_element) + { + std::vector data = { 42 }; + + etl::partial_sort(data.begin(), data.end(), data.end()); + + CHECK_EQUAL(1U, data.size()); + CHECK_EQUAL(42, data[0]); + } + + //************************************************************************* + TEST(partial_sort_first_one) + { + std::vector data1 = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 1, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 1, data2.end()); + + // The first element should be the minimum + CHECK_EQUAL(data1[0], data2[0]); + CHECK_EQUAL(1, data2[0]); + } + + //************************************************************************* + TEST(partial_sort_duplicates) + { + std::vector data1 = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 6, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 6, data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.begin() + 6, data2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(data2.begin(), data2.begin() + 6)); + } + + //************************************************************************* + TEST(partial_sort_already_sorted) + { + std::vector data1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 5, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(data2.begin(), data2.begin() + 5)); + } + + //************************************************************************* + TEST(partial_sort_reverse_sorted) + { + std::vector data1 = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + std::vector data2(data1); + + std::partial_sort(data1.begin(), data1.begin() + 5, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 5, data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.begin() + 5, data2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(data2.begin(), data2.begin() + 5)); + } + + //************************************************************************* + TEST(partial_sort_pointer) + { + int data1[] = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + int data2[] = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + + std::partial_sort(std::begin(data1), std::begin(data1) + 5, std::end(data1)); + etl::partial_sort(std::begin(data2), std::begin(data2) + 5, std::end(data2)); + + bool isEqual = std::equal(std::begin(data1), std::begin(data1) + 5, std::begin(data2)); + CHECK(isEqual); + } + + //************************************************************************* + TEST(partial_sort_all_permutations) + { + std::vector initial = { 1, 2, 3, 4, 5 }; + + do + { + std::vector data1(initial); + std::vector data2(initial); + + std::partial_sort(data1.begin(), data1.begin() + 3, data1.end()); + etl::partial_sort(data2.begin(), data2.begin() + 3, data2.end()); + + bool isEqual = std::equal(data1.begin(), data1.begin() + 3, data2.begin()); + CHECK(isEqual); + } while (std::next_permutation(initial.begin(), initial.end())); + } + + //************************************************************************* + TEST(partial_sort_all_equal) + { + std::vector data = { 5, 5, 5, 5, 5, 5 }; + + etl::partial_sort(data.begin(), data.begin() + 3, data.end()); + + CHECK(std::is_sorted(data.begin(), data.begin() + 3)); + + for (size_t i = 0; i < data.size(); ++i) + { + CHECK_EQUAL(5, data[i]); + } + } + + //************************************************************************* + TEST(partial_sort_copy_default) + { + std::vector input = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector output1(5); + std::vector output2(5); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_compare) + { + std::vector input = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector output1(5); + std::vector output2(5); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end(), Greater()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end(), Greater()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end(), Greater())); + } + + //************************************************************************* + TEST(partial_sort_copy_empty_input) + { + std::vector input; + std::vector output(5, 99); + + auto result = etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(result == output.begin()); + + // Output should be unchanged + for (size_t i = 0; i < output.size(); ++i) + { + CHECK_EQUAL(99, output[i]); + } + } + + //************************************************************************* + TEST(partial_sort_copy_empty_output) + { + std::vector input = { 5, 3, 8, 1, 9 }; + std::vector output; + + auto result = etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(result == output.begin()); + } + + //************************************************************************* + TEST(partial_sort_copy_output_larger_than_input) + { + std::vector input = { 5, 3, 1 }; + std::vector output1(6, 99); + std::vector output2(6, 99); + + auto result1 = std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + auto result2 = etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + CHECK_EQUAL(std::distance(output1.begin(), result1), std::distance(output2.begin(), result2)); + + bool isEqual = std::equal(output1.begin(), result1, output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), result2)); + } + + //************************************************************************* + TEST(partial_sort_copy_output_smaller_than_input) + { + std::vector input = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector output1(3); + std::vector output2(3); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_output_same_size_as_input) + { + std::vector input = { 5, 3, 8, 1, 9 }; + std::vector output1(5); + std::vector output2(5); + + auto result1 = std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + auto result2 = etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + CHECK(result1 == output1.end()); + CHECK(result2 == output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_single_element_input) + { + std::vector input = { 42 }; + std::vector output(3, 0); + + auto result = etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(result == output.begin() + 1); + CHECK_EQUAL(42, output[0]); + } + + //************************************************************************* + TEST(partial_sort_copy_single_element_output) + { + std::vector input = { 5, 3, 8, 1, 9 }; + std::vector output1(1); + std::vector output2(1); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + CHECK_EQUAL(output1[0], output2[0]); + CHECK_EQUAL(1, output2[0]); + } + + //************************************************************************* + TEST(partial_sort_copy_duplicates) + { + std::vector input = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5 }; + std::vector output1(6); + std::vector output2(6); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_already_sorted) + { + std::vector input = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + std::vector output1(5); + std::vector output2(5); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_reverse_sorted) + { + std::vector input = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + std::vector output1(5); + std::vector output2(5); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + + //************************************************************************* + TEST(partial_sort_copy_pointer) + { + int input[] = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + int output1[5] = {}; + int output2[5] = {}; + + std::partial_sort_copy(std::begin(input), std::end(input), std::begin(output1), std::end(output1)); + etl::partial_sort_copy(std::begin(input), std::end(input), std::begin(output2), std::end(output2)); + + bool isEqual = std::equal(std::begin(output1), std::end(output1), std::begin(output2)); + CHECK(isEqual); + } + + //************************************************************************* + TEST(partial_sort_copy_all_equal) + { + std::vector input = { 5, 5, 5, 5, 5, 5 }; + std::vector output(3); + + etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + CHECK(std::is_sorted(output.begin(), output.end())); + + for (size_t i = 0; i < output.size(); ++i) + { + CHECK_EQUAL(5, output[i]); + } + } + + //************************************************************************* + TEST(partial_sort_copy_input_not_modified) + { + std::vector input = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector original(input); + std::vector output(5); + + etl::partial_sort_copy(input.begin(), input.end(), output.begin(), output.end()); + + // Input should not be modified + bool isEqual = std::equal(input.begin(), input.end(), original.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST(partial_sort_copy_from_list) + { + std::list input = { 5, 3, 8, 1, 9, 2, 7, 4, 6, 10 }; + std::vector output1(5); + std::vector output2(5); + + std::partial_sort_copy(input.begin(), input.end(), output1.begin(), output1.end()); + etl::partial_sort_copy(input.begin(), input.end(), output2.begin(), output2.end()); + + bool isEqual = std::equal(output1.begin(), output1.end(), output2.begin()); + CHECK(isEqual); + + CHECK(std::is_sorted(output2.begin(), output2.end())); + } + //************************************************************************* TEST(find) { @@ -1199,6 +2358,216 @@ namespace CHECK_EQUAL(5U, *(data2[4])); } + //************************************************************************* + TEST(move_s_random_iterator_same_size) + { + typedef std::vector> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + data1.push_back(std::unique_ptr(new uint32_t(4U))); + data1.push_back(std::unique_ptr(new uint32_t(5U))); + + Data data2(5); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.end() == result); + + CHECK_EQUAL(1U, *(data2[0])); + CHECK_EQUAL(2U, *(data2[1])); + CHECK_EQUAL(3U, *(data2[2])); + CHECK_EQUAL(4U, *(data2[3])); + CHECK_EQUAL(5U, *(data2[4])); + } + + //************************************************************************* + TEST(move_s_random_iterator_destination_smaller) + { + typedef std::vector> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + data1.push_back(std::unique_ptr(new uint32_t(4U))); + data1.push_back(std::unique_ptr(new uint32_t(5U))); + + Data data2(3); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.end() == result); + + CHECK_EQUAL(1U, *(data2[0])); + CHECK_EQUAL(2U, *(data2[1])); + CHECK_EQUAL(3U, *(data2[2])); + } + + //************************************************************************* + TEST(move_s_random_iterator_source_smaller) + { + typedef std::vector> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + + Data data2(5); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.begin() + 3 == result); + + CHECK_EQUAL(1U, *(data2[0])); + CHECK_EQUAL(2U, *(data2[1])); + CHECK_EQUAL(3U, *(data2[2])); + } + + //************************************************************************* + TEST(move_s_non_random_iterator_same_size) + { + typedef std::list> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + data1.push_back(std::unique_ptr(new uint32_t(4U))); + data1.push_back(std::unique_ptr(new uint32_t(5U))); + + Data data2(5); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.end() == result); + + Data::iterator itr = data2.begin(); + CHECK_EQUAL(1U, **(itr++)); + CHECK_EQUAL(2U, **(itr++)); + CHECK_EQUAL(3U, **(itr++)); + CHECK_EQUAL(4U, **(itr++)); + CHECK_EQUAL(5U, **(itr++)); + } + + //************************************************************************* + TEST(move_s_non_random_iterator_destination_smaller) + { + typedef std::list> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + data1.push_back(std::unique_ptr(new uint32_t(4U))); + data1.push_back(std::unique_ptr(new uint32_t(5U))); + + Data data2(3); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.end() == result); + + Data::iterator itr = data2.begin(); + CHECK_EQUAL(1U, **(itr++)); + CHECK_EQUAL(2U, **(itr++)); + CHECK_EQUAL(3U, **(itr++)); + } + + //************************************************************************* + TEST(move_s_non_random_iterator_source_smaller) + { + typedef std::list> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + + Data data2(5); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + Data::iterator expected_pos = data2.begin(); + std::advance(expected_pos, 3); + CHECK(expected_pos == result); + + Data::iterator itr = data2.begin(); + CHECK_EQUAL(1U, **(itr++)); + CHECK_EQUAL(2U, **(itr++)); + CHECK_EQUAL(3U, **(itr++)); + } + + //************************************************************************* + TEST(move_s_empty_source) + { + typedef std::vector> Data; + + Data data1; + Data data2(3); + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.begin() == result); + } + + //************************************************************************* + TEST(move_s_empty_destination) + { + typedef std::vector> Data; + + Data data1; + data1.push_back(std::unique_ptr(new uint32_t(1U))); + data1.push_back(std::unique_ptr(new uint32_t(2U))); + data1.push_back(std::unique_ptr(new uint32_t(3U))); + + Data data2; + + Data::iterator result = etl::move_s(data1.begin(), data1.end(), data2.begin(), data2.end()); + + CHECK(data2.begin() == result); + } + + //************************************************************************* + TEST(move_s_pod_random_iterator) + { + int data1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int data2[] = { 1, 2, 3, 4, 5 }; + + int out1[10]; + int out2[5]; + + int check1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int check2[] = { 1, 2, 3, 4, 5 }; + int check3[] = { 1, 2, 3, 4, 5, 0, 0, 0, 0, 0 }; + + int* result; + + // Same size. + std::fill(std::begin(out1), std::end(out1), 0); + result = etl::move_s(std::begin(data1), std::end(data1), std::begin(out1), std::end(out1)); + CHECK_EQUAL(std::end(out1), result); + bool is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check1)); + CHECK(is_same); + + // Destination smaller. + std::fill(std::begin(out2), std::end(out2), 0); + result = etl::move_s(std::begin(data1), std::end(data1), std::begin(out2), std::end(out2)); + CHECK_EQUAL(std::end(out2), result); + is_same = std::equal(std::begin(out2), std::end(out2), std::begin(check2)); + CHECK(is_same); + + // Source smaller. + std::fill(std::begin(out1), std::end(out1), 0); + result = etl::move_s(std::begin(data2), std::end(data2), std::begin(out1), std::end(out1)); + CHECK_EQUAL(std::begin(out1) + 5, result); + is_same = std::equal(std::begin(out1), std::end(out1), std::begin(check3)); + CHECK(is_same); + } + //************************************************************************* TEST(rotate_pod) { @@ -1405,6 +2774,29 @@ namespace CHECK(!is_permutation); } + //************************************************************************* + TEST(is_permutation_different_lengths) + { + int shorter[] = { 1, 2 }; + int longer[] = { 1, 2, 3 }; + + // Four-iterator: range2 longer than range1 (extra elements only in range2) + bool result = etl::is_permutation(std::begin(shorter), std::end(shorter), std::begin(longer), std::end(longer)); + CHECK(!result); + + // Four-iterator: range1 longer than range2 + result = etl::is_permutation(std::begin(longer), std::end(longer), std::begin(shorter), std::end(shorter)); + CHECK(!result); + + // Four-iterator with predicate: range2 longer than range1 + result = etl::is_permutation(std::begin(shorter), std::end(shorter), std::begin(longer), std::end(longer), etl::equal_to()); + CHECK(!result); + + // Four-iterator with predicate: range1 longer than range2 + result = etl::is_permutation(std::begin(longer), std::end(longer), std::begin(shorter), std::end(shorter), etl::equal_to()); + CHECK(!result); + } + //************************************************************************* TEST(is_partitioned) { @@ -2227,6 +3619,30 @@ namespace } } + //************************************************************************* + TEST(selection_sort_empty_range) + { + // Forward iterators + std::forward_list fwd_data; + etl::selection_sort(fwd_data.begin(), fwd_data.end()); + CHECK(fwd_data.empty()); + + // Bidirectional iterators + std::list bidir_data; + etl::selection_sort(bidir_data.begin(), bidir_data.end()); + CHECK(bidir_data.empty()); + + // Random access iterators + std::vector ra_data; + etl::selection_sort(ra_data.begin(), ra_data.end()); + CHECK(ra_data.empty()); + + // With comparator + std::forward_list fwd_data2; + etl::selection_sort(fwd_data2.begin(), fwd_data2.end(), std::greater()); + CHECK(fwd_data2.empty()); + } + //************************************************************************* TEST(heap_sort_default) { @@ -2371,6 +3787,275 @@ namespace CHECK(is_same); } + //************************************************************************* + TEST(unique) + { + std::array data = { 1, 1, 2, 3, 3, 3, 4, 4, 5, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + + auto end = etl::unique(data.begin(), data.end()); + + CHECK_EQUAL(5, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_empty_range) + { + std::array data = {}; + + auto end = etl::unique(data.begin(), data.end()); + + CHECK(end == data.end()); + } + + //************************************************************************* + TEST(unique_single_element) + { + std::array data = { 42 }; + std::array expected = { 42 }; + + auto end = etl::unique(data.begin(), data.end()); + + CHECK_EQUAL(1, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_no_duplicates) + { + std::array data = { 1, 2, 3, 4, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + + auto end = etl::unique(data.begin(), data.end()); + + CHECK_EQUAL(5, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_all_same) + { + std::array data = { 7, 7, 7, 7, 7 }; + std::array expected = { 7 }; + + auto end = etl::unique(data.begin(), data.end()); + + CHECK_EQUAL(1, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_with_predicate) + { + std::array data = { 1, 1, 2, 3, 3, 3, 4, 4, 5, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + + auto end = etl::unique(data.begin(), data.end(), std::equal_to()); + + CHECK_EQUAL(5, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_with_predicate_custom) + { + // Group elements that are close to each other (differ by less than 3) + std::array data = { 1, 2, 3, 7, 8, 9, 20, 21 }; + std::array expected = { 1, 7, 20 }; + + auto end = etl::unique(data.begin(), data.end(), [](int a, int b) { return (b - a) < 3; }); + + CHECK_EQUAL(3, std::distance(data.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_matches_std) + { + std::array data1 = { 1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5 }; + std::array data2 = data1; + + auto std_end = std::unique(data1.begin(), data1.end()); + auto etl_end = etl::unique(data2.begin(), data2.end()); + + size_t std_size = std::distance(data1.begin(), std_end); + size_t etl_size = std::distance(data2.begin(), etl_end); + + CHECK_EQUAL(std_size, etl_size); + bool is_same = std::equal(data1.begin(), std_end, data2.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_with_predicate_matches_std) + { + std::array data1 = { 1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5 }; + std::array data2 = data1; + + auto std_end = std::unique(data1.begin(), data1.end(), std::equal_to()); + auto etl_end = etl::unique(data2.begin(), data2.end(), std::equal_to()); + + size_t std_size = std::distance(data1.begin(), std_end); + size_t etl_size = std::distance(data2.begin(), etl_end); + + CHECK_EQUAL(std_size, etl_size); + bool is_same = std::equal(data1.begin(), std_end, data2.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy) + { + std::array data = { 1, 1, 2, 3, 3, 3, 4, 4, 5, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin()); + + CHECK_EQUAL(5, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_empty_range) + { + std::array data = {}; + std::array result = { 99 }; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin()); + + CHECK(end == result.begin()); + CHECK_EQUAL(99, result[0]); // output unchanged + } + + //************************************************************************* + TEST(unique_copy_single_element) + { + std::array data = { 42 }; + std::array expected = { 42 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin()); + + CHECK_EQUAL(1, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_no_duplicates) + { + std::array data = { 1, 2, 3, 4, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin()); + + CHECK_EQUAL(5, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_all_same) + { + std::array data = { 7, 7, 7, 7, 7 }; + std::array expected = { 7 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin()); + + CHECK_EQUAL(1, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_source_unchanged) + { + std::array data = { 1, 1, 2, 3, 3, 3, 4, 4, 5, 5 }; + std::array original = data; + std::array result = {}; + + etl::unique_copy(data.begin(), data.end(), result.begin()); + + bool is_same = std::equal(original.begin(), original.end(), data.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_with_predicate) + { + std::array data = { 1, 1, 2, 3, 3, 3, 4, 4, 5, 5 }; + std::array expected = { 1, 2, 3, 4, 5 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin(), std::equal_to()); + + CHECK_EQUAL(5, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_with_predicate_custom) + { + // Group elements that are close to each other (differ by less than 3) + std::array data = { 1, 2, 3, 7, 8, 9, 20, 21 }; + std::array expected = { 1, 7, 20 }; + std::array result = {}; + + auto end = etl::unique_copy(data.begin(), data.end(), result.begin(), [](int a, int b) { return (b - a) < 3; }); + + CHECK_EQUAL(3, std::distance(result.begin(), end)); + bool is_same = std::equal(expected.begin(), expected.end(), result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_matches_std) + { + std::array data = { 1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5 }; + std::array std_result = {}; + std::array etl_result = {}; + + auto std_end = std::unique_copy(data.begin(), data.end(), std_result.begin()); + auto etl_end = etl::unique_copy(data.begin(), data.end(), etl_result.begin()); + + size_t std_size = std::distance(std_result.begin(), std_end); + size_t etl_size = std::distance(etl_result.begin(), etl_end); + + CHECK_EQUAL(std_size, etl_size); + bool is_same = std::equal(std_result.begin(), std_end, etl_result.begin()); + CHECK(is_same); + } + + //************************************************************************* + TEST(unique_copy_with_predicate_matches_std) + { + std::array data = { 1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5 }; + std::array std_result = {}; + std::array etl_result = {}; + + auto std_end = std::unique_copy(data.begin(), data.end(), std_result.begin(), std::equal_to()); + auto etl_end = etl::unique_copy(data.begin(), data.end(), etl_result.begin(), std::equal_to()); + + size_t std_size = std::distance(std_result.begin(), std_end); + size_t etl_size = std::distance(etl_result.begin(), etl_end); + + CHECK_EQUAL(std_size, etl_size); + bool is_same = std::equal(std_result.begin(), std_end, etl_result.begin()); + CHECK(is_same); + } + //************************************************************************* struct generator { @@ -2556,6 +4241,116 @@ namespace } } + //************************************************************************* + TEST(accumulate_default) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 0); + int result = etl::accumulate(std::begin(data), std::end(data), 0); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_with_initial_value) + { + int data[] = { 1, 2, 3, 4, 5 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 100); + int result = etl::accumulate(std::begin(data), std::end(data), 100); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_custom_operation) + { + int data[] = { 1, 2, 3, 4, 5 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 1, std::multiplies()); + int result = etl::accumulate(std::begin(data), std::end(data), 1, std::multiplies()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_empty_range) + { + int data[] = { 1 }; + + int expected = std::accumulate(std::begin(data), std::begin(data), 42); + int result = etl::accumulate(std::begin(data), std::begin(data), 42); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_single_element) + { + int data[] = { 7 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 0); + int result = etl::accumulate(std::begin(data), std::end(data), 0); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_negative_values) + { + int data[] = { -3, -2, -1, 0, 1, 2, 3 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 0); + int result = etl::accumulate(std::begin(data), std::end(data), 0); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_custom_operation_subtraction) + { + int data[] = { 1, 2, 3, 4, 5 }; + + int expected = std::accumulate(std::begin(data), std::end(data), 100, std::minus()); + int result = etl::accumulate(std::begin(data), std::end(data), 100, std::minus()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_non_random_iterator) + { + List data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + int expected = std::accumulate(data.begin(), data.end(), 0); + int result = etl::accumulate(data.begin(), data.end(), 0); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_non_random_iterator_custom_operation) + { + List data = { 1, 2, 3, 4, 5 }; + + int expected = std::accumulate(data.begin(), data.end(), 1, std::multiplies()); + int result = etl::accumulate(data.begin(), data.end(), 1, std::multiplies()); + + CHECK_EQUAL(expected, result); + } + + //************************************************************************* + TEST(accumulate_double) + { + double data[] = { 1.5, 2.5, 3.5, 4.5, 5.5 }; + + double expected = std::accumulate(std::begin(data), std::end(data), 0.0); + double result = etl::accumulate(std::begin(data), std::end(data), 0.0); + + CHECK_CLOSE(expected, result, 1e-10); + } + //************************************************************************* TEST(clamp_run_time) { @@ -2591,5 +4386,432 @@ namespace CHECK_EQUAL(0, result5); CHECK_EQUAL(10, result6); } + + //************************************************************************* + TEST(merge_default_comparator) + { + int input1[] = { 1, 3, 5, 7, 9 }; + int input2[] = { 2, 4, 6, 8, 10 }; + int output[10]; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 10); + } + + //************************************************************************* + TEST(merge_custom_comparator) + { + int input1[] = { 9, 7, 5, 3, 1 }; + int input2[] = { 10, 8, 6, 4, 2 }; + int output[10]; + int expected[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output), + Greater()); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 10); + } + + //************************************************************************* + TEST(merge_first_range_empty) + { + int input1[] = { 0 }; // dummy, won't be used + int input2[] = { 1, 2, 3 }; + int output[3]; + int expected[] = { 1, 2, 3 }; + + int* result = etl::merge(input1, input1, // empty range + std::begin(input2), std::end(input2), + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 3); + } + + //************************************************************************* + TEST(merge_second_range_empty) + { + int input1[] = { 1, 2, 3 }; + int input2[] = { 0 }; // dummy, won't be used + int output[3]; + int expected[] = { 1, 2, 3 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + input2, input2, // empty range + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 3); + } + + //************************************************************************* + TEST(merge_both_ranges_empty) + { + int input1[] = { 0 }; + int output[] = { 99 }; + + int* result = etl::merge(input1, input1, + input1, input1, + std::begin(output)); + + CHECK_EQUAL(std::begin(output), result); + CHECK_EQUAL(99, output[0]); // output should be unchanged + } + + //************************************************************************* + TEST(merge_with_duplicates) + { + int input1[] = { 1, 3, 3, 5 }; + int input2[] = { 2, 3, 4, 5 }; + int output[8]; + int expected[] = { 1, 2, 3, 3, 3, 4, 5, 5 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 8); + } + + //************************************************************************* + TEST(merge_different_sizes) + { + int input1[] = { 1, 5 }; + int input2[] = { 2, 3, 4, 6, 7, 8 }; + int output[8]; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 8); + } + + //************************************************************************* + TEST(merge_single_elements) + { + int input1[] = { 1 }; + int input2[] = { 2 }; + int output[2]; + int expected[] = { 1, 2 }; + + int* result = etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output)); + + CHECK_EQUAL(std::end(output), result); + CHECK_ARRAY_EQUAL(expected, output, 2); + } + + //************************************************************************* + TEST(merge_with_list_iterators) + { + std::list input1 = { 1, 3, 5, 7 }; + std::list input2 = { 2, 4, 6, 8 }; + std::vector output(8); + std::vector expected = { 1, 2, 3, 4, 5, 6, 7, 8 }; + + std::vector::iterator result = etl::merge(input1.begin(), input1.end(), + input2.begin(), input2.end(), + output.begin()); + + CHECK(output.end() == result); + CHECK_ARRAY_EQUAL(expected.data(), output.data(), 8); + } + + //************************************************************************* + TEST(merge_matches_std) + { + int input1[] = { 1, 4, 7, 8, 10 }; + int input2[] = { 2, 3, 5, 6, 9 }; + int etl_output[10]; + int std_output[10]; + + etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(etl_output)); + + std::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(std_output)); + + CHECK_ARRAY_EQUAL(std_output, etl_output, 10); + } + + //************************************************************************* + TEST(merge_matches_std_with_comparator) + { + int input1[] = { 10, 8, 7, 4, 1 }; + int input2[] = { 9, 6, 5, 3, 2 }; + int etl_output[10]; + int std_output[10]; + + etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(etl_output), + Greater()); + + std::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(std_output), + Greater()); + + CHECK_ARRAY_EQUAL(std_output, etl_output, 10); + } + + //************************************************************************* + TEST(merge_stability) + { + // Test that merge is stable: equivalent elements from the first range + // come before those from the second range. + Data input1[] = { Data(1, 1), Data(3, 1), Data(5, 1) }; + Data input2[] = { Data(1, 2), Data(3, 2), Data(5, 2) }; + Data output[6]; + + etl::merge(std::begin(input1), std::end(input1), + std::begin(input2), std::end(input2), + std::begin(output), + DataPredicate()); + + // Elements from input1 (b==1) should come before elements from input2 (b==2) + // for equivalent keys. + CHECK_EQUAL(1, output[0].a); CHECK_EQUAL(1, output[0].b); // from input1 + CHECK_EQUAL(1, output[1].a); CHECK_EQUAL(2, output[1].b); // from input2 + CHECK_EQUAL(3, output[2].a); CHECK_EQUAL(1, output[2].b); // from input1 + CHECK_EQUAL(3, output[3].a); CHECK_EQUAL(2, output[3].b); // from input2 + CHECK_EQUAL(5, output[4].a); CHECK_EQUAL(1, output[4].b); // from input1 + CHECK_EQUAL(5, output[5].a); CHECK_EQUAL(2, output[5].b); // from input2 + } + + //************************************************************************* + TEST(inplace_merge_default_comparator) + { + int data[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 }; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 10); + } + + //************************************************************************* + TEST(inplace_merge_custom_comparator) + { + int data[] = { 9, 7, 5, 3, 1, 10, 8, 6, 4, 2 }; + int expected[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data), Greater()); + + CHECK_ARRAY_EQUAL(expected, data, 10); + } + + //************************************************************************* + TEST(inplace_merge_first_range_empty) + { + int data[] = { 1, 2, 3, 4, 5 }; + int expected[] = { 1, 2, 3, 4, 5 }; + + etl::inplace_merge(std::begin(data), std::begin(data), std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 5); + } + + //************************************************************************* + TEST(inplace_merge_second_range_empty) + { + int data[] = { 1, 2, 3, 4, 5 }; + int expected[] = { 1, 2, 3, 4, 5 }; + + etl::inplace_merge(std::begin(data), std::end(data), std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 5); + } + + //************************************************************************* + TEST(inplace_merge_both_ranges_empty) + { + int data[] = { 99 }; + + etl::inplace_merge(data, data, data); // empty range + + CHECK_EQUAL(99, data[0]); // unchanged + } + + //************************************************************************* + TEST(inplace_merge_with_duplicates) + { + int data[] = { 1, 3, 3, 5, 2, 3, 4, 5 }; + int expected[] = { 1, 2, 3, 3, 3, 4, 5, 5 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 4, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 8); + } + + //************************************************************************* + TEST(inplace_merge_different_sizes) + { + int data[] = { 1, 5, 2, 3, 4, 6, 7, 8 }; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 2, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 8); + } + + //************************************************************************* + TEST(inplace_merge_single_elements) + { + int data[] = { 2, 1 }; + int expected[] = { 1, 2 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 1, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 2); + } + + //************************************************************************* + TEST(inplace_merge_single_element_halves_already_sorted) + { + int data[] = { 1, 2 }; + int expected[] = { 1, 2 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 1, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 2); + } + + //************************************************************************* + TEST(inplace_merge_already_sorted) + { + int data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 10); + } + + //************************************************************************* + TEST(inplace_merge_reverse_halves) + { + // Second half all less than first half + int data[] = { 6, 7, 8, 9, 10, 1, 2, 3, 4, 5 }; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 10); + } + + //************************************************************************* + TEST(inplace_merge_with_list_iterators) + { + std::vector data = { 1, 3, 5, 7, 2, 4, 6, 8 }; + std::vector expected = { 1, 2, 3, 4, 5, 6, 7, 8 }; + + etl::inplace_merge(data.begin(), data.begin() + 4, data.end()); + + CHECK(expected == data); + } + + //************************************************************************* + TEST(inplace_merge_matches_std) + { + int etl_data[] = { 1, 4, 7, 8, 10, 2, 3, 5, 6, 9 }; + int std_data[] = { 1, 4, 7, 8, 10, 2, 3, 5, 6, 9 }; + + etl::inplace_merge(std::begin(etl_data), std::begin(etl_data) + 5, std::end(etl_data)); + std::inplace_merge(std::begin(std_data), std::begin(std_data) + 5, std::end(std_data)); + + CHECK_ARRAY_EQUAL(std_data, etl_data, 10); + } + + //************************************************************************* + TEST(inplace_merge_matches_std_with_comparator) + { + int etl_data[] = { 10, 8, 7, 4, 1, 9, 6, 5, 3, 2 }; + int std_data[] = { 10, 8, 7, 4, 1, 9, 6, 5, 3, 2 }; + + etl::inplace_merge(std::begin(etl_data), std::begin(etl_data) + 5, std::end(etl_data), Greater()); + std::inplace_merge(std::begin(std_data), std::begin(std_data) + 5, std::end(std_data), Greater()); + + CHECK_ARRAY_EQUAL(std_data, etl_data, 10); + } + + //************************************************************************* + TEST(inplace_merge_stability) + { + // Test that inplace_merge is stable: equivalent elements from the first + // range come before those from the second range. + Data data[] = { Data(1, 1), Data(3, 1), Data(5, 1), + Data(1, 2), Data(3, 2), Data(5, 2) }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 3, std::end(data), DataPredicate()); + + // Elements from first half (b==1) should come before elements from second half (b==2) + // for equivalent keys. + CHECK_EQUAL(1, data[0].a); CHECK_EQUAL(1, data[0].b); // from first half + CHECK_EQUAL(1, data[1].a); CHECK_EQUAL(2, data[1].b); // from second half + CHECK_EQUAL(3, data[2].a); CHECK_EQUAL(1, data[2].b); // from first half + CHECK_EQUAL(3, data[3].a); CHECK_EQUAL(2, data[3].b); // from second half + CHECK_EQUAL(5, data[4].a); CHECK_EQUAL(1, data[4].b); // from first half + CHECK_EQUAL(5, data[5].a); CHECK_EQUAL(2, data[5].b); // from second half + } + + //************************************************************************* + TEST(inplace_merge_single_element_first_half) + { + int data[] = { 5, 1, 2, 3, 4 }; + int expected[] = { 1, 2, 3, 4, 5 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 1, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 5); + } + + //************************************************************************* + TEST(inplace_merge_single_element_second_half) + { + int data[] = { 1, 2, 3, 4, 5, 3 }; + int expected[] = { 1, 2, 3, 3, 4, 5 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 5, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 6); + } + + //************************************************************************* + TEST(inplace_merge_all_equal) + { + int data[] = { 5, 5, 5, 5, 5, 5 }; + int expected[] = { 5, 5, 5, 5, 5, 5 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 3, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 6); + } + + //************************************************************************* + TEST(inplace_merge_interleaved) + { + int data[] = { 1, 3, 5, 7, 9, 11, 2, 4, 6, 8, 10, 12 }; + int expected[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; + + etl::inplace_merge(std::begin(data), std::begin(data) + 6, std::end(data)); + + CHECK_ARRAY_EQUAL(expected, data, 12); + } } } From 54a1c82c98969d130634da5c990e4756ec4b3ddb Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 12 Mar 2026 16:54:51 +0100 Subject: [PATCH 09/31] Fix run-syntax-checks.sh to be executable (#1342) Co-authored-by: John Wellbelove --- test/run-syntax-checks.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 test/run-syntax-checks.sh diff --git a/test/run-syntax-checks.sh b/test/run-syntax-checks.sh old mode 100644 new mode 100755 From 0a56d40bdd3d86f3f7fc59df6579c5a137411ca9 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 12 Mar 2026 17:38:10 +0100 Subject: [PATCH 10/31] Fix undefined behaviour of etl::absolute() for etl::numeric_limits::min (#1333) absolute_unsigned() already handled that case correctly. Now adding this check to absolute() also. It can be caught at compile time now, if in a constexpr context. Also separating integral and non-integral versions via enable_if. Co-authored-by: John Wellbelove --- include/etl/absolute.h | 31 +++++++++++++++++++++++++++++-- test/test_math_functions.cpp | 3 +-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/include/etl/absolute.h b/include/etl/absolute.h index cad11b7c..52cfe8bd 100644 --- a/include/etl/absolute.h +++ b/include/etl/absolute.h @@ -34,16 +34,43 @@ SOFTWARE. #include "platform.h" #include "type_traits.h" #include "integral_limits.h" +#include "error_handler.h" namespace etl { + namespace private_absolute + { + //************************************************************************* + // Non-constexpr function that is never called for valid inputs. + // If reached during constant evaluation, the compiler emits an error + // because it's not constexpr. + // At runtime, triggers the ETL assert handler. + //************************************************************************* + template + inline T signed_min_error() + { + ETL_ASSERT_FAIL(ETL_ERROR_GENERIC("absolute value of minimum signed integer is undefined")); + return T(0); + } + } + //*************************************************************************** // For signed types. //*************************************************************************** template ETL_NODISCARD - ETL_CONSTEXPR - typename etl::enable_if::value, T>::type + ETL_CONSTEXPR + typename etl::enable_if::value && etl::is_integral::value, T>::type + absolute(T value) + { + return (value == etl::integral_limits::min) ? etl::private_absolute::signed_min_error() + : (value < T(0)) ? -value : value; + } + + template + ETL_NODISCARD + ETL_CONSTEXPR + typename etl::enable_if::value && !etl::is_integral::value, T>::type absolute(T value) ETL_NOEXCEPT { return (value < T(0)) ? -value : value; diff --git a/test/test_math_functions.cpp b/test/test_math_functions.cpp index 0b7c9205..f5ef7842 100644 --- a/test/test_math_functions.cpp +++ b/test/test_math_functions.cpp @@ -574,12 +574,11 @@ namespace constexpr uint16_t absolute1 = etl::absolute(int16_t(0)); constexpr uint16_t absolute2 = etl::absolute(int16_t(32767)); constexpr uint16_t absolute3 = etl::absolute(int16_t(-32767)); - constexpr uint16_t absolute4 = etl::absolute(int16_t(-32768)); + //constexpr uint16_t absolute4 = etl::absolute(int16_t(-32768)); // Compile error CHECK_EQUAL(uint16_t(0), absolute1); CHECK_EQUAL(uint16_t(32767), absolute2); CHECK_EQUAL(uint16_t(32767), absolute3); - CHECK_EQUAL(uint16_t(32768), absolute4); constexpr uint16_t absolute5 = etl::absolute(uint16_t(0)); constexpr uint16_t absolute6 = etl::absolute(uint16_t(65535)); From d3affac417827a9d2bef270c3430dcf334cd9d00 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 12 Mar 2026 17:06:26 +0000 Subject: [PATCH 11/31] Enforce o(log n) dispatch for messages when using fsm for c++11 and up (#1337) * Updated message handling to be worst case O(logN) * Copied optimised message handling from etl::fsm * Updated fsm generator * Updated message_router generator * Added optimised accepts() member function * Modified comment, as the FSM doesn't support a successor * Updated version and release notes * Hotfix/etl multiset iterator invalidation during erase leads to incorrect sorted order in depth first traversal (#1317) * Fixed issue for both multiset and multimap * Added std::is_sorted checks to all map/set tests * Updated with coderabbit suggestions --------- Co-authored-by: John Wellbelove * Updated release notes and version * Changed std::is_same to etl::is_same in struct type_list_is_unique (#1320) Co-authored-by: John Wellbelove * Updated release notes and version * Fix etl::rotate (#1327) Per the C++ standard, std::rotate returns first + (last - middle): * When first == middle, return last * When middle == last, return first * Added missing files from VS2022 project * Fix greater_equal and less_equal (#1331) * Align comparison operators (#1330) In functional.h, the comparison operators for equal_to and not_equal_to mismatch between the actual comparison execution and the type inference for the return type. This change adjusts it by using the same operator==() in the return type inference as used in the comparison execution. Co-authored-by: John Wellbelove * Add missing tests (#1321) * Add missing tests * Typo fixes --------- Co-authored-by: John Wellbelove * Add ETL_FORMAT_NO_FLOATING_POINT control macro for etl::format (#1329) When ETL_FORMAT_NO_FLOATING_POINT is defined, all floating-point formatting support (float, double, long double) is excluded from etl::format. This reduces code size on targets that do not require floating-point formatting. Guarded sections: - #include - float/double/long double in supported_format_types variant - float/double/long double constructors in basic_format_arg - format_floating_* functions and format_aligned_floating - formatter, formatter, formatter - Floating-point test cases in test_format.cpp Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus Co-authored-by: John Wellbelove * Manchester documentation (#1325) * manchester * Added manchester code and test * manchester * Formatting and added missing file * manchester * Some functions can only be constexpr since C++14 * manchester * Manchester decode and some refactoring * manchester * Added some missing typenames * manchester * constexpr void function not allowed in C++11 * manchester * condition on static_assert tests * manchester * revert CMakeLists.txt * Using ETL_STATIC_ASSERT * Some cleanup * manchester * Added static_assert message * manchester * Added compile time tests * manchester * Added invert manchester * Some refactoring * manchester * Disable test for now * Move ETL_NODISCARD before static * manchester * Test for valid_span * manchester * Remove redundant (?) storage specifiers for template specializations. Storage specifier already given in base template * manchester * refactoring to get rid of specialized template functions in template class * manchester * cleanup * manchester * Added documentation comments * Some refactoring * manchester * introducing namespace detail_manchester * manchester * Some refactoring * Update tests * manchester * Some refactoring * Removed possible undefined behavior by refactoring encode_span * constexpr version of encode_span * Static assertion for rare case where code doesn't work because CHAR_BIT is not the same as the number of bits in uint_least8_t * manchester * renamed valid to is_valid * manchester * renamed is_valid_span to is_valid * Using etl exceptions in ETL_ASSERT * manchester * Removed _fast functions * merged encode_in_place with encode and decode_in_place with decode * removed _span to create normal overloads of encode and decode for span * Some renaming and minor refactoring * manchester * Fix build issues * manchester * Conditionally compile manchester_decoded * Update test_manchester.cpp Removed redundant semicolon * #1258 Manchester coding * Formatting * consistency: hex literals with lower case 0x * #1258 Manchester coding * Moved copyright to top of file * Make constexpr encode/decode span functions equal for little and big endian platforms * #1258 Manchester coding * Added missing include * Added missing 8bit/64bit guards * Fixed is_valid for big endian platforms * #1258 Manchester coding * private memcpy alias * #1258 Manchester coding * Review comments * #1258 Manchester coding * Cleanup * Fix build error * #1258 Manchester coding * Add manchester documentation * #1258 Manchester coding * Preparation for GitHub pages * #1324 Manchester documentation * Some small fixes --------- Co-authored-by: Timon Zijnge * Changes from review of algorithm.h on development branch (#1340) * Add missing constexpr in algorithm.h * Fix call of nth_element 2nd argument (nth) was missing * Replace partition point with O(log(N)) algorithm The C++ standard defines O(log(N)) calls of predicate as the complexity of partition_point(). The old algorithm was linear. * Use predicate in calculation of is_permutation consistently In case of predicate not equal_to, the calculation previously returned wron results * Omit swap in selection_sort if iterators are equal * Use difference_type in rotate_general() instead of int * Typo fix in algorithm.h * Simplifications in algorithm.h Application of plain refactoring by keeping semantics * Guard against past-end iterator in etl::rotate() And fix scope of rotate_right_by_one for etl::rotate() * Support empty ranges in selection_sort * Add tests for swap_ranges * Add tests for binary_search * Add tests for find_end * Add tests for accumulate * Add tests for move_s * Added tests for is_heap and sort_heap * Remove early exit for empty input * Add adjacent_find * Add unique * Add unique_copy * Add merge * Add inplace_merge * Add partial_sort * Add partial_sort_copy * copilot review change --------- Co-authored-by: John Wellbelove Co-authored-by: Roland Reichwein Co-authored-by: Niu Zhihong Co-authored-by: Sisyphus Co-authored-by: Timon Zijnge <47081647+tzijnge@users.noreply.github.com> Co-authored-by: Timon Zijnge --- include/etl/fsm.h | 410 +++++++++++++----- include/etl/generators/fsm_generator.h | 376 ++++++++++++---- .../etl/generators/message_router_generator.h | 54 +-- include/etl/message_router.h | 54 +-- test/test_fsm.cpp | 4 +- 5 files changed, 633 insertions(+), 265 deletions(-) diff --git a/include/etl/fsm.h b/include/etl/fsm.h index 36130a47..a00206e7 100644 --- a/include/etl/fsm.h +++ b/include/etl/fsm.h @@ -62,6 +62,7 @@ SOFTWARE. #include "largest.h" #if ETL_USING_CPP11 #include "tuple.h" + #include "type_list.h" #endif #include @@ -83,14 +84,14 @@ namespace etl // For internal FSM use. typedef typename etl::larger_type::type fsm_internal_id_t; -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above template class fsm_state; #else template class fsm_state; #endif @@ -195,7 +196,7 @@ namespace etl // Pass this whenever no state change is desired. // The highest unsigned value of fsm_state_id_t. static ETL_CONSTANT fsm_state_id_t No_State_Change = etl::integral_limits::max; - + // Pass this when this event also needs to be passed to the parent. static ETL_CONSTANT fsm_state_id_t Pass_To_Parent = No_State_Change - 1U; @@ -213,15 +214,15 @@ namespace etl ETL_CONSTANT fsm_state_id_t ifsm_state_helper::Self_Transition; // Compile-time: TState::ID must equal its index in the type list (0..N-1) - template struct check_ids : etl::true_type + template struct check_ids : etl::true_type { }; template struct check_ids - : etl::integral_constant::value> + : etl::integral_constant::value> { - }; + }; //*************************************************************************** /// RAII detection mechanism to catch reentrant calls to methods that might @@ -250,11 +251,11 @@ namespace etl { is_locked = false; } - + private: // Reference to the flag signifying a lock on the state machine. bool& is_locked; - + // Copy & move semantics disabled since this is a guard. fsm_reentrancy_guard(fsm_reentrancy_guard const&) ETL_DELETE; fsm_reentrancy_guard& operator= (fsm_reentrancy_guard const&) ETL_DELETE; @@ -272,7 +273,7 @@ namespace etl /// A class to store FSM states. //*************************************************************************** template - class fsm_state_pack + class fsm_state_pack { public: @@ -294,18 +295,18 @@ namespace etl /// Gets a reference to the state. //********************************* template - TState& get() - { - return etl::get(storage); + TState& get() + { + return etl::get(storage); } //********************************* /// Gets a const reference to the state. //********************************* template - const TState& get() const - { - return etl::get(storage); + const TState& get() const + { + return etl::get(storage); } private: @@ -341,14 +342,14 @@ namespace etl using private_fsm::ifsm_state_helper<>::Pass_To_Parent; using private_fsm::ifsm_state_helper<>::Self_Transition; -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above template friend class fsm_state; #else template friend class etl::fsm_state; #endif @@ -560,7 +561,7 @@ namespace etl { etl::fsm_state_id_t next_state_id = p_state->process_event(message); - process_state_change(next_state_id); + process_state_change(next_state_id); } else { @@ -691,7 +692,7 @@ namespace etl p_state->on_exit_state(); next_state_id = p_state->on_enter_state(); } - + if (have_changed_state(next_state_id)) { ETL_ASSERT_OR_RETURN_VALUE(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception), p_state->get_state_id()); @@ -722,17 +723,249 @@ namespace etl }; //************************************************************************************************* - // For C++17 and above. + // For C++11 and above. //************************************************************************************************* -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above //*************************************************************************** // The definition for all types. //*************************************************************************** template class fsm_state : public ifsm_state { + private: + + using message_id_sequence = etl::index_sequence; + public: + using message_types = etl::type_list; + using sorted_message_types = etl::type_list_sort_t; + + static_assert(etl::type_list_is_unique::value, "All TMessageTypes must be unique"); + static_assert(etl::type_list_all_of::value, "All TMessageTypes must satisfy the condition etl::is_message_type"); + static_assert(etl::index_sequence_is_unique::value, "All message IDs must be unique"); + + static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + protected: + + ~fsm_state() + { + } + + TContext& get_fsm_context() const + { + return static_cast(ifsm_state::get_fsm_context()); + } + + private: + + static constexpr size_t Number_Of_Messages = sizeof...(TMessageTypes); + static constexpr etl::message_id_t Message_Id_Start = etl::type_list_type_at_index_t::ID; + + static_assert(Number_Of_Messages > 0, "Zero messages"); + + //********************************************** + // Checks that the message ids are contiguous. + //********************************************** + template = Number_Of_Messages)> + struct contiguous_impl; + + template + struct contiguous_impl : etl::true_type + { + }; + + template + struct contiguous_impl + : etl::bool_constant<(etl::type_list_type_at_index_t::ID + 1U == + etl::type_list_type_at_index_t::ID) && + contiguous_impl::value> + { + + + }; + + // The message ids are contiguous if there are 0 or 1 message types, or if each message id is one greater than the previous message id. + static constexpr bool Message_Ids_Are_Contiguous = (Number_Of_Messages <= 1U) ? true : contiguous_impl<0U>::value; + + using handler_ptr = etl::fsm_state_id_t (*)(TDerived&, const etl::imessage&); ///< Pointer to a handler function that takes a reference to the derived class and a reference to the message. + using message_dispatch_table_t = etl::array; ///< The dispatch table type. An array of handler pointers, one for each message type. + using message_id_table_t = etl::array; ///< The message id table type. An array of message ids, one for each message type. + + //******************************************** + etl::fsm_state_id_t process_event(const etl::imessage& message) + { + const etl::message_id_t id = message.get_message_id(); + + // The IDs are sorted, so an ID less than the first is not handled by this router. + if (id >= Message_Id_Start) + { + const size_t index = get_dispatch_index_from_message_id(id); + + // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. + if (index < Number_Of_Messages) + { + const etl::fsm_state_id_t new_state_id = dispatch(message, index); + + if (new_state_id != Pass_To_Parent) + { + return new_state_id; + } + } + } + +#include "etl/private/diagnostic_array_bounds_push.h" + // If we get here, then we don't have a handler for this message type, so pass it to the parent if we have one, otherwise call on_event_unknown. + return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); +#include "etl/private/diagnostic_pop.h" + } + + //********************************************** + // Call for a single message type + //********************************************** + template + static etl::fsm_state_id_t call_on_event(TDerived& derived, const imessage& msg) + { + return derived.on_event(static_cast(msg)); + } + + //********************************************** + // Get the handler for a single message type at the index in the sorted type_list. + // This will be called for each message type to generate the dispatch table. + //********************************************** + template + static constexpr handler_ptr get_message_handler() + { + return &call_on_event>; + } + + //********************************************** + // Generate the dispatch table at compile time. + // This will create an array of handler pointers, one for each message type. + //********************************************** + template + static constexpr message_dispatch_table_t make_message_dispatch_table(etl::index_sequence) + { + return message_dispatch_table_t{ { get_message_handler()... } }; + } + + //********************************************** + // Get the message id for a single message type at an index in the sorted type_list. + // This will be called for each message type to generate the message id table. + //********************************************** + template + static constexpr etl::message_id_t get_message_id_from_index() + { + return etl::type_list_type_at_index_t::ID; + } + + //********************************************** + // Generate the message id table at compile time. + // This will create an array of message ids, one for each message type. + //********************************************** + template + static constexpr message_id_table_t make_message_id_table(etl::index_sequence) + { + return message_id_table_t{ { get_message_id_from_index()... } }; + } + + //********************************************** + // Get the dispatch index for a message id. + // This will be used at runtime to find the handler for a message id. + // If the message ids are contiguous, we can calculate the index directly. If they are not contiguous, we need to do a binary search. + // This will return Number_Of_Messages if the message id is not found. + //********************************************** + static size_t get_dispatch_index_from_message_id(etl::message_id_t id) + { + if ETL_IF_CONSTEXPR(Message_Ids_Are_Contiguous) + { + // The IDs are contiguous, so we can calculate the index directly. + return static_cast(id - Message_Id_Start); + } + else + { + // The IDs are not contiguous, so we need to do a binary search. + size_t left = 0; + size_t right = Number_Of_Messages; + + while (left < right) + { + size_t mid = (left + right) / 2; + + if (message_id_table[mid] == id) + { + return mid; + } + else if (message_id_table[mid] < id) + { + left = mid + 1; + } + else + { + right = mid; + } + } + } + + return Number_Of_Messages; // Not found + } + + //********************************************** + // Dispatch the message to the appropriate handler based on the index in the dispatch table. + //********************************************** + etl::fsm_state_id_t dispatch(const etl::imessage& msg, size_t index) + { + return message_dispatch_table[index](static_cast(*this), msg); + } + + //********************************************** + // The dispatch table is generated at compile time. The dispatch table contains pointers to the on_receive handlers for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_dispatch_table_t message_dispatch_table = + etl::fsm_state::make_message_dispatch_table(etl::make_index_sequence::Number_Of_Messages>{}); + + //********************************************** + // The message id table is generated at compile time. The message id table contains the corresponding message ids for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_id_table_t message_id_table = + etl::fsm_state::make_message_id_table(etl::make_index_sequence::Number_Of_Messages>{}); + }; + +#if ETL_USING_CPP11 && !ETL_USING_CPP17 + template + constexpr const typename etl::fsm_state::message_dispatch_table_t + etl::fsm_state::message_dispatch_table; + + template + constexpr const typename etl::fsm_state::message_id_table_t + etl::fsm_state::message_id_table; +#endif + + /// Definition of STATE_ID + template + ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID; + + //*************************************************************************** + // The definition for no messages. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + private: + + using message_id_sequence = etl::index_sequence<>; + + public: + + using message_types = etl::type_list<>; + using sorted_message_types = etl::type_list<>; + static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_; fsm_state() @@ -753,59 +986,24 @@ namespace etl private: - //******************************************** - struct result_t - { - bool was_handled; - etl::fsm_state_id_t state_id; - }; - //******************************************** etl::fsm_state_id_t process_event(const etl::imessage& message) { - etl::fsm_state_id_t new_state_id; - - const bool was_handled = (process_event_type(message, new_state_id) || ...); - - if (!was_handled || (new_state_id == Pass_To_Parent)) - { - new_state_id = (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); - } - - return new_state_id; - } - - //******************************************** - template - bool process_event_type(const etl::imessage& msg, etl::fsm_state_id_t& new_state_id) - { - if (TMessage::ID == msg.get_message_id()) - { - new_state_id = static_cast(this)->on_event(static_cast(msg)); - return true; - } - else - { - return false; - } + return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); } }; - /// Definition of STATE_ID - template - ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID; - #else //************************************************************************************************* -// For C++14 and below. +// For C++03 and below. //************************************************************************************************* //*************************************************************************** // The definition for all 16 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -864,10 +1062,10 @@ namespace etl //*************************************************************************** // Specialisation for 15 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -925,10 +1123,10 @@ namespace etl //*************************************************************************** // Specialisation for 14 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -985,10 +1183,10 @@ namespace etl //*************************************************************************** // Specialisation for 13 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1044,9 +1242,9 @@ namespace etl //*************************************************************************** // Specialisation for 12 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1101,9 +1299,9 @@ namespace etl //*************************************************************************** // Specialisation for 11 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1157,9 +1355,9 @@ namespace etl //*************************************************************************** // Specialisation for 10 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1212,9 +1410,9 @@ namespace etl //*************************************************************************** // Specialisation for 9 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1266,8 +1464,8 @@ namespace etl //*************************************************************************** // Specialisation for 8 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1318,8 +1516,8 @@ namespace etl //*************************************************************************** // Specialisation for 7 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1369,8 +1567,8 @@ namespace etl //*************************************************************************** // Specialisation for 6 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1419,8 +1617,8 @@ namespace etl //*************************************************************************** // Specialisation for 5 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1468,7 +1666,7 @@ namespace etl //*************************************************************************** // Specialisation for 4 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1515,7 +1713,7 @@ namespace etl //*************************************************************************** // Specialisation for 3 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1561,7 +1759,7 @@ namespace etl //*************************************************************************** // Specialisation for 2 message types. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1606,7 +1804,7 @@ namespace etl //*************************************************************************** // Specialisation for 1 message type. //*************************************************************************** - template class fsm_state : public ifsm_state { @@ -1680,10 +1878,10 @@ namespace etl } }; - template ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID; #endif diff --git a/include/etl/generators/fsm_generator.h b/include/etl/generators/fsm_generator.h index 0887af6c..98d4c6b0 100644 --- a/include/etl/generators/fsm_generator.h +++ b/include/etl/generators/fsm_generator.h @@ -74,6 +74,7 @@ cog.outl("//******************************************************************** #include "largest.h" #if ETL_USING_CPP11 #include "tuple.h" + #include "type_list.h" #endif #include @@ -95,20 +96,20 @@ namespace etl // For internal FSM use. typedef typename etl::larger_type::type fsm_internal_id_t; -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above template class fsm_state; #else /*[[[cog import cog cog.outl("template ") + cog.out(" ") + cog.outl(" typename>") cog.outl("class fsm_state;") ]]]*/ /*[[[end]]]*/ @@ -214,7 +215,7 @@ namespace etl // Pass this whenever no state change is desired. // The highest unsigned value of fsm_state_id_t. static ETL_CONSTANT fsm_state_id_t No_State_Change = etl::integral_limits::max; - + // Pass this when this event also needs to be passed to the parent. static ETL_CONSTANT fsm_state_id_t Pass_To_Parent = No_State_Change - 1U; @@ -232,15 +233,15 @@ namespace etl ETL_CONSTANT fsm_state_id_t ifsm_state_helper::Self_Transition; // Compile-time: TState::ID must equal its index in the type list (0..N-1) - template struct check_ids : etl::true_type + template struct check_ids : etl::true_type { }; template struct check_ids - : etl::integral_constant::value> + : etl::integral_constant::value> { - }; + }; //*************************************************************************** /// RAII detection mechanism to catch reentrant calls to methods that might @@ -269,11 +270,11 @@ namespace etl { is_locked = false; } - + private: // Reference to the flag signifying a lock on the state machine. bool& is_locked; - + // Copy & move semantics disabled since this is a guard. fsm_reentrancy_guard(fsm_reentrancy_guard const&) ETL_DELETE; fsm_reentrancy_guard& operator= (fsm_reentrancy_guard const&) ETL_DELETE; @@ -291,7 +292,7 @@ namespace etl /// A class to store FSM states. //*************************************************************************** template - class fsm_state_pack + class fsm_state_pack { public: @@ -313,18 +314,18 @@ namespace etl /// Gets a reference to the state. //********************************* template - TState& get() - { - return etl::get(storage); + TState& get() + { + return etl::get(storage); } //********************************* /// Gets a const reference to the state. //********************************* template - const TState& get() const - { - return etl::get(storage); + const TState& get() const + { + return etl::get(storage); } private: @@ -360,20 +361,20 @@ namespace etl using private_fsm::ifsm_state_helper<>::Pass_To_Parent; using private_fsm::ifsm_state_helper<>::Self_Transition; -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above template friend class fsm_state; #else /*[[[cog import cog cog.outl(" template ") + cog.out(" ") + cog.outl(" typename>") ]]]*/ /*[[[end]]]*/ friend class etl::fsm_state; @@ -586,7 +587,7 @@ namespace etl { etl::fsm_state_id_t next_state_id = p_state->process_event(message); - process_state_change(next_state_id); + process_state_change(next_state_id); } else { @@ -717,7 +718,7 @@ namespace etl p_state->on_exit_state(); next_state_id = p_state->on_enter_state(); } - + if (have_changed_state(next_state_id)) { ETL_ASSERT_OR_RETURN_VALUE(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception), p_state->get_state_id()); @@ -748,17 +749,249 @@ namespace etl }; //************************************************************************************************* - // For C++17 and above. + // For C++11 and above. //************************************************************************************************* -#if ETL_USING_CPP17 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++17 and above +#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above //*************************************************************************** // The definition for all types. //*************************************************************************** template class fsm_state : public ifsm_state { + private: + + using message_id_sequence = etl::index_sequence; + public: + using message_types = etl::type_list; + using sorted_message_types = etl::type_list_sort_t; + + static_assert(etl::type_list_is_unique::value, "All TMessageTypes must be unique"); + static_assert(etl::type_list_all_of::value, "All TMessageTypes must satisfy the condition etl::is_message_type"); + static_assert(etl::index_sequence_is_unique::value, "All message IDs must be unique"); + + static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + protected: + + ~fsm_state() + { + } + + TContext& get_fsm_context() const + { + return static_cast(ifsm_state::get_fsm_context()); + } + + private: + + static constexpr size_t Number_Of_Messages = sizeof...(TMessageTypes); + static constexpr etl::message_id_t Message_Id_Start = etl::type_list_type_at_index_t::ID; + + static_assert(Number_Of_Messages > 0, "Zero messages"); + + //********************************************** + // Checks that the message ids are contiguous. + //********************************************** + template = Number_Of_Messages)> + struct contiguous_impl; + + template + struct contiguous_impl : etl::true_type + { + }; + + template + struct contiguous_impl + : etl::bool_constant<(etl::type_list_type_at_index_t::ID + 1U == + etl::type_list_type_at_index_t::ID) && + contiguous_impl::value> + { + + + }; + + // The message ids are contiguous if there are 0 or 1 message types, or if each message id is one greater than the previous message id. + static constexpr bool Message_Ids_Are_Contiguous = (Number_Of_Messages <= 1U) ? true : contiguous_impl<0U>::value; + + using handler_ptr = etl::fsm_state_id_t (*)(TDerived&, const etl::imessage&); ///< Pointer to a handler function that takes a reference to the derived class and a reference to the message. + using message_dispatch_table_t = etl::array; ///< The dispatch table type. An array of handler pointers, one for each message type. + using message_id_table_t = etl::array; ///< The message id table type. An array of message ids, one for each message type. + + //******************************************** + etl::fsm_state_id_t process_event(const etl::imessage& message) + { + const etl::message_id_t id = message.get_message_id(); + + // The IDs are sorted, so an ID less than the first is not handled by this router. + if (id >= Message_Id_Start) + { + const size_t index = get_dispatch_index_from_message_id(id); + + // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. + if (index < Number_Of_Messages) + { + const etl::fsm_state_id_t new_state_id = dispatch(message, index); + + if (new_state_id != Pass_To_Parent) + { + return new_state_id; + } + } + } + +#include "etl/private/diagnostic_array_bounds_push.h" + // If we get here, then we don't have a handler for this message type, so pass it to the parent if we have one, otherwise call on_event_unknown. + return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); +#include "etl/private/diagnostic_pop.h" + } + + //********************************************** + // Call for a single message type + //********************************************** + template + static etl::fsm_state_id_t call_on_event(TDerived& derived, const imessage& msg) + { + return derived.on_event(static_cast(msg)); + } + + //********************************************** + // Get the handler for a single message type at the index in the sorted type_list. + // This will be called for each message type to generate the dispatch table. + //********************************************** + template + static constexpr handler_ptr get_message_handler() + { + return &call_on_event>; + } + + //********************************************** + // Generate the dispatch table at compile time. + // This will create an array of handler pointers, one for each message type. + //********************************************** + template + static constexpr message_dispatch_table_t make_message_dispatch_table(etl::index_sequence) + { + return message_dispatch_table_t{ { get_message_handler()... } }; + } + + //********************************************** + // Get the message id for a single message type at an index in the sorted type_list. + // This will be called for each message type to generate the message id table. + //********************************************** + template + static constexpr etl::message_id_t get_message_id_from_index() + { + return etl::type_list_type_at_index_t::ID; + } + + //********************************************** + // Generate the message id table at compile time. + // This will create an array of message ids, one for each message type. + //********************************************** + template + static constexpr message_id_table_t make_message_id_table(etl::index_sequence) + { + return message_id_table_t{ { get_message_id_from_index()... } }; + } + + //********************************************** + // Get the dispatch index for a message id. + // This will be used at runtime to find the handler for a message id. + // If the message ids are contiguous, we can calculate the index directly. If they are not contiguous, we need to do a binary search. + // This will return Number_Of_Messages if the message id is not found. + //********************************************** + static size_t get_dispatch_index_from_message_id(etl::message_id_t id) + { + if ETL_IF_CONSTEXPR(Message_Ids_Are_Contiguous) + { + // The IDs are contiguous, so we can calculate the index directly. + return static_cast(id - Message_Id_Start); + } + else + { + // The IDs are not contiguous, so we need to do a binary search. + size_t left = 0; + size_t right = Number_Of_Messages; + + while (left < right) + { + size_t mid = (left + right) / 2; + + if (message_id_table[mid] == id) + { + return mid; + } + else if (message_id_table[mid] < id) + { + left = mid + 1; + } + else + { + right = mid; + } + } + } + + return Number_Of_Messages; // Not found + } + + //********************************************** + // Dispatch the message to the appropriate handler based on the index in the dispatch table. + //********************************************** + etl::fsm_state_id_t dispatch(const etl::imessage& msg, size_t index) + { + return message_dispatch_table[index](static_cast(*this), msg); + } + + //********************************************** + // The dispatch table is generated at compile time. The dispatch table contains pointers to the on_receive handlers for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_dispatch_table_t message_dispatch_table = + etl::fsm_state::make_message_dispatch_table(etl::make_index_sequence::Number_Of_Messages>{}); + + //********************************************** + // The message id table is generated at compile time. The message id table contains the corresponding message ids for each message type. + //********************************************** + static ETL_INLINE_VAR constexpr message_id_table_t message_id_table = + etl::fsm_state::make_message_id_table(etl::make_index_sequence::Number_Of_Messages>{}); + }; + +#if ETL_USING_CPP11 && !ETL_USING_CPP17 + template + constexpr const typename etl::fsm_state::message_dispatch_table_t + etl::fsm_state::message_dispatch_table; + + template + constexpr const typename etl::fsm_state::message_id_table_t + etl::fsm_state::message_id_table; +#endif + + /// Definition of STATE_ID + template + ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID; + + //*************************************************************************** + // The definition for no messages. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + private: + + using message_id_sequence = etl::index_sequence<>; + + public: + + using message_types = etl::type_list<>; + using sorted_message_types = etl::type_list<>; + static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_; fsm_state() @@ -779,51 +1012,16 @@ namespace etl private: - //******************************************** - struct result_t - { - bool was_handled; - etl::fsm_state_id_t state_id; - }; - //******************************************** etl::fsm_state_id_t process_event(const etl::imessage& message) { - etl::fsm_state_id_t new_state_id; - - const bool was_handled = (process_event_type(message, new_state_id) || ...); - - if (!was_handled || (new_state_id == Pass_To_Parent)) - { - new_state_id = (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); - } - - return new_state_id; - } - - //******************************************** - template - bool process_event_type(const etl::imessage& msg, etl::fsm_state_id_t& new_state_id) - { - if (TMessage::ID == msg.get_message_id()) - { - new_state_id = static_cast(this)->on_event(static_cast(msg)); - return true; - } - else - { - return false; - } + return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); } }; - /// Definition of STATE_ID - template - ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID; - #else //************************************************************************************************* -// For C++14 and below. +// For C++03 and below. //************************************************************************************************* /*[[[cog import cog @@ -833,14 +1031,14 @@ namespace etl cog.outl("//***************************************************************************") cog.outl("// The definition for all %s message types." % Handlers) cog.outl("//***************************************************************************") - cog.outl("template " % Handlers) + cog.out(" ") + cog.outl(" typename T%s = void>" % Handlers) cog.outl("class fsm_state : public ifsm_state") cog.outl("{") cog.outl("public:") @@ -896,26 +1094,26 @@ namespace etl else: cog.outl("// Specialisation for %d message types." % n) cog.outl("//***************************************************************************") - cog.outl("template " % n) - cog.out("class fsm_state" % n) + cog.out("class fsm_state : public ifsm_state") + cog.outl(" void> : public ifsm_state") cog.outl("{") cog.outl("public:") cog.outl("") @@ -966,13 +1164,13 @@ namespace etl cog.outl("// Specialisation for 0 message types.") cog.outl("//***************************************************************************") cog.outl("template ") - cog.out("class fsm_state : public ifsm_state") + cog.outl(" void> : public ifsm_state") cog.outl("{") cog.outl("public:") cog.outl("") @@ -1002,18 +1200,18 @@ namespace etl cog.outl("};") cog.outl("") - cog.outl("template " % Handlers) - cog.out("ETL_CONSTANT etl::fsm_state_id_t fsm_state" % Handlers) + cog.out("ETL_CONSTANT etl::fsm_state_id_t fsm_state::STATE_ID;" % Handlers) + cog.out(" T%s," % n) + cog.outl(" T%s>::STATE_ID;" % Handlers) ]]]*/ /*[[[end]]]*/ #endif diff --git a/include/etl/generators/message_router_generator.h b/include/etl/generators/message_router_generator.h index eeec698f..312848b6 100644 --- a/include/etl/generators/message_router_generator.h +++ b/include/etl/generators/message_router_generator.h @@ -525,33 +525,31 @@ namespace etl //*********************************************** void receive(const etl::imessage& msg) ETL_OVERRIDE { - etl::message_id_t id = msg.get_message_id(); - size_t index = Number_Of_Messages; + const etl::message_id_t id = msg.get_message_id(); // The IDs are sorted, so an ID less than the first is not handled by this router. if (id >= Message_Id_Start) { - index = get_dispatch_index_from_message_id(id); + const size_t index = get_dispatch_index_from_message_id(id); + + // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. + if (index < Number_Of_Messages) + { + dispatch(msg, index); + return; + } } - // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. - if (index < Number_Of_Messages) + // We don't have a handler for this message type, so pass it to a successor if there is one, or call on_receive_unknown() if there isn't. + if (has_successor()) { - dispatch(msg, index); + get_successor().receive(msg); } else { - // We don't have a handler for this message type, so pass it to a successor if there is one, or call on_receive_unknown() if there isn't. - if (has_successor()) - { - get_successor().receive(msg); - } - else - { #include "etl/private/diagnostic_array_bounds_push.h" - static_cast(this)->on_receive_unknown(msg); + static_cast(this)->on_receive_unknown(msg); #include "etl/private/diagnostic_pop.h" - } } } @@ -601,29 +599,17 @@ namespace etl //*********************************************** bool accepts(etl::message_id_t id) const ETL_OVERRIDE { - size_t index = Number_Of_Messages; - - // The IDs are sorted, so an ID less than the first is not handled by this router. if (id >= Message_Id_Start) { - index = get_dispatch_index_from_message_id(id); + const size_t index = get_dispatch_index_from_message_id(id); + + if (index < Number_Of_Messages) + { + return true; + } } - if (index < Number_Of_Messages) - { - return true; - } - else - { - if (has_successor()) - { - return get_successor().accepts(id); - } - else - { - return false; - } - } + return has_successor() ? get_successor().accepts(id) : false; } //******************************************** diff --git a/include/etl/message_router.h b/include/etl/message_router.h index e9b79d30..7e510466 100644 --- a/include/etl/message_router.h +++ b/include/etl/message_router.h @@ -513,33 +513,31 @@ namespace etl //*********************************************** void receive(const etl::imessage& msg) ETL_OVERRIDE { - etl::message_id_t id = msg.get_message_id(); - size_t index = Number_Of_Messages; + const etl::message_id_t id = msg.get_message_id(); // The IDs are sorted, so an ID less than the first is not handled by this router. if (id >= Message_Id_Start) { - index = get_dispatch_index_from_message_id(id); + const size_t index = get_dispatch_index_from_message_id(id); + + // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. + if (index < Number_Of_Messages) + { + dispatch(msg, index); + return; + } } - // If the index is less than Number_Of_Messages, then we have a handler for this message type, so dispatch it. - if (index < Number_Of_Messages) + // We don't have a handler for this message type, so pass it to a successor if there is one, or call on_receive_unknown() if there isn't. + if (has_successor()) { - dispatch(msg, index); + get_successor().receive(msg); } else { - // We don't have a handler for this message type, so pass it to a successor if there is one, or call on_receive_unknown() if there isn't. - if (has_successor()) - { - get_successor().receive(msg); - } - else - { #include "etl/private/diagnostic_array_bounds_push.h" - static_cast(this)->on_receive_unknown(msg); + static_cast(this)->on_receive_unknown(msg); #include "etl/private/diagnostic_pop.h" - } } } @@ -589,29 +587,17 @@ namespace etl //*********************************************** bool accepts(etl::message_id_t id) const ETL_OVERRIDE { - size_t index = Number_Of_Messages; - - // The IDs are sorted, so an ID less than the first is not handled by this router. if (id >= Message_Id_Start) { - index = get_dispatch_index_from_message_id(id); + const size_t index = get_dispatch_index_from_message_id(id); + + if (index < Number_Of_Messages) + { + return true; + } } - if (index < Number_Of_Messages) - { - return true; - } - else - { - if (has_successor()) - { - return get_successor().accepts(id); - } - else - { - return false; - } - } + return has_successor() ? get_successor().accepts(id) : false; } //******************************************** diff --git a/test/test_fsm.cpp b/test/test_fsm.cpp index c6a34e8e..61030b1b 100644 --- a/test/test_fsm.cpp +++ b/test/test_fsm.cpp @@ -515,7 +515,7 @@ namespace //************************************************************************* TEST(test_fsm_emergency_stop) { - motorControl.Initialise(stateList, ETL_OR_STD17::size(stateList)); + motorControl.Initialise(stateList, ETL_OR_STD17::size(stateList)); motorControl.reset(); motorControl.ClearStatistics(); @@ -758,7 +758,7 @@ namespace CHECK_EQUAL(StateId::Running, int(motorControl.get_state().get_state_id())); auto id2 = motorControl.transition_to(StateId::Idle); - + // Now in Locked state. CHECK_EQUAL(StateId::Locked, int(id2)); CHECK_EQUAL(StateId::Locked, int(motorControl.get_state_id())); From 8a61985ac847aa530fa49d9c9b5e79211dbe78c1 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 12 Mar 2026 17:08:02 +0000 Subject: [PATCH 12/31] span is constructible from temporary (#1338) * Added missing files from VS2022 project * Added global data() overloads to complement etl::size() * Added C++03 compatible implementation of etl::is_convertible * Updated etl::span to more closely align with std::span * Fix etl::rotate (#1327) Per the C++ standard, std::rotate returns first + (last - middle): * When first == middle, return last * When middle == last, return first * Fix greater_equal and less_equal (#1331) * Align comparison operators (#1330) In functional.h, the comparison operators for equal_to and not_equal_to mismatch between the actual comparison execution and the type inference for the return type. This change adjusts it by using the same operator==() in the return type inference as used in the comparison execution. Co-authored-by: John Wellbelove * Add missing tests (#1321) * Add missing tests * Typo fixes --------- Co-authored-by: John Wellbelove * Add ETL_FORMAT_NO_FLOATING_POINT control macro for etl::format (#1329) When ETL_FORMAT_NO_FLOATING_POINT is defined, all floating-point formatting support (float, double, long double) is excluded from etl::format. This reduces code size on targets that do not require floating-point formatting. Guarded sections: - #include - float/double/long double in supported_format_types variant - float/double/long double constructors in basic_format_arg - format_floating_* functions and format_aligned_floating - formatter, formatter, formatter - Floating-point test cases in test_format.cpp Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus Co-authored-by: John Wellbelove # Conflicts: # include/etl/platform.h * Manchester documentation (#1325) * manchester * Added manchester code and test * manchester * Formatting and added missing file * manchester * Some functions can only be constexpr since C++14 * manchester * Manchester decode and some refactoring * manchester * Added some missing typenames * manchester * constexpr void function not allowed in C++11 * manchester * condition on static_assert tests * manchester * revert CMakeLists.txt * Using ETL_STATIC_ASSERT * Some cleanup * manchester * Added static_assert message * manchester * Added compile time tests * manchester * Added invert manchester * Some refactoring * manchester * Disable test for now * Move ETL_NODISCARD before static * manchester * Test for valid_span * manchester * Remove redundant (?) storage specifiers for template specializations. Storage specifier already given in base template * manchester * refactoring to get rid of specialized template functions in template class * manchester * cleanup * manchester * Added documentation comments * Some refactoring * manchester * introducing namespace detail_manchester * manchester * Some refactoring * Update tests * manchester * Some refactoring * Removed possible undefined behavior by refactoring encode_span * constexpr version of encode_span * Static assertion for rare case where code doesn't work because CHAR_BIT is not the same as the number of bits in uint_least8_t * manchester * renamed valid to is_valid * manchester * renamed is_valid_span to is_valid * Using etl exceptions in ETL_ASSERT * manchester * Removed _fast functions * merged encode_in_place with encode and decode_in_place with decode * removed _span to create normal overloads of encode and decode for span * Some renaming and minor refactoring * manchester * Fix build issues * manchester * Conditionally compile manchester_decoded * Update test_manchester.cpp Removed redundant semicolon * #1258 Manchester coding * Formatting * consistency: hex literals with lower case 0x * #1258 Manchester coding * Moved copyright to top of file * Make constexpr encode/decode span functions equal for little and big endian platforms * #1258 Manchester coding * Added missing include * Added missing 8bit/64bit guards * Fixed is_valid for big endian platforms * #1258 Manchester coding * private memcpy alias * #1258 Manchester coding * Review comments * #1258 Manchester coding * Cleanup * Fix build error * #1258 Manchester coding * Add manchester documentation * #1258 Manchester coding * Preparation for GitHub pages * #1324 Manchester documentation * Some small fixes --------- Co-authored-by: Timon Zijnge * Moved and split has_size_and_data in span.h to has_size & has_data in type_traits.h * Removed has_size_and_data traits, and move to type_traits.h Added ETL_ASSERT for for fixed extent constructors from iterator range and begin/size * Added macro ETL_NOEXCEPT_IF that takes a compile time boolean expression * Changed two fixed span constructors to ETL_CONSTEXPR14 due to ETL_ASSERT in the constructor bodies Added ETL_NOEXCEPT_IF for simpler boolean conditions Added tests for construction from mismatched sizes * Added definition for ETL_NOEXCEPT_IF in no C++11 path * Changes to disable construction from rvalue temporaries --------- Co-authored-by: John Wellbelove Co-authored-by: Roland Reichwein Co-authored-by: Niu Zhihong Co-authored-by: Sisyphus Co-authored-by: Timon Zijnge <47081647+tzijnge@users.noreply.github.com> Co-authored-by: Timon Zijnge --- include/etl/array.h | 2 +- .../etl/generators/type_traits_generator.h | 74 +++- include/etl/iterator.h | 52 ++- include/etl/platform.h | 13 +- include/etl/span.h | 385 +++++++++++++----- include/etl/type_traits.h | 154 ++++++- test/test_container.cpp | 29 ++ test/test_span_dynamic_extent.cpp | 18 + test/test_span_fixed_extent.cpp | 36 +- 9 files changed, 619 insertions(+), 144 deletions(-) diff --git a/include/etl/array.h b/include/etl/array.h index 1f2f5044..da4fcd04 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -1105,7 +1105,7 @@ namespace etl #if ETL_USING_CPP17 template array(T...) -> array::type, sizeof...(T)>; -#endif +#endif //************************************************************************* /// Make diff --git a/include/etl/generators/type_traits_generator.h b/include/etl/generators/type_traits_generator.h index 1588544f..42189f43 100644 --- a/include/etl/generators/type_traits_generator.h +++ b/include/etl/generators/type_traits_generator.h @@ -765,11 +765,11 @@ namespace etl ///\ingroup type_traits /// Implemented by checking if type is convertible to an integer through static_cast - namespace private_type_traits + namespace private_type_traits { // Base case template - struct is_convertible_to_int + struct is_convertible_to_int : false_type { }; @@ -778,7 +778,7 @@ namespace etl // 2nd template argument of base case defaults to int to ensure that this partial specialization is always tried first template struct is_convertible_to_int(declval()))> - : true_type + : true_type { }; } @@ -788,7 +788,7 @@ namespace etl : integral_constant::value && !is_class::value && !is_arithmetic::value && - !is_reference::value> + !is_reference::value> { }; @@ -1647,7 +1647,7 @@ typedef integral_constant true_type; //*************************************************************************** /// Get the Nth base of a recursively inherited type. /// Requires that the class has defined 'base_type'. - //*************************************************************************** + //*************************************************************************** // Recursive definition of the type. template struct nth_base @@ -2192,7 +2192,7 @@ typedef integral_constant true_type; #if ETL_USING_CPP11 //*************************************************************************** /// is_constructible - namespace private_type_traits + namespace private_type_traits { template struct is_constructible_ : etl::false_type {}; @@ -2371,7 +2371,7 @@ typedef integral_constant true_type; }; template - struct common_type_2_impl + struct common_type_2_impl : decay_conditional_result { }; @@ -2716,7 +2716,7 @@ typedef integral_constant true_type; struct is_member_pointer_helper : etl::true_type {}; } - template + template struct is_member_pointer : private_type_traits::is_member_pointer_helper::type> {}; #if ETL_USING_CPP17 @@ -2809,10 +2809,10 @@ typedef integral_constant true_type; //*************************************************************************** namespace private_type_traits { - template + template struct is_member_object_pointer_helper : public etl::false_type {}; - template + template struct is_member_object_pointer_helper : public etl::negation> {}; } @@ -3034,6 +3034,60 @@ typedef integral_constant true_type; }; #endif + +#if ETL_USING_CPP11 + template + struct has_size : etl::false_type {}; + + template + struct has_size().size())> > + : etl::true_type {}; +#else + template + struct has_size + { + private: + typedef char yes; + struct no { char dummy[2]; }; + + template + static yes test_size(char (*)[sizeof(&U::size)]); + + template + static no test_size(...); + + public: + + static const bool value = (sizeof(test_size(0)) == sizeof(yes)); + }; +#endif + +#if ETL_USING_CPP11 + template + struct has_data : etl::false_type {}; + + template + struct has_data().data())> > + : etl::true_type {}; +#else + template + struct has_data + { + private: + typedef char yes; + struct no { char dummy[2]; }; + + template + static yes test_data(char (*)[sizeof(&U::data)]); + + template + static no test_data(...); + + public: + + static const bool value = (sizeof(test_data(0)) == sizeof(yes)); + }; +#endif } // Helper macros diff --git a/include/etl/iterator.h b/include/etl/iterator.h index 80535132..d3b47d20 100644 --- a/include/etl/iterator.h +++ b/include/etl/iterator.h @@ -67,7 +67,7 @@ namespace etl typedef typename TIterator::pointer pointer; typedef typename TIterator::reference reference; }; - + // For pointers. template struct iterator_traits @@ -606,7 +606,7 @@ namespace etl ETL_CONSTEXPR14 back_insert_iterator& operator =(const typename TContainer::value_type& value) { container->push_back(value); - + return (*this); } @@ -617,7 +617,7 @@ namespace etl ETL_CONSTEXPR14 back_insert_iterator& operator =(typename TContainer::value_type&& value) { container->push_back(etl::move(value)); - + return (*this); } #endif // ETL_USING_CPP11 @@ -655,8 +655,8 @@ namespace etl /// Creates a back_insert_iterator from a container. //*************************************************************************** template - ETL_NODISCARD - ETL_CONSTEXPR14 + ETL_NODISCARD + ETL_CONSTEXPR14 etl::back_insert_iterator back_inserter(TContainer& container) { return etl::back_insert_iterator(container); @@ -1211,6 +1211,48 @@ namespace etl char(&array_size(T(&array)[Array_Size]))[Array_Size]; #define ETL_ARRAY_SIZE(a) sizeof(etl::array_size(a)) + +#if ETL_NOT_USING_STL || ETL_CPP17_NOT_SUPPORTED + //************************************************************************** + /// Returns a pointer to the block of memory containing the elements of the range. + ///\ingroup container + //************************************************************************** + template + ETL_CONSTEXPR typename TContainer::pointer data(TContainer& container) + { + return container.data(); + } + + //************************************************************************** + /// Returns a const_pointer to the block of memory containing the elements of the range. + ///\ingroup container + //************************************************************************** + template + ETL_CONSTEXPR typename TContainer::const_pointer data(const TContainer& container) + { + return container.data(); + } + + ///************************************************************************** + /// Returns a pointer to the block of memory containing the elements of the range. + ///\ingroup container + ///************************************************************************** + template + ETL_CONSTEXPR TValue* data(TValue(&a)[Array_Size]) + { + return a; + } + + ///************************************************************************** + /// Returns a const pointer to the block of memory containing the elements of the range. + ///\ingroup container + ///************************************************************************** + template + ETL_CONSTEXPR const TValue* data(const TValue(&a)[Array_Size]) + { + return a; + } +#endif } #endif diff --git a/include/etl/platform.h b/include/etl/platform.h index a1ae4029..c519ff08 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -382,11 +382,13 @@ SOFTWARE. #if ETL_USING_EXCEPTIONS #define ETL_NOEXCEPT noexcept #define ETL_NOEXCEPT_EXPR(...) noexcept(__VA_ARGS__) + #define ETL_NOEXCEPT_IF(b) noexcept((b)) #define ETL_NOEXCEPT_FROM(x) noexcept(noexcept(x)) #else #define ETL_NOEXCEPT #define ETL_NOEXCEPT_EXPR(...) - #define ETL_NOEXCEPT_FROM(x) + #define ETL_NOEXCEPT_IF(b) + #define ETL_NOEXCEPT_FROM(x) #endif #else #define ETL_CONSTEXPR @@ -399,7 +401,8 @@ SOFTWARE. #define ETL_NORETURN #define ETL_NOEXCEPT #define ETL_NOEXCEPT_EXPR(...) - #define ETL_NOEXCEPT_FROM(x) + #define ETL_NOEXCEPT_IF(b) + #define ETL_NOEXCEPT_FROM(x) #define ETL_MOVE(x) x #define ETL_ENUM_CLASS(name) enum name #define ETL_ENUM_CLASS_TYPE(name, type) enum name @@ -412,7 +415,7 @@ SOFTWARE. #if ETL_USING_CPP14 #define ETL_CONSTEXPR14 constexpr - #if !defined(ETL_IN_UNIT_TEST) + #if !defined(ETL_IN_UNIT_TEST) #define ETL_DEPRECATED [[deprecated]] #define ETL_DEPRECATED_REASON(reason) [[deprecated(reason)]] #else @@ -611,7 +614,7 @@ SOFTWARE. #elif defined(ETL_COMPILER_MICROSOFT) #define ETL_PACKED_CLASS(class_type) __pragma(pack(push, 1)) class class_type #define ETL_PACKED_STRUCT(struct_type) __pragma(pack(push, 1)) struct struct_type - #define ETL_PACKED + #define ETL_PACKED #define ETL_END_PACKED __pragma(pack(pop)) #define ETL_HAS_PACKED 1 #else @@ -662,7 +665,7 @@ namespace etl static ETL_CONSTANT bool using_libc_wchar_h = (ETL_USING_LIBC_WCHAR_H == 1); static ETL_CONSTANT bool using_std_exception = (ETL_USING_STD_EXCEPTION == 1); static ETL_CONSTANT bool using_format_floating_point = (ETL_USING_FORMAT_FLOATING_POINT == 1); - + // Has... static ETL_CONSTANT bool has_initializer_list = (ETL_HAS_INITIALIZER_LIST == 1); static ETL_CONSTANT bool has_8bit_types = (ETL_USING_8BIT_TYPES == 1); diff --git a/include/etl/span.h b/include/etl/span.h index c60a9125..c053d37b 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -47,6 +47,12 @@ SOFTWARE. #include "array.h" #include "byte.h" #include "static_assert.h" +#include "container.h" +#include "private/tuple_size.h" + +#if ETL_USING_STL && ETL_USING_CPP20 + #include +#endif #include "private/dynamic_extent.h" @@ -72,7 +78,28 @@ namespace etl //*************************************************************************** // Tag to indicate a class is a span. //*************************************************************************** - class span_tag {}; + + // Forward declaration for trait + template + class span; + + namespace private_span + { + template + struct is_span_helper : etl::false_type {}; + + template + struct is_span_helper > : etl::true_type {}; + } + + template + struct is_span + : private_span::is_span_helper::type> {}; + +#if ETL_USING_CPP17 + template + inline constexpr bool is_span_v = is_span::value; +#endif //*************************************************************************** ///\ingroup span @@ -130,6 +157,12 @@ namespace etl } }; + //*************************************************************************** + ///\ingroup span + /// Tag to indicate a class is a span. + /// Deprecated, use is_span trait instead. + class span_tag {}; + //*************************************************************************** /// Span - Fixed Extent //*************************************************************************** @@ -141,13 +174,13 @@ namespace etl typedef T element_type; typedef typename etl::remove_cv::type value_type; typedef size_t size_type; - typedef T& reference; - typedef const T& const_reference; - typedef T* pointer; - typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T* pointer; + typedef const T* const_pointer; - typedef T* iterator; - typedef const T* const_iterator; + typedef T* iterator; + typedef const T* const_iterator; typedef ETL_OR_STD::reverse_iterator reverse_iterator; typedef ETL_OR_STD::reverse_iterator const_reverse_iterator; @@ -157,72 +190,92 @@ namespace etl static ETL_CONSTANT size_t extent = Extent; //************************************************************************* - /// Construct from iterators + size + /// Default constructor + /// Enabled only for zero extent, creates an empty span. //************************************************************************* - template - ETL_CONSTEXPR explicit span(const TIterator begin_, const TSize /*size_*/) ETL_NOEXCEPT +#if ETL_USING_CPP11 + template ::type> + ETL_CONSTEXPR span() ETL_NOEXCEPT + : pbegin(ETL_NULLPTR) + { + } +#else + ETL_CONSTEXPR span() ETL_NOEXCEPT + : pbegin(ETL_NULLPTR) + { + ETL_STATIC_ASSERT(Extent == 0, "Default constructor only available for zero extent"); + } +#endif + + //************************************************************************* + /// Construct from iterator + size + //************************************************************************* + template + explicit ETL_CONSTEXPR14 span(const TIterator begin_, const size_t size_) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) : pbegin(etl::to_address(begin_)) { + ETL_ASSERT(size_ == Extent, ETL_ERROR(span_size_mismatch)); + (void)size_; } //************************************************************************* /// Construct from iterators //************************************************************************* - template - ETL_CONSTEXPR explicit span(const TIterator begin_, const TIterator /*end_*/) ETL_NOEXCEPT + template + ETL_CONSTEXPR14 span(const TIteratorBegin begin_, const TIteratorEnd end_, + typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) : pbegin(etl::to_address(begin_)) { + ETL_ASSERT(etl::distance(begin_, end_) == Extent, ETL_ERROR(span_size_mismatch)); + (void)end_; } //************************************************************************* /// Construct from C array //************************************************************************* -#if ETL_USING_CPP11 - template::type> - ETL_CONSTEXPR span(element_type(&begin_)[Array_Size]) ETL_NOEXCEPT - : pbegin(begin_) - { - } -#else - //************************************************************************* - /// Construct from C array - //************************************************************************* template - ETL_CONSTEXPR span(element_type(&begin_)[Array_Size], typename etl::enable_if<(Array_Size == Extent), void>::type* = 0) ETL_NOEXCEPT + ETL_CONSTEXPR span(typename etl::type_identity::type(&begin_)[Array_Size], typename etl::enable_if<(Array_Size == Extent), void>::type* = 0) ETL_NOEXCEPT : pbegin(begin_) { } -#endif #if ETL_USING_CPP11 //************************************************************************* /// Construct from a container or other type that supports /// data() and size() member functions. //************************************************************************* - template >::value && - !etl::is_std_array>::value && - !etl::is_etl_array>::value && - !etl::is_pointer>::value && - !etl::is_array>::value && - etl::is_same, etl::remove_cv_t::value_type>>::value, void>::type> - ETL_CONSTEXPR span(TContainer&& a) ETL_NOEXCEPT - : pbegin(a.data()) - { - } + template + ETL_CONSTEXPR14 span(TContainer&& a, typename etl::enable_if::value && + !etl::is_std_array::type>::value && + !etl::is_etl_array::type>::value && + !etl::is_pointer::type>::value && + !etl::is_array::value && + etl::is_lvalue_reference::value && + has_size::value && + has_data::value && + etl::is_convertible::type&>().data()), pointer>::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS) + : pbegin(a.data()) + { + ETL_ASSERT(a.size() == Extent, ETL_ERROR(span_size_mismatch)); + } #else //************************************************************************* /// Construct from a container or other type that supports /// data() and size() member functions. //************************************************************************* template - span(TContainer& a, typename etl::enable_if::type>::value && + span(TContainer& a, typename etl::enable_if::value && !etl::is_std_array::type>::value && !etl::is_etl_array::type>::value && !etl::is_pointer::type>::value && !etl::is_array::value && - etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT + has_size::value && + has_data::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) : pbegin(a.data()) { + ETL_ASSERT(a.size() == Extent, ETL_ERROR(span_size_mismatch)); } //************************************************************************* @@ -230,17 +283,69 @@ namespace etl /// data() and size() member functions. //************************************************************************* template - span(const TContainer& a, typename etl::enable_if::type>::value && + span(const TContainer& a, typename etl::enable_if::value && !etl::is_std_array::type>::value && !etl::is_etl_array::type>::value && !etl::is_pointer::type>::value && - !etl::is_array::value&& - etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT + has_size::value && + has_data::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) : pbegin(a.data()) { + ETL_ASSERT(a.size() == Extent, ETL_ERROR(span_size_mismatch)); } #endif + //************************************************************************* + /// Constructor from etl array. + //************************************************************************* + template + ETL_CONSTEXPR span(etl::array& other, typename etl::enable_if<(Size == Extent) && + etl::is_convertible::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + { + } + + //************************************************************************* + /// Constructor from const etl array. + //************************************************************************* + template + ETL_CONSTEXPR span(const etl::array& other, typename etl::enable_if<(Size == Extent) && + etl::is_convertible::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + { + } + +#if ETL_USING_CPP11 + template + span(etl::array&&) = delete; +#endif + +#if ETL_USING_STL && ETL_USING_CPP11 + //************************************************************************* + /// Constructor from std array. + //************************************************************************* + template + ETL_CONSTEXPR span(std::array& other, typename etl::enable_if<(Size == Extent) && + etl::is_convertible::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + { + } + + //************************************************************************* + /// Constructor from const std array. + //************************************************************************* + template + ETL_CONSTEXPR span(const std::array& other, typename etl::enable_if<(Size == Extent) && + etl::is_convertible::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + { + } + + template + span(std::array&&) = delete; +#endif + //************************************************************************* /// Copy constructor //************************************************************************* @@ -254,7 +359,7 @@ namespace etl /// From fixed extent span. //************************************************************************* template - ETL_CONSTEXPR span(const etl::span& other, typename etl::enable_if::type* = 0) ETL_NOEXCEPT + ETL_CONSTEXPR span(const etl::span& other, typename etl::enable_if<(Size == Extent) && (Size != etl::dynamic_extent), void>::type* = 0) ETL_NOEXCEPT : pbegin(other.data()) { } @@ -264,50 +369,37 @@ namespace etl /// From dynamic extent span. //************************************************************************* template - ETL_CONSTEXPR14 span(const etl::span& other, typename etl::enable_if::type* = 0) + ETL_CONSTEXPR14 explicit span(const etl::span& other, typename etl::enable_if<(Size == etl::dynamic_extent), void>::type* = 0) : pbegin(other.data()) { ETL_ASSERT(other.size() == Extent, ETL_ERROR(span_size_mismatch)); } -#if ETL_USING_STL && ETL_USING_CPP11 +#if ETL_USING_STL && ETL_USING_CPP20 //************************************************************************* - /// Constructor from std array. + /// Copy constructor + /// From fixed extent std::span. //************************************************************************* template - ETL_CONSTEXPR span(std::array& other, typename etl::enable_if::type* = 0) ETL_NOEXCEPT + ETL_CONSTEXPR span(const std::span& other, typename etl::enable_if<(Size == Extent) && + etl::is_convertible::value, int>::type* = 0) ETL_NOEXCEPT : pbegin(other.data()) { } //************************************************************************* - /// Constructor from const std array. + /// Copy constructor + /// From dynamic extent std::span. //************************************************************************* template - ETL_CONSTEXPR span(const std::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT + ETL_CONSTEXPR14 span(const std::span& other, typename etl::enable_if<(Size == etl::dynamic_extent && + etl::is_convertible::value), int>::type* = 0) ETL_NOEXCEPT : pbegin(other.data()) { + ETL_ASSERT(other.size() == Extent, ETL_ERROR(span_size_mismatch)); } #endif - //************************************************************************* - /// Constructor from etl array. - //************************************************************************* - template - ETL_CONSTEXPR span(etl::array& other, typename etl::enable_if::type* = 0) ETL_NOEXCEPT - : pbegin(other.data()) - { - } - - //************************************************************************* - /// Constructor from const etl array. - //************************************************************************* - template - ETL_CONSTEXPR span(const etl::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT - : pbegin(other.data()) - { - } - //************************************************************************* /// Returns a reference to the first element. //************************************************************************* @@ -455,7 +547,7 @@ namespace etl { pbegin = other.pbegin; return *this; - } + } //************************************************************************* /// Returns a reference to the value at index 'i'. @@ -506,13 +598,13 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the first count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range)); #else ETL_ASSERT_CHECK_EXTRA(count <= size(), ETL_ERROR(span_out_of_range)); - + return etl::span(pbegin, pbegin + count); #endif } @@ -532,11 +624,11 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the last count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA - return count <= size() ? - etl::span((pbegin + Extent) - count, (pbegin + Extent)) : + return count <= size() ? + etl::span((pbegin + Extent) - count, (pbegin + Extent)) : throw(ETL_ERROR(span_out_of_range)); #else ETL_ASSERT_CHECK_EXTRA(count <= size(), ETL_ERROR(span_out_of_range)); @@ -589,7 +681,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view from 'offset' over the next 'count' elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return (offset <= size()) && (count != etl::dynamic_extent ? count <= (size() - offset) : true) ? @@ -636,7 +728,7 @@ namespace etl /// Span - Dynamic Extent //*************************************************************************** template - class span : public span_tag + class span : public span_tag { public: @@ -668,10 +760,10 @@ namespace etl } //************************************************************************* - /// Construct from pointer + size + /// Construct from iterator + size //************************************************************************* - template - ETL_CONSTEXPR span(const TIterator begin_, const TSize size_) ETL_NOEXCEPT + template + ETL_CONSTEXPR span(const TIterator begin_, size_t size_) ETL_NOEXCEPT : pbegin(etl::to_address(begin_)) , pend(etl::to_address(begin_) + size_) { @@ -680,8 +772,9 @@ namespace etl //************************************************************************* /// Construct from iterators //************************************************************************* - template - ETL_CONSTEXPR span(const TIterator begin_, const TIterator end_) ETL_NOEXCEPT + template + ETL_CONSTEXPR span(const TIteratorBegin begin_, const TIteratorEnd end_, + typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT : pbegin(etl::to_address(begin_)) , pend(etl::to_address(begin_) + etl::distance(begin_, end_)) { @@ -702,11 +795,17 @@ namespace etl /// Construct from a container or other type that supports /// data() and size() member functions. //************************************************************************* - template >::value && - !etl::is_pointer>::value && - !etl::is_array>::value && - etl::is_same, etl::remove_cv_t::value_type>>::value, void>::type> - ETL_CONSTEXPR span(TContainer&& a) ETL_NOEXCEPT + template + ETL_CONSTEXPR span(TContainer&& a, typename etl::enable_if::value && + !etl::is_std_array::type>::value && + !etl::is_etl_array::type>::value && + !etl::is_pointer::type>::value && + !etl::is_array::value && + etl::is_lvalue_reference::value && + has_size::value && + has_data::value && + etl::is_convertible::type&>().data()), pointer>::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT : pbegin(a.data()) , pend(a.data() + a.size()) { @@ -717,10 +816,14 @@ namespace etl /// data() and size() member functions. //************************************************************************* template - ETL_CONSTEXPR span(TContainer& a, typename etl::enable_if::type>::value && - !etl::is_pointer::type>::value && - !etl::is_array::value && - etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT + span(TContainer& a, typename etl::enable_if::value && + !etl::is_std_array::type>::value && + !etl::is_etl_array::type>::value && + !etl::is_pointer::type>::value && + !etl::is_array::value && + has_size::value && + has_data::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT : pbegin(a.data()) , pend(a.data() + a.size()) { @@ -731,16 +834,92 @@ namespace etl /// data() and size() member functions. //************************************************************************* template - ETL_CONSTEXPR span(const TContainer& a, typename etl::enable_if::type>::value && - !etl::is_pointer::type>::value && - !etl::is_array::value && - etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT + span(const TContainer& a, typename etl::enable_if::value && + !etl::is_std_array::type>::value && + !etl::is_etl_array::type>::value && + !etl::is_pointer::type>::value && + !etl::is_array::value && + has_size::value && + has_data::value && + etl::is_same::type, typename etl::remove_cv::type::value_type>::type>::value, void>::type* = 0) ETL_NOEXCEPT : pbegin(a.data()) , pend(a.data() + a.size()) { } #endif +#if ETL_USING_STL && ETL_USING_CPP20 + //************************************************************************* + /// Constructor from std span. + //************************************************************************* + template + ETL_CONSTEXPR span(std::span& other, typename etl::enable_if::value, int>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + , pend(other.data() + other.size()) + { + } + + //************************************************************************* + /// Constructor from const std span. + //************************************************************************* + template + ETL_CONSTEXPR span(const std::span& other, typename etl::enable_if::value, int>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()) + , pend(other.data() + other.size()) + { + } +#endif + + //************************************************************************* + /// Constructor from etl array. + //************************************************************************* + template + ETL_CONSTEXPR span(etl::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()), + pend(other.data() + Size) + { + } + + //************************************************************************* + /// Constructor from const etl array. + //************************************************************************* + template + ETL_CONSTEXPR span(const etl::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()), + pend(other.data() + Size) + { + } + +#if ETL_USING_CPP11 + template + span(etl::array&&) = delete; +#endif + +#if ETL_USING_STL && ETL_USING_CPP11 + //************************************************************************* + /// Constructor from std array. + //************************************************************************* + template + ETL_CONSTEXPR span(std::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()), + pend(other.data() + Size) + { + } + + //************************************************************************* + /// Constructor from const std array. + //************************************************************************* + template + ETL_CONSTEXPR span(const std::array& other, typename etl::enable_if::value, void>::type* = 0) ETL_NOEXCEPT + : pbegin(other.data()), + pend(other.data() + Size) + { + } + + template + span(std::array&&) = delete; +#endif + //************************************************************************* /// Copy constructor //************************************************************************* @@ -763,7 +942,7 @@ namespace etl //************************************************************************* /// Returns a reference to the first element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR reference front() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return size() > 0 ? *pbegin : throw(ETL_ERROR(span_out_of_range)); @@ -777,7 +956,7 @@ namespace etl //************************************************************************* /// Returns a reference to the last element. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR reference back() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return size() > 0 ? *(pend - 1) : throw(ETL_ERROR(span_out_of_range)); @@ -956,7 +1135,7 @@ namespace etl /// Obtains a span that is a view over the first COUNT elements of this span. //************************************************************************* template - ETL_NODISCARD ETL_CONSTEXPR etl::span first() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return COUNT <= size() ? etl::span(pbegin, pbegin + COUNT) : throw(ETL_ERROR(span_out_of_range)); @@ -970,7 +1149,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the first count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span first(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pbegin, pbegin + count) : throw(ETL_ERROR(span_out_of_range)); @@ -985,7 +1164,7 @@ namespace etl /// Obtains a span that is a view over the last COUNT elements of this span. //************************************************************************* template - ETL_NODISCARD ETL_CONSTEXPR etl::span last() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return COUNT <= size() ? etl::span(pend - COUNT, pend) : throw(ETL_ERROR(span_out_of_range)); @@ -999,7 +1178,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view over the last count elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR etl::span last(size_t count) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return count <= size() ? etl::span(pend - count, pend) : throw(ETL_ERROR(span_out_of_range)); @@ -1016,7 +1195,7 @@ namespace etl //************************************************************************* template ETL_NODISCARD ETL_CONSTEXPR - etl::span subspan() const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + etl::span subspan() const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { #if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA return (OFFSET <= size()) && (COUNT != etl::dynamic_extent ? COUNT <= (size() - OFFSET) : true) ? @@ -1055,7 +1234,7 @@ namespace etl //************************************************************************* /// Obtains a span that is a view from 'offset' over the next 'count' elements of this span. //************************************************************************* - ETL_NODISCARD ETL_CONSTEXPR14 etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) + ETL_NODISCARD ETL_CONSTEXPR14 etl::span subspan(size_t offset, size_t count = etl::dynamic_extent) const ETL_NOEXCEPT_IF(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_EXTRA) { ETL_ASSERT_CHECK_EXTRA(offset <= size(), ETL_ERROR(span_out_of_range)); ETL_ASSERT_CHECK_EXTRA(count != etl::dynamic_extent ? count <= (size() - offset) : true, ETL_ERROR(span_out_of_range)); @@ -1161,7 +1340,7 @@ namespace etl /// Compare two spans for equality. //************************************************************************* template - ETL_NODISCARD + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::type, typename etl::remove_cv::type>::value, bool>::type operator ==(const etl::span& lhs, const etl::span& rhs) ETL_NOEXCEPT @@ -1255,7 +1434,7 @@ namespace etl template span(etl::ivector&) -> span; - + template span(const etl::ivector&) -> span; @@ -1269,7 +1448,7 @@ namespace etl span(const std::array&) ->span; #endif -#endif +#endif //************************************************************************* /// Hash function. @@ -1290,7 +1469,7 @@ namespace etl /// Obtains a view to the byte representation of the elements of the span s. //************************************************************************* template - span + span as_bytes(span s) ETL_NOEXCEPT { return span(reinterpret_cast(s.data()), s.size_bytes()); @@ -1300,7 +1479,7 @@ namespace etl /// Obtains a view to the byte representation of the elements of the span s. //************************************************************************* template - span + span as_writable_bytes(span s) ETL_NOEXCEPT { ETL_STATIC_ASSERT(!etl::is_const::value, "span must be of non-const type"); diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 524b74eb..521aaf48 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -753,11 +753,11 @@ namespace etl ///\ingroup type_traits /// Implemented by checking if type is convertible to an integer through static_cast - namespace private_type_traits + namespace private_type_traits { // Base case template - struct is_convertible_to_int + struct is_convertible_to_int : false_type { }; @@ -766,7 +766,7 @@ namespace etl // 2nd template argument of base case defaults to int to ensure that this partial specialization is always tried first template struct is_convertible_to_int(declval()))> - : true_type + : true_type { }; } @@ -776,7 +776,7 @@ namespace etl : integral_constant::value && !is_class::value && !is_arithmetic::value && - !is_reference::value> + !is_reference::value> { }; @@ -860,6 +860,42 @@ namespace etl public: static ETL_CONSTANT bool value = decltype(test(0))::value; }; +#else + namespace private_type_traits + { + typedef char yes; + struct no { char dummy[2]; }; + + template + struct is_convertible_impl + { + static yes test(TTo); + static no test(...); + static TFrom make(); + static const bool value = (sizeof(test(make())) == sizeof(yes)); + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template <> + struct is_convertible_impl + { + static const bool value = true; + }; + } + + template + struct is_convertible : etl::bool_constant::value> {}; #endif #if ETL_USING_CPP17 @@ -1408,6 +1444,42 @@ typedef integral_constant true_type; public: static ETL_CONSTANT bool value = decltype(test(0))::value; }; +#else + namespace private_type_traits + { + typedef char yes; + struct no { char dummy[2]; }; + + template + struct is_convertible_impl + { + static yes test(TTo); + static no test(...); + static TFrom make(); + static const bool value = (sizeof(test(make())) == sizeof(yes)); + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template <> + struct is_convertible_impl + { + static const bool value = true; + }; + } + + template + struct is_convertible : etl::bool_constant::value> {}; #endif #if ETL_USING_CPP17 @@ -1542,13 +1614,13 @@ typedef integral_constant true_type; /// Template to determine if a type is one of a specified list. ///\ingroup types template struct is_one_of { - static const bool value = + static const bool value = etl::is_same::value || etl::is_same::value || etl::is_same::value || @@ -1640,7 +1712,7 @@ typedef integral_constant true_type; //*************************************************************************** /// Get the Nth base of a recursively inherited type. /// Requires that the class has defined 'base_type'. - //*************************************************************************** + //*************************************************************************** // Recursive definition of the type. template struct nth_base @@ -2185,7 +2257,7 @@ typedef integral_constant true_type; #if ETL_USING_CPP11 //*************************************************************************** /// is_constructible - namespace private_type_traits + namespace private_type_traits { template struct is_constructible_ : etl::false_type {}; @@ -2364,7 +2436,7 @@ typedef integral_constant true_type; }; template - struct common_type_2_impl + struct common_type_2_impl : decay_conditional_result { }; @@ -2709,7 +2781,7 @@ typedef integral_constant true_type; struct is_member_pointer_helper : etl::true_type {}; } - template + template struct is_member_pointer : private_type_traits::is_member_pointer_helper::type> {}; #if ETL_USING_CPP17 @@ -2802,10 +2874,10 @@ typedef integral_constant true_type; //*************************************************************************** namespace private_type_traits { - template + template struct is_member_object_pointer_helper : public etl::false_type {}; - template + template struct is_member_object_pointer_helper : public etl::negation> {}; } @@ -3027,6 +3099,60 @@ typedef integral_constant true_type; }; #endif + +#if ETL_USING_CPP11 + template + struct has_size : etl::false_type {}; + + template + struct has_size().size())> > + : etl::true_type {}; +#else + template + struct has_size + { + private: + typedef char yes; + struct no { char dummy[2]; }; + + template + static yes test_size(char (*)[sizeof(&U::size)]); + + template + static no test_size(...); + + public: + + static const bool value = (sizeof(test_size(0)) == sizeof(yes)); + }; +#endif + +#if ETL_USING_CPP11 + template + struct has_data : etl::false_type {}; + + template + struct has_data().data())> > + : etl::true_type {}; +#else + template + struct has_data + { + private: + typedef char yes; + struct no { char dummy[2]; }; + + template + static yes test_data(char (*)[sizeof(&U::data)]); + + template + static no test_data(...); + + public: + + static const bool value = (sizeof(test_data(0)) == sizeof(yes)); + }; +#endif } // Helper macros diff --git a/test/test_container.cpp b/test/test_container.cpp index ece1afba..3583be25 100644 --- a/test/test_container.cpp +++ b/test/test_container.cpp @@ -31,6 +31,7 @@ SOFTWARE. #include "etl/container.h" #include +#include #if ETL_NOT_USING_STL @@ -145,6 +146,34 @@ namespace size_t compiletime_size = sizeof(etl::array_size(data)); CHECK_EQUAL(SIZE, compiletime_size); } + + //************************************************************************* + TEST(test_stl_style_container_data) + { + const size_t SIZE = 10UL; + std::vector data(SIZE); + const std::vector cdata(SIZE); + + int* pdata = ETL_OR_STD17::data(data); + const int* pcdata = ETL_OR_STD17::data(cdata); + + CHECK(data.data() == pdata); + CHECK(cdata.data() == pcdata); + } + + //************************************************************************* + TEST(test_c_array_data) + { + const size_t SIZE = 10UL; + int data[SIZE]; + const int cdata[SIZE] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + int* pdata = ETL_OR_STD17::data(data); + const int* pcdata = ETL_OR_STD17::data(cdata); + + CHECK(&data[0] == pdata); + CHECK(&cdata[0] == pcdata); + } } } diff --git a/test/test_span_dynamic_extent.cpp b/test/test_span_dynamic_extent.cpp index 586eadcb..832b4b22 100644 --- a/test/test_span_dynamic_extent.cpp +++ b/test/test_span_dynamic_extent.cpp @@ -1627,5 +1627,23 @@ namespace } #include "etl/private/diagnostic_pop.h" + + //************************************************************************* + TEST(test_not_constructible_from_rvalue_container) + { +#if ETL_USING_CPP17 + CHECK(!(etl::is_constructible_v)); + CHECK(!(etl::is_constructible_v)); + + CHECK(!(etl::is_constructible_v)); + CHECK(!(etl::is_constructible_v)); +#else + CHECK(!(etl::is_constructible::value)); + CHECK(!(etl::is_constructible::value)); + + CHECK(!(etl::is_constructible::value)); + CHECK(!(etl::is_constructible::value)); +#endif + } } } diff --git a/test/test_span_fixed_extent.cpp b/test/test_span_fixed_extent.cpp index a1825c2c..a79ebb70 100644 --- a/test/test_span_fixed_extent.cpp +++ b/test/test_span_fixed_extent.cpp @@ -496,13 +496,19 @@ namespace //************************************************************************* TEST(test_empty) { - View view1(etldata.begin(), etldata.begin()); - CHECK(!view1.empty()); - EView view2(etldata.begin(), etldata.begin()); CHECK(view2.empty()); } + //************************************************************************* + TEST(test_construction_from_mismatched_size) + { + CHECK_THROW((View(etldata.begin(), etldata.begin())), etl::span_size_mismatch); + CHECK_THROW((View(etldata.begin(), 1)), etl::span_size_mismatch); + CHECK_THROW((View(etldata.begin(), etldata.size() - 1)), etl::span_size_mismatch); + CHECK_THROW((View(etldata.begin(), etldata.size() + 1)), etl::span_size_mismatch); + } + //************************************************************************* TEST(test_size) { @@ -664,7 +670,7 @@ namespace CHECK_EQUAL(sub1.size(), cspan1.extent); CHECK_EQUAL(sub1.size(), cspan1.size()); - auto span2 = view.subspan<2>(); + auto span2 = view.subspan<2>(); isEqual = std::equal(sub2.begin(), sub2.end(), span2.begin()); CHECK(isEqual); CHECK_EQUAL(span2.size(), span2.extent); @@ -804,7 +810,7 @@ namespace //************************************************************************* #include "etl/private/diagnostic_unused_function_push.h" - + struct C_issue_482 {}; void f_issue_482(etl::span) @@ -1220,7 +1226,7 @@ namespace etl::span span2(span1); //etl::span span3(span1); // This line should fail to compile. } - + //************************************************************************* TEST(test_reinterpret_as) { @@ -1306,5 +1312,23 @@ namespace } #include "etl/private/diagnostic_pop.h" + + //************************************************************************* + TEST(test_not_constructible_from_rvalue_container) + { +#if ETL_USING_CPP17 + CHECK(!(etl::is_constructible_v)); + CHECK(!(etl::is_constructible_v)); + + CHECK(!(etl::is_constructible_v)); + CHECK(!(etl::is_constructible_v)); +#else + CHECK(!(etl::is_constructible::value)); + CHECK(!(etl::is_constructible::value)); + + CHECK(!(etl::is_constructible::value)); + CHECK(!(etl::is_constructible::value)); +#endif + } } } From 237d83c107d92f8d20f611b326cce1ba4a8b019f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BCthing?= Date: Thu, 12 Mar 2026 20:47:39 +0100 Subject: [PATCH 13/31] Add pre C++20 Support for ETL_CONSTINIT using Compiler Extensions (#1335) Co-authored-by: John Wellbelove --- include/etl/platform.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/etl/platform.h b/include/etl/platform.h index c519ff08..08532546 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -461,7 +461,13 @@ SOFTWARE. #define ETL_UNLIKELY #define ETL_CONSTEXPR20 #define ETL_CONSTEVAL - #define ETL_CONSTINIT + #if ETL_USING_CLANG_COMPILER && ETL_COMPILER_FULL_VERSION >= 40000 + #define ETL_CONSTINIT __attribute__((require_constant_initialization)) + #elif ETL_USING_GCC_COMPILER && ETL_COMPILER_FULL_VERSION >= 100000 + #define ETL_CONSTINIT __constinit + #else + #define ETL_CONSTINIT + #endif #define ETL_NO_UNIQUE_ADDRESS #define ETL_EXPLICIT_EXPR(...) explicit #endif From 78d8b82afb61bca0ee4599403700641b321dd493 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 12 Mar 2026 20:55:16 +0100 Subject: [PATCH 14/31] Print test names at test time (#1343) --- .github/workflows/clang-c++11.yml | 4 +- .github/workflows/clang-c++14.yml | 4 +- .github/workflows/clang-c++17.yml | 4 +- .github/workflows/clang-c++20.yml | 14 ++--- .github/workflows/clang-c++23.yml | 14 ++--- .github/workflows/gcc-c++11.yml | 4 +- .github/workflows/gcc-c++14.yml | 4 +- .github/workflows/gcc-c++17.yml | 4 +- .github/workflows/gcc-c++20.yml | 8 +-- .github/workflows/gcc-c++23.yml | 8 +-- .github/workflows/msvc.yml | 8 +-- test/main.cpp | 85 ++++++++++++++++++++++++++++++- test/run-tests.sh | 33 ++++++++---- 13 files changed, 144 insertions(+), 50 deletions(-) diff --git a/.github/workflows/clang-c++11.yml b/.github/workflows/clang-c++11.yml index 1d6976eb..d511f5b7 100644 --- a/.github/workflows/clang-c++11.yml +++ b/.github/workflows/clang-c++11.yml @@ -28,7 +28,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp11-linux-no-stl: name: Clang C++11 Linux - No STL @@ -50,4 +50,4 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests \ No newline at end of file + run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/clang-c++14.yml b/.github/workflows/clang-c++14.yml index dd6c2aad..d06778b1 100644 --- a/.github/workflows/clang-c++14.yml +++ b/.github/workflows/clang-c++14.yml @@ -28,7 +28,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp14-linux-no-stl: name: Clang C++14 Linux - No STL @@ -50,4 +50,4 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests \ No newline at end of file + run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/clang-c++17.yml b/.github/workflows/clang-c++17.yml index bc54989d..9fb4611f 100644 --- a/.github/workflows/clang-c++17.yml +++ b/.github/workflows/clang-c++17.yml @@ -28,7 +28,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp17-linux-no-stl: name: Clang C++17 Linux - No STL @@ -50,4 +50,4 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests \ No newline at end of file + run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/clang-c++20.yml b/.github/workflows/clang-c++20.yml index bfd88665..c68f9870 100644 --- a/.github/workflows/clang-c++20.yml +++ b/.github/workflows/clang-c++20.yml @@ -35,7 +35,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp20-linux-stl-force-cpp03: name: Clang C++20 Linux - STL - Force C++03 @@ -64,7 +64,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp20-linux-no-stl-force-cpp03: name: Clang C++20 Linux - No STL - Force C++03 @@ -93,7 +93,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp20-osx-stl: name: Clang C++20 OSX - STL @@ -115,7 +115,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp20-osx-no-stl: name: Clang C++20 OSX - No STL @@ -137,7 +137,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp20-osx-stl-force-cpp03: name: Clang C++20 OSX - STL - Force C++03 @@ -159,7 +159,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp20-osx-no-stl-force-cpp03: name: Clang C++20 OSX - No STL - Force C++03 @@ -181,5 +181,5 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v diff --git a/.github/workflows/clang-c++23.yml b/.github/workflows/clang-c++23.yml index 3e1f5a67..ffa88744 100644 --- a/.github/workflows/clang-c++23.yml +++ b/.github/workflows/clang-c++23.yml @@ -35,7 +35,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp23-linux-stl-force-cpp03: name: Clang C++23 Linux - STL - Force C++03 @@ -64,7 +64,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp2-linux-no-stl-force-cpp03: name: Clang C++23 Linux - No STL - Force C++03 @@ -93,7 +93,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp23-osx-stl: name: Clang C++23 OSX - STL @@ -115,7 +115,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp23-osx-no-stl: name: Clang C++23 OSX - No STL @@ -137,7 +137,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp23-osx-stl-force-cpp03: name: Clang C++23 OSX - STL - Force C++03 @@ -159,7 +159,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-clang-cpp23-osx-no-stl-force-cpp03: name: Clang C++23 OSX - No STL - Force C++03 @@ -181,5 +181,5 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v diff --git a/.github/workflows/gcc-c++11.yml b/.github/workflows/gcc-c++11.yml index 938402c1..571c6d6e 100644 --- a/.github/workflows/gcc-c++11.yml +++ b/.github/workflows/gcc-c++11.yml @@ -29,7 +29,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-gcc-cpp11-linux-no-stl: name: GCC C++11 Linux - No STL @@ -52,4 +52,4 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v diff --git a/.github/workflows/gcc-c++14.yml b/.github/workflows/gcc-c++14.yml index 59a06efc..f623af08 100644 --- a/.github/workflows/gcc-c++14.yml +++ b/.github/workflows/gcc-c++14.yml @@ -28,7 +28,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-gcc-cpp14-linux-no-stl: name: GCC C++14 Linux - No STL @@ -50,4 +50,4 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests \ No newline at end of file + run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/gcc-c++17.yml b/.github/workflows/gcc-c++17.yml index 90f6ced5..fc0ddd97 100644 --- a/.github/workflows/gcc-c++17.yml +++ b/.github/workflows/gcc-c++17.yml @@ -28,7 +28,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-gcc-cpp17-linux-no-stl: name: GCC C++17 Linux - No STL @@ -50,4 +50,4 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v diff --git a/.github/workflows/gcc-c++20.yml b/.github/workflows/gcc-c++20.yml index 62833fbf..5965e05e 100644 --- a/.github/workflows/gcc-c++20.yml +++ b/.github/workflows/gcc-c++20.yml @@ -28,7 +28,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-gcc-cpp20-linux-no-stl: name: GCC C++20 Linux - No STL @@ -50,7 +50,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-gcc-cpp20-linux-stl-force-cpp03: name: GCC C++20 Linux - STL - Force C++03 @@ -72,7 +72,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-gcc-cpp20-linux-no-stl-force-cpp03: name: GCC C++20 Linux - No STL - Force C++03 @@ -94,4 +94,4 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests \ No newline at end of file + run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/gcc-c++23.yml b/.github/workflows/gcc-c++23.yml index 4b2efa3d..f91924bd 100644 --- a/.github/workflows/gcc-c++23.yml +++ b/.github/workflows/gcc-c++23.yml @@ -28,7 +28,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-gcc-cpp23-linux-no-stl: name: GCC C++23 Linux - No STL @@ -50,7 +50,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-gcc-cpp23-linux-stl-force-cpp03: name: GCC C++23 Linux - STL - Force C++03 @@ -72,7 +72,7 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests + run: ./test/etl_tests -v build-gcc-cpp23-linux-no-stl-force-cpp03: name: GCC C++23 Linux - No STL - Force C++03 @@ -94,4 +94,4 @@ jobs: make -j $(getconf _NPROCESSORS_ONLN) - name: Run tests - run: ./test/etl_tests \ No newline at end of file + run: ./test/etl_tests -v \ No newline at end of file diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 9f28f025..32d7aee8 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -27,7 +27,7 @@ jobs: MSBuild.exe .\etl.sln - name: Run tests - run: test/Debug/etl_tests.exe + run: test/Debug/etl_tests.exe -v build-windows-msvc-no-stl: name: Windows - No STL @@ -48,7 +48,7 @@ jobs: MSBuild.exe .\etl.sln - name: Run tests - run: test/Debug/etl_tests.exe + run: test/Debug/etl_tests.exe -v build-windows-msvc-stl-force-cpp03: name: Windows - STL - Force C++03 @@ -70,7 +70,7 @@ jobs: MSBuild.exe .\etl.sln - name: Run tests - run: test/Debug/etl_tests.exe + run: test/Debug/etl_tests.exe -v build-windows-msvc-no-stl-force-cpp03: name: Windows - No STL - Force C++03 @@ -92,5 +92,5 @@ jobs: MSBuild.exe .\etl.sln - name: Run tests - run: test/Debug/etl_tests.exe + run: test/Debug/etl_tests.exe -v diff --git a/test/main.cpp b/test/main.cpp index fd6aaf87..68b74581 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -27,7 +27,88 @@ SOFTWARE. #include "unit_test_framework.h" -int main() +#include "UnitTest++/TestReporter.h" +#include "UnitTest++/TestReporterStdout.h" +#include "UnitTest++/TestRunner.h" +#include "UnitTest++/TestDetails.h" + +#include +#include +#include + +class VerboseTestReporter : public UnitTest::TestReporter { - return UnitTest::RunAllTests(); +public: + VerboseTestReporter() : m_testIndex(0), m_currentTestFailed(false) {} + + void ReportTestStart(UnitTest::TestDetails const& test) override + { + ++m_testIndex; + m_currentTestFailed = false; + std::cout << "[START #" << m_testIndex << "] " + << test.suiteName << "::" << test.testName << std::endl; + std::cout.flush(); + std::cerr.flush(); + } + + void ReportTestFinish(UnitTest::TestDetails const& test, float secondsElapsed) override + { + std::cout.flush(); + std::cerr.flush(); + std::cout << "[" << (m_currentTestFailed ? "FAILED" : "PASSED") + << " #" << m_testIndex << "] " + << test.suiteName << "::" << test.testName + << " (" << std::fixed << std::setprecision(4) << secondsElapsed << " s)" + << std::endl; + } + + void ReportFailure(UnitTest::TestDetails const& details, char const* failure) override + { + m_currentTestFailed = true; +#if defined(__APPLE__) || defined(__GNUG__) + std::cerr << details.filename << ":" << details.lineNumber << ":1: error: Failure in " + << details.testName << ": " << failure << std::endl; +#else + std::cerr << details.filename << "(" << details.lineNumber << "): error: Failure in " + << details.testName << ": " << failure << std::endl; +#endif + } + + void ReportSummary(int totalTestCount, int failedTestCount, + int failureCount, float secondsElapsed) override + { + if (failureCount > 0) + std::cout << "FAILURE: " << failedTestCount << " out of " << totalTestCount + << " tests failed (" << failureCount << " failures)." << std::endl; + else + std::cout << "Success: " << totalTestCount << " tests passed." << std::endl; + + std::cout << "Test time: " << std::fixed << std::setprecision(2) + << secondsElapsed << " seconds." << std::endl; + } + +private: + int m_testIndex; + bool m_currentTestFailed; +}; + +int main(int argc, char* argv[]) +{ + bool verbose = false; + for (int i = 1; i < argc; ++i) + { + if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) + verbose = true; + } + + if (verbose) + { + VerboseTestReporter reporter; + UnitTest::TestRunner runner(reporter); + return runner.RunTestsIf(UnitTest::Test::GetTestList(), NULL, UnitTest::True(), 0); + } + else + { + return UnitTest::RunAllTests(); + } } diff --git a/test/run-tests.sh b/test/run-tests.sh index e914114b..06e39400 100755 --- a/test/run-tests.sh +++ b/test/run-tests.sh @@ -38,9 +38,10 @@ PrintHeader() echo " Configuration : $configuration_name" | tee -a log.txt echo " Compiler : $compiler " | tee -a log.txt echo " Language : C++$cxx_standard " | tee -a log.txt - echo " Optimisation : $opt " | tee -a log.txt + echo " Optimisation : $opt " | tee -a log.txt echo " Sanitizer : $sanitize " | tee -a log.txt echo " Compiler select : $compiler_enabled " | tee -a log.txt + echo " Verbose : $verbose " | tee -a log.txt echo " ETL version : $etl_version " | tee -a log.txt echo " Git branch : $(ParseGitBranch) " | tee -a log.txt echo " Processes : ${CMAKE_BUILD_PARALLEL_LEVEL}" | tee -a log.txt @@ -51,14 +52,15 @@ PrintHeader() PrintHelp() { echo "$HelpColour" - echo "------------------------------------------------------------------------------------------------" - echo " Syntax : ./run-tests.sh " - echo " C++ Standard : 11, 14, 17, 20 or 23 " - echo " Optimisation : 0, 1, 2 or 3. Default = 0 " - echo " Threads : Number of threads to use. Default = 4 " - echo " Sanitizer : s enables sanitizer checks, n disables. Default disabled " - echo " Compiler select : gcc or clang. Default All compilers " - echo "------------------------------------------------------------------------------------------------" + echo "----------------------------------------------------------------------------------------------------------" + echo " Syntax : ./run-tests.sh " + echo " C++ Standard : 11, 14, 17, 20 or 23 " + echo " Optimisation : 0, 1, 2 or 3. Default = 0 " + echo " Threads : Number of threads to use. Default = 4 " + echo " Sanitizer : s enables sanitizer checks, n disables. Default disabled " + echo " Compiler select : gcc or clang. Default All compilers " + echo " Verbose : v enables verbose log, n disables. Default disabled " + echo "----------------------------------------------------------------------------------------------------------" echo "$NoColour" } @@ -173,6 +175,17 @@ else compiler_enabled="All compilers" fi +#****************************************************************************** +# Set the verbose enable. Default OFF +#****************************************************************************** +if [ "$6" = "v" ]; then + verbose="On" + verbose_flag="-v" +else + verbose="Off" + verbose_flag="" +fi + #****************************************************************************** # Get the ETL version #****************************************************************************** @@ -210,7 +223,7 @@ while read i ; do FailedCompilation exit $? fi - ./etl_tests + ./etl_tests $verbose_flag if [ $? -eq 0 ]; then PassedTests else From ad6e027b08b4d900f5cac616db0ab77cc17ef4c7 Mon Sep 17 00:00:00 2001 From: Ralph Rooding Date: Thu, 12 Mar 2026 21:39:53 +0100 Subject: [PATCH 15/31] Add PURL to Zephyr module.yml for SBoM generation (#1344) Co-authored-by: John Wellbelove --- scripts/update_version.py | 25 +++++++++++++++++++++++++ zephyr/module.yml | 3 +++ 2 files changed, 28 insertions(+) diff --git a/scripts/update_version.py b/scripts/update_version.py index 9d9fbc65..705a07a8 100644 --- a/scripts/update_version.py +++ b/scripts/update_version.py @@ -129,6 +129,29 @@ def update_library_properties(filename): f.write(line) f.write('\n') +#------------------------------------------------------------------------------ +def update_zephyr_module(): + print('') + print('Updating zephyr/module.yml') + + zephyr_module = os.path.join(etl_dir, 'zephyr', 'module.yml') + + with open(zephyr_module, 'r') as f: + text = f.read().splitlines() + + search_purl = 'pkg:github/ETLCPP/etl@' + + for i in range(len(text)): + if search_purl in text[i]: + idx = text[i].find(search_purl) + text[i] = text[i][:idx] + search_purl + full_version + print(text[i]) + + with open(zephyr_module, 'w') as f: + for line in text: + f.write(line) + f.write('\n') + #------------------------------------------------------------------------------ def update_versions(): print('') @@ -149,6 +172,8 @@ def update_versions(): update_library_properties(os.path.join(etl_dir, 'library.properties')) + update_zephyr_module() + #------------------------------------------------------------------------------ if __name__ == "__main__": update_versions() diff --git a/zephyr/module.yml b/zephyr/module.yml index b292d6a1..be27ae79 100644 --- a/zephyr/module.yml +++ b/zephyr/module.yml @@ -2,3 +2,6 @@ name: etl build: cmake: zephyr kconfig: zephyr/Kconfig +security: + external-references: + - pkg:github/ETLCPP/etl@20.46.2 From 89284de73377992171bb65d975f254c59422a1cd Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 13 Mar 2026 01:11:51 +0100 Subject: [PATCH 16/31] Add treefmt support (#1323) Co-authored-by: John Wellbelove --- .treefmt.toml | 38 ++++++++++++++ CONTRIBUTING.md | 1 + docs/source-formatting.md | 99 ++++++++++++++++++++++++++++++++++++ scripts/clang-format-wrapper | 48 +++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 .treefmt.toml create mode 100644 docs/source-formatting.md create mode 100755 scripts/clang-format-wrapper diff --git a/.treefmt.toml b/.treefmt.toml new file mode 100644 index 00000000..1dbcf80d --- /dev/null +++ b/.treefmt.toml @@ -0,0 +1,38 @@ +[global] +excludes = [ + "**/Doxyfile", + "**/Makefile", + "*.*-format", + "*.S", + "*.cmm", + "*.css", + "*.dld", + "*.gdb", + "*.gif", + "*.gitignore", + "*.html", + "*.ini", + "*.josh", + "*.json", + "*.md", + "*.png", + "*.puml", + "*.py", + "*.rb", + "*.rst", + "*.s", + "*.sh", + "*.spec", + "*.toml", + "*.txt", + "*.yaml", + "*.yml", + "docker/**", + "scripts/clang-format-wrapper", + "include/etl/generators/**" +] + +[formatter.cpp] +command = "scripts/clang-format-wrapper" +options = [ "-i", "--style=file" ] +includes = [ "*.c", "*.cc", "*.cpp", "*.h", "*.hh", "*.hpp" ] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6231d084..2d7a3028 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,7 @@ Thanks for considering a contribution! Here’s what you need to know before ope - If you are fixing a bug, add a unit test that *fails* before the bug fix is implemented. - Do not initiate a pull request until all of the units tests pass. See below for information on project files and test scripts. - Branches should be based on the branch `master`. If `development` has pending updates, I’ll rebase the PR against it before pulling.. +- For formatting help, you can use clang-format, or the convenience wrapper treefmt. See also [docs/source-formatting.md](docs/source-formatting.md) There is a project file for VS2022 for C++14, 17, 20, 23, and bash scripts that run the tests for C++11, 14, 17, 20, 23 under Linux with GCC and Clang. There are syntax-only check bash scripts that cover C++03, 11, 14, 17, 20, 23 under Linux with GCC and Clang. diff --git a/docs/source-formatting.md b/docs/source-formatting.md new file mode 100644 index 00000000..4c353c05 --- /dev/null +++ b/docs/source-formatting.md @@ -0,0 +1,99 @@ +# Source Formatting + +This project uses **clang-format** (version 18) to enforce a consistent coding style +for C and C++ source files. For convenience, **treefmt** is also configured as a +single-command wrapper that discovers and formats every file in the tree. + +--- + +## clang-format + +### Configuration file + +The formatting rules live in [`.clang-format`](../.clang-format) at the repository +root. The style is based on **LLVM**. + +See the `.clang-format` file itself for the complete list. + +### Version requirement + +clang-format **18** is required. +The helper script [`scripts/clang-format-wrapper`](../scripts/clang-format-wrapper) +automatically resolves the correct binary: it first looks for `clang-format-18` on +`PATH`, then falls back to `clang-format` and verifies that its major version is 18. +All other tooling in the repo calls this wrapper instead of `clang-format` directly. + +### Running clang-format manually + +Format every tracked source file in the repository: + +```bash +git ls-files -z \ + '*.c' '*.cc' '*.cpp' \ + '*.h' '*.hh' '*.hpp' \ + ':(exclude)include/etl/generators/*' | xargs -0 scripts/clang-format-wrapper -i --verbose --style=file +``` + +You can also format individual files directly: + +```bash +scripts/clang-format-wrapper -i --style=file path/to/file.cpp +``` + +--- + +## treefmt + +[treefmt](https://treefmt.com) is a language-agnostic source-tree formatter. +It reads a single configuration file and dispatches each file to the appropriate +formatter. In this project, it delegates all C/C++ formatting to the same +`clang-format-wrapper` described above. + +In comparison to calling clang-format directly, it brings a significant speedup. + +### Configuration file + +The configuration lives in [`.treefmt.toml`](../.treefmt.toml) at the repository root. + +### Installing treefmt + +treefmt is a standalone Go binary. Install it with any of: + +```bash +# Using the official install script +curl -fsSL https://raw.githubusercontent.com/numtide/treefmt/main/install.sh | bash + +# Or via Homebrew +brew install treefmt + +# Or via Nix +nix profile install nixpkgs#treefmt2 +``` + +See the [treefmt documentation](https://treefmt.com) for more options. + +### Running treefmt + +From the repository root: + +```bash +# Format everything +treefmt + +# Check formatting without modifying files (useful in CI) +treefmt --fail-on-change +``` + +--- + +## Excluded paths + +`.treefmt.toml` excludes generated files under +`include/etl/generators/`. Do **not** format those files manually via clang-format or treefmt. + +## Pre-commit + +Before submitting a PR / contribution, run `treefmt --fail-on-change` to catch +unformatted code before merge. + +Alternatively, a plain `treefmt` automatically fixes any issues. \ No newline at end of file diff --git a/scripts/clang-format-wrapper b/scripts/clang-format-wrapper new file mode 100755 index 00000000..b501b663 --- /dev/null +++ b/scripts/clang-format-wrapper @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +from shutil import which +import re +import subprocess +import sys + + +def get_correct_clang_format(): + path = which("clang-format-18") + if path: + return path + + if which("clang-format") is None: + raise Exception("no clang-format found") + + result = subprocess.run( + ["clang-format", "--version"], capture_output=True, text=True, check=True + ) + match = re.search(r"\b(\d+\.\d+\.\d+)\b", result.stdout) + if not match: + raise Exception( + f"could not determine clang-format version from: {result.stdout.strip()}" + ) + version = match.group(1) + if version.split(".")[0] != "18": + raise Exception(f"clang-format version 18 required. Found {version}") + + return "clang-format" + + +def main(): + clang_format = get_correct_clang_format() + try: + completed = subprocess.run([clang_format] + sys.argv[1:]) + except FileNotFoundError: + print(f"error: clang-format not found at '{clang_format}'", file=sys.stderr) + sys.exit(1) + except PermissionError: + print(f"error: permission denied when running '{clang_format}'", file=sys.stderr) + sys.exit(1) + except OSError as exc: + print(f"error: failed to run '{clang_format}': {exc}", file=sys.stderr) + sys.exit(1) + sys.exit(completed.returncode) + + +if __name__ == "__main__": + main() From 3639850b6bcb8fc3161f64d22612d6ab25f710fa Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Fri, 13 Mar 2026 07:37:02 +0000 Subject: [PATCH 17/31] Fixed type_traits.h generator --- .../etl/generators/type_traits_generator.h | 84 +++++++++++++++++-- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/include/etl/generators/type_traits_generator.h b/include/etl/generators/type_traits_generator.h index 42189f43..373b5d0e 100644 --- a/include/etl/generators/type_traits_generator.h +++ b/include/etl/generators/type_traits_generator.h @@ -872,6 +872,42 @@ namespace etl public: static ETL_CONSTANT bool value = decltype(test(0))::value; }; +#else + namespace private_type_traits + { + typedef char yes; + struct no { char dummy[2]; }; + + template + struct is_convertible_impl + { + static yes test(TTo); + static no test(...); + static TFrom make(); + static const bool value = (sizeof(test(make())) == sizeof(yes)); + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template <> + struct is_convertible_impl + { + static const bool value = true; + }; + } + + template + struct is_convertible : etl::bool_constant::value> {}; #endif #if ETL_USING_CPP17 @@ -1420,6 +1456,42 @@ typedef integral_constant true_type; public: static ETL_CONSTANT bool value = decltype(test(0))::value; }; +#else + namespace private_type_traits + { + typedef char yes; + struct no { char dummy[2]; }; + + template + struct is_convertible_impl + { + static yes test(TTo); + static no test(...); + static TFrom make(); + static const bool value = (sizeof(test(make())) == sizeof(yes)); + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template + struct is_convertible_impl + { + static const bool value = false; + }; + + template <> + struct is_convertible_impl + { + static const bool value = true; + }; + } + + template + struct is_convertible : etl::bool_constant::value> {}; #endif #if ETL_USING_CPP17 @@ -1556,17 +1628,17 @@ typedef integral_constant true_type; cog.outl("/// Template to determine if a type is one of a specified list.") cog.outl("///\\ingroup types") cog.outl("template " % IsOneOf) + cog.out(" ") + cog.outl(" typename T%s = void>" % IsOneOf) cog.outl("struct is_one_of") cog.outl("{") - cog.outl(" static const bool value = ") + cog.outl(" static const bool value =") for n in range(1, int(IsOneOf)): cog.outl(" etl::is_same::value ||" % n) cog.outl(" etl::is_same::value;" % IsOneOf) From 29ad4b327e8a3981dac9b6787fb7273651c841ff Mon Sep 17 00:00:00 2001 From: Henri Bragge Date: Tue, 17 Mar 2026 12:05:28 +0100 Subject: [PATCH 18/31] Fix comment in queue::pop (#1347) * Print test names at test time (#1343) * Fix comment in queue::pop --------- Co-authored-by: Roland Reichwein Co-authored-by: John Wellbelove --- include/etl/queue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/etl/queue.h b/include/etl/queue.h index 4c4fcd9f..6baa3e49 100644 --- a/include/etl/queue.h +++ b/include/etl/queue.h @@ -454,7 +454,7 @@ namespace etl } //************************************************************************* - /// Removes the oldest value from the back of the queue. + /// Removes the oldest value from the front of the queue. /// Does nothing if the queue is already empty. /// If asserts or exceptions are enabled, throws an etl::queue_empty if the queue is empty. //************************************************************************* From 325908b1b9b04d5e67655932bd07f390ebe982b0 Mon Sep 17 00:00:00 2001 From: William Sciaroni Date: Mon, 23 Mar 2026 14:30:03 -0500 Subject: [PATCH 19/31] Add permutation functions (#1348) * Print test names at test time (#1343) * Add permutation functions * Refactor permutation to use etl::less * Add test for next_permutation where begin == end * Update code to avoid multiple bind1st objects in loop * Remove duplicate is_partitioned test case Removed the duplicate is_partitioned test case from the test suite. --------- Co-authored-by: Roland Reichwein Co-authored-by: John Wellbelove --- include/etl/algorithm.h | 164 ++++++++++++++++++++++++++++++++---- test/test_algorithm.cpp | 180 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 321 insertions(+), 23 deletions(-) diff --git a/include/etl/algorithm.h b/include/etl/algorithm.h index cb38f8c3..ab4ae01e 100644 --- a/include/etl/algorithm.h +++ b/include/etl/algorithm.h @@ -2005,11 +2005,12 @@ namespace etl for (TIterator1 i = begin1; i != end1; ++i) { - if (i == etl::find_if(begin1, i, etl::bind1st(predicate, *i))) + const typename etl::binder1st predicate_is_i = etl::bind1st(predicate, *i); + if (i == etl::find_if(begin1, i, predicate_is_i)) { - size_t n = etl::count_if(begin2, end2, etl::bind1st(predicate, *i)); + size_t n = etl::count_if(begin2, end2, predicate_is_i); - if (n == 0 || size_t(etl::count_if(i, end1, etl::bind1st(predicate, *i))) != n) + if (n == 0 || size_t(etl::count_if(i, end1, predicate_is_i)) != n) { return false; } @@ -2038,15 +2039,18 @@ namespace etl return false; } - for (TIterator1 i = begin1; i != end1; ++i) + if (begin1 != end1) { - if (i == etl::find(begin1, i, *i)) + for (TIterator1 i = begin1; i != end1; ++i) { - size_t n = etl::count(begin2, end2, *i); - - if (n == 0 || size_t(etl::count(i, end1, *i)) != n) + if (i == etl::find(begin1, i, *i)) { - return false; + size_t n = etl::count(begin2, end2, *i); + + if (n == 0 || size_t(etl::count(i, end1, *i)) != n) + { + return false; + } } } } @@ -2073,15 +2077,19 @@ namespace etl return false; } - for (TIterator1 i = begin1; i != end1; ++i) + if (begin1 != end1) { - if (i == etl::find_if(begin1, i, etl::bind1st(predicate, *i))) + for (TIterator1 i = begin1; i != end1; ++i) { - size_t n = etl::count_if(begin2, end2, etl::bind1st(predicate, *i)); - - if (n == 0 || size_t(etl::count_if(i, end1, etl::bind1st(predicate, *i))) != n) + const typename etl::binder1st predicate_is_i = etl::bind1st(predicate, *i); + if (i == etl::find_if(begin1, i, predicate_is_i)) { - return false; + size_t n = etl::count_if(begin2, end2, predicate_is_i); + + if (n == 0 || size_t(etl::count_if(i, end1, predicate_is_i)) != n) + { + return false; + } } } } @@ -2089,6 +2097,132 @@ namespace etl return true; } + //*************************************************************************** + /// next_permutation + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + bool next_permutation(TIterator first, TIterator last, TCompare compare) + { + if (first == last) + { + return false; + } + + TIterator i = last; + --i; + + if (first == i) + { + return false; + } + + while (true) + { + TIterator i1 = i; + --i; + + if (compare(*i, *i1)) + { + TIterator j = last; + --j; + + while (!compare(*i, *j)) + { + --j; + } + + etl::iter_swap(i, j); + etl::reverse(i1, last); + return true; + } + + if (i == first) + { + etl::reverse(first, last); + return false; + } + } + } + + //*************************************************************************** + /// next_permutation + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + bool next_permutation(TIterator first, TIterator last) + { + typedef etl::less::value_type> compare; + return etl::next_permutation(first, last, compare()); + } + + //*************************************************************************** + /// prev_permutation + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + bool prev_permutation(TIterator first, TIterator last, TCompare compare) + { + if (first == last) + { + return false; + } + + TIterator i = last; + --i; + + if (first == i) + { + return false; + } + + while (true) + { + TIterator i1 = i; + --i; + + if (compare(*i1, *i)) + { + TIterator j = last; + --j; + + while (!compare(*j, *i)) + { + --j; + } + + etl::iter_swap(i, j); + etl::reverse(i1, last); + return true; + } + + if (i == first) + { + etl::reverse(first, last); + return false; + } + } + } + + //*************************************************************************** + /// prev_permutation + ///\ingroup algorithm + /// + //*************************************************************************** + template + ETL_CONSTEXPR14 + bool prev_permutation(TIterator first, TIterator last) + { + typedef etl::less::value_type> compare; + return etl::prev_permutation(first, last, compare()); + } + //*************************************************************************** /// is_partitioned ///\ingroup algorithm diff --git a/test/test_algorithm.cpp b/test/test_algorithm.cpp index 1429a977..66cd7caa 100644 --- a/test/test_algorithm.cpp +++ b/test/test_algorithm.cpp @@ -116,6 +116,14 @@ namespace } }; + struct DataEquivalenceByA : public etl::binary_function + { + bool operator ()(const Data& lhs, const Data& rhs) const + { + return lhs.a == rhs.a; + } + }; + Data dataD[10] = { Data(1, 2), Data(2, 1), Data(3, 4), Data(4, 3), Data(5, 6), Data(6, 5), Data(7, 8), Data(8, 7), Data(9, 10), Data(10, 9) }; struct Greater : public etl::binary_function @@ -3449,18 +3457,174 @@ namespace } //************************************************************************* - TEST(stable_sort_greater) + TEST(next_permutation) { - std::vector initial_data = { NDC(1, 1), NDC(2, 1), NDC(3, 1), NDC(2, 2), NDC(3, 2), NDC(4, 1), NDC(2, 3), NDC(3, 3), NDC(5, 1) }; + std::array expected = { 1, 1, 2, 2 }; + std::array result = expected; - std::vector data1(initial_data); - std::vector data2(initial_data); + for (size_t i = 0U; i < 8U; ++i) + { + bool expected_has_next = std::next_permutation(expected.begin(), expected.end()); + bool result_has_next = etl::next_permutation(result.begin(), result.end()); - std::stable_sort(data1.begin(), data1.end(), std::greater()); - etl::stable_sort(data2.begin(), data2.end(), std::greater()); + CHECK_EQUAL(expected_has_next, result_has_next); + CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size()); + } - bool is_same = std::equal(data1.begin(), data1.end(), data2.begin(), NDC::are_identical); - CHECK(is_same); + // Check one past the end. + bool expected_has_next = std::next_permutation(expected.begin(), expected.end()); + bool result_has_next = etl::next_permutation(result.begin(), result.end()); + CHECK_EQUAL(expected_has_next, result_has_next); + CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size()); + + int single_expected[] = { 1 }; + int single_result[] = { 1 }; + + expected_has_next = std::next_permutation(std::begin(single_expected), std::end(single_expected)); + result_has_next = etl::next_permutation(std::begin(single_result), std::end(single_result)); + + CHECK_EQUAL(expected_has_next, result_has_next); + CHECK_ARRAY_EQUAL(single_expected, single_result, 1U); + + // Check for what happens if the beginning and end are the same. + expected_has_next = std::next_permutation(std::begin(single_expected), std::begin(single_expected)); + result_has_next = etl::next_permutation(std::begin(single_result), std::begin(single_result)); + + CHECK_EQUAL(expected_has_next, result_has_next); + } + + //************************************************************************* + TEST(next_permutation_compare) + { + std::array expected = { 3, 2, 2, 1 }; + std::array result = expected; + + for (size_t i = 0U; i < 8U; ++i) + { + bool expected_has_next = std::next_permutation(expected.begin(), expected.end(), std::greater()); + bool result_has_next = etl::next_permutation(result.begin(), result.end(), std::greater()); + + CHECK_EQUAL(expected_has_next, result_has_next); + CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size()); + } + + // Check one past the end. + bool expected_has_next = std::next_permutation(expected.begin(), expected.end(), std::greater()); + bool result_has_next = etl::next_permutation(result.begin(), result.end(), std::greater()); + CHECK_EQUAL(expected_has_next, result_has_next); + CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size()); + + int single_expected[] = { 1 }; + int single_result[] = { 1 }; + + // Check for what happens if the beginning and end are the same. + expected_has_next = std::next_permutation(std::begin(single_expected), std::begin(single_expected), std::greater()); + result_has_next = etl::next_permutation(std::begin(single_result), std::begin(single_result), std::greater()); + + CHECK_EQUAL(expected_has_next, result_has_next); + CHECK_ARRAY_EQUAL(single_expected, single_result, 1U); + } + + //************************************************************************* + TEST(prev_permutation) + { + std::array expected = { 2, 2, 1, 1 }; + std::array result = expected; + + for (size_t i = 0U; i < 8U; ++i) + { + bool expected_has_prev = std::prev_permutation(expected.begin(), expected.end()); + bool result_has_prev = etl::prev_permutation(result.begin(), result.end()); + + CHECK_EQUAL(expected_has_prev, result_has_prev); + CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size()); + } + + // Check one past the end. + bool expected_has_prev = std::prev_permutation(expected.begin(), expected.end()); + bool result_has_prev = etl::prev_permutation(result.begin(), result.end()); + CHECK_EQUAL(expected_has_prev, result_has_prev); + CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size()); + + int single_expected[] = { 1 }; + int single_result[] = { 1 }; + + expected_has_prev = std::prev_permutation(std::begin(single_expected), std::end(single_expected)); + result_has_prev = etl::prev_permutation(std::begin(single_result), std::end(single_result)); + + CHECK_EQUAL(expected_has_prev, result_has_prev); + CHECK_ARRAY_EQUAL(single_expected, single_result, 1U); + + // Check for what happens if the beginning and end are the same. + expected_has_prev = std::prev_permutation(std::begin(single_expected), std::begin(single_expected)); + result_has_prev = etl::prev_permutation(std::begin(single_result), std::begin(single_result)); + + CHECK_EQUAL(expected_has_prev, result_has_prev); + } + + //************************************************************************* + TEST(prev_permutation_compare) + { + std::array expected = { 1, 1, 2, 3 }; + std::array result = expected; + + for (size_t i = 0U; i < 8U; ++i) + { + bool expected_has_prev = std::prev_permutation(expected.begin(), expected.end(), std::greater()); + bool result_has_prev = etl::prev_permutation(result.begin(), result.end(), std::greater()); + + CHECK_EQUAL(expected_has_prev, result_has_prev); + CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size()); + } + + // Check one past the end. + bool expected_has_prev = std::prev_permutation(expected.begin(), expected.end(), std::greater()); + bool result_has_prev = etl::prev_permutation(result.begin(), result.end(), std::greater()); + CHECK_EQUAL(expected_has_prev, result_has_prev); + CHECK_ARRAY_EQUAL(expected.data(), result.data(), result.size()); + + int single_expected[] = { 1 }; + int single_result[] = { 1 }; + + // Check for what happens if the beginning and end are the same. + expected_has_prev = std::prev_permutation(std::begin(single_expected), std::begin(single_expected), std::greater()); + result_has_prev = etl::prev_permutation(std::begin(single_result), std::begin(single_result), std::greater()); + + CHECK_EQUAL(expected_has_prev, result_has_prev); + CHECK_ARRAY_EQUAL(single_expected, single_result, 1U); + } + + //************************************************************************* + TEST(is_permutation_length_mismatch) + { + int data1[] = { 1, 2, 3 }; + int data2[] = { 1, 2, 3, 4 }; + + bool is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(data2), std::end(data2)); + CHECK_FALSE(is_permutation); + + is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(data2), std::end(data2), etl::equal_to()); + CHECK_FALSE(is_permutation); + } + + //************************************************************************* + TEST(is_permutation_predicate) + { + Data data1[] = { Data(1, 10), Data(2, 20), Data(2, 30), Data(3, 40) }; + Data permutation[] = { Data(2, 200), Data(1, 100), Data(3, 300), Data(2, 400) }; + Data not_permutation[] = { Data(2, 200), Data(1, 100), Data(4, 300), Data(2, 400) }; + + bool is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(permutation), DataEquivalenceByA()); + CHECK_TRUE(is_permutation); + + is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(not_permutation), DataEquivalenceByA()); + CHECK_FALSE(is_permutation); + + is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(permutation), std::end(permutation), DataEquivalenceByA()); + CHECK_TRUE(is_permutation); + + is_permutation = etl::is_permutation(std::begin(data1), std::end(data1), std::begin(not_permutation), std::end(not_permutation), DataEquivalenceByA()); + CHECK_FALSE(is_permutation); } //************************************************************************* From fa3ac745589295f89d73baa4b71be67bf45ac06f Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Mon, 23 Mar 2026 20:51:30 +0100 Subject: [PATCH 20/31] Limit clock duration to milliseconds type on narrow int type (#1364) * Print test names at test time (#1343) * Limit clock duration to milliseconds type on narrow int type On AVR, for 16 bit int types, nano and micro are not provided. Now, consistently use them only conditionally. --------- Co-authored-by: John Wellbelove --- include/etl/private/chrono/clocks.h | 18 +++++++++++++++--- include/etl/private/chrono/duration.h | 8 +++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/include/etl/private/chrono/clocks.h b/include/etl/private/chrono/clocks.h index 595290d7..cbdbfd26 100644 --- a/include/etl/private/chrono/clocks.h +++ b/include/etl/private/chrono/clocks.h @@ -33,7 +33,11 @@ SOFTWARE. #endif #if !defined(ETL_CHRONO_SYSTEM_CLOCK_DURATION) - #define ETL_CHRONO_SYSTEM_CLOCK_DURATION etl::chrono::nanoseconds + #if (INT_MAX >= INT32_MAX) + #define ETL_CHRONO_SYSTEM_CLOCK_DURATION etl::chrono::nanoseconds + #else + #define ETL_CHRONO_SYSTEM_CLOCK_DURATION etl::chrono::milliseconds + #endif #endif #if !defined(ETL_CHRONO_SYSTEM_CLOCK_IS_STEADY) @@ -41,7 +45,11 @@ SOFTWARE. #endif #if !defined(ETL_CHRONO_HIGH_RESOLUTION_CLOCK_DURATION) - #define ETL_CHRONO_HIGH_RESOLUTION_CLOCK_DURATION etl::chrono::nanoseconds + #if (INT_MAX >= INT32_MAX) + #define ETL_CHRONO_HIGH_RESOLUTION_CLOCK_DURATION etl::chrono::nanoseconds + #else + #define ETL_CHRONO_HIGH_RESOLUTION_CLOCK_DURATION etl::chrono::milliseconds + #endif #endif #if !defined(ETL_CHRONO_HIGH_RESOLUTION_CLOCK_IS_STEADY) @@ -49,7 +57,11 @@ SOFTWARE. #endif #if !defined(ETL_CHRONO_STEADY_CLOCK_DURATION) -#define ETL_CHRONO_STEADY_CLOCK_DURATION etl::chrono::nanoseconds + #if (INT_MAX >= INT32_MAX) + #define ETL_CHRONO_STEADY_CLOCK_DURATION etl::chrono::nanoseconds + #else + #define ETL_CHRONO_STEADY_CLOCK_DURATION etl::chrono::milliseconds + #endif #endif extern "C" ETL_CHRONO_SYSTEM_CLOCK_DURATION::rep etl_get_system_clock(); diff --git a/include/etl/private/chrono/duration.h b/include/etl/private/chrono/duration.h index cd81e9d9..043174b0 100644 --- a/include/etl/private/chrono/duration.h +++ b/include/etl/private/chrono/duration.h @@ -315,13 +315,17 @@ namespace etl /// Duration types //*********************************************************************** #if (ETL_USING_64BIT_TYPES) + #if (INT_MAX >= INT32_MAX) using nanoseconds = etl::chrono::duration; using microseconds = etl::chrono::duration; + #endif using milliseconds = etl::chrono::duration; using seconds = etl::chrono::duration>; #else + #if (INT_MAX >= INT32_MAX) using nanoseconds = etl::chrono::duration; using microseconds = etl::chrono::duration; + #endif using milliseconds = etl::chrono::duration; using seconds = etl::chrono::duration>; #endif @@ -811,6 +815,7 @@ namespace etl return etl::chrono::duration(s); } +#if (INT_MAX >= INT32_MAX) //*********************************************************************** /// Literal for microseconds duration //*********************************************************************** @@ -848,7 +853,7 @@ namespace etl } //*********************************************************************** - /// Literal for floating point microseconds duration + /// Literal for floating point nanoseconds duration //*********************************************************************** #if ETL_USING_VERBOSE_CHRONO_LITERALS inline ETL_CONSTEXPR14 etl::chrono::duration operator ""_nanoseconds(long double s) ETL_NOEXCEPT @@ -858,6 +863,7 @@ namespace etl { return etl::chrono::duration(s); } +#endif } } } From 0b5621e8091929aaf3f83ffe99cdb7462f1b36df Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 25 Mar 2026 08:54:18 +0100 Subject: [PATCH 21/31] Fix span size type (#1363) * Print test names at test time (#1343) * Fix span size type size() returned a signed value while declared (unsigned) size_t. --------- Co-authored-by: John Wellbelove --- include/etl/span.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/etl/span.h b/include/etl/span.h index c053d37b..5cdf5ba5 100644 --- a/include/etl/span.h +++ b/include/etl/span.h @@ -1068,7 +1068,7 @@ namespace etl //************************************************************************* ETL_NODISCARD ETL_CONSTEXPR size_t size() const ETL_NOEXCEPT { - return (pend - pbegin); + return static_cast(pend - pbegin); } //************************************************************************* @@ -1076,7 +1076,7 @@ namespace etl //************************************************************************* ETL_NODISCARD ETL_CONSTEXPR size_t size_bytes() const ETL_NOEXCEPT { - return sizeof(element_type) * (pend - pbegin); + return sizeof(element_type) * static_cast(pend - pbegin); } //************************************************************************* @@ -1262,7 +1262,7 @@ namespace etl ETL_ASSERT(etl::is_aligned::value>(pbegin), ETL_ERROR(span_alignment_exception)); return etl::span(reinterpret_cast(pbegin), - (pend - pbegin) * sizeof(element_type) / sizeof(TNew)); + static_cast(pend - pbegin) * sizeof(element_type) / sizeof(TNew)); } //************************************************************************* From bd15e4244018c162c84de8d70105d181f09ee8e5 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 25 Mar 2026 09:14:33 +0100 Subject: [PATCH 22/31] Fix etl::optional for deleted copy constructors (#1362) * Print test names at test time (#1343) * Fix etl::optional for deleted copy constructors --------- Co-authored-by: John Wellbelove --- include/etl/optional.h | 152 +++++++++++++++++------------------------ test/test_optional.cpp | 41 +++++++++++ 2 files changed, 104 insertions(+), 89 deletions(-) diff --git a/include/etl/optional.h b/include/etl/optional.h index ec998479..c696a66d 100644 --- a/include/etl/optional.h +++ b/include/etl/optional.h @@ -169,21 +169,19 @@ namespace etl } //*************************************************************************** - /// Constructor from value type. + /// Converting constructor from value type. + /// Constructs T in-place from U&&, without requiring T to be + /// copy/move constructible. //*************************************************************************** + template ::value && + !etl::is_same::type, etl::in_place_t>::value && + !etl::is_same::type, optional_impl>::value, int>::type = 0> ETL_CONSTEXPR20_STL - optional_impl(const T& value_) + optional_impl(U&& value_) { - storage.construct(value_); - } - - //*************************************************************************** - /// Constructor from value type. - //*************************************************************************** - ETL_CONSTEXPR20_STL - optional_impl(T&& value_) - { - storage.construct(etl::move(value_)); + storage.construct(etl::forward(value_)); } //*************************************************************************** @@ -280,23 +278,24 @@ namespace etl //*************************************************************************** /// Assignment operator from value type. //*************************************************************************** +#if ETL_USING_CPP11 + template ::value && + !etl::is_same::type, optional_impl>::value, int>::type = 0> + ETL_CONSTEXPR20_STL + optional_impl& operator =(U&& value_) + { + storage.construct(etl::forward(value_)); + + return *this; + } +#else ETL_CONSTEXPR20_STL optional_impl& operator =(const T& value_) { storage.construct(value_); - return *this; - } - -#if ETL_USING_CPP11 - //*************************************************************************** - /// Assignment operator from value type. - //*************************************************************************** - ETL_CONSTEXPR20_STL - optional_impl& operator =(T&& value_) - { - storage.construct(etl::move(value_)); - return *this; } #endif @@ -1442,22 +1441,36 @@ namespace etl #if ETL_USING_CPP11 //*************************************************************************** - /// Construct from value type. + /// Converting constructor from value type. + /// Constructs T in-place from U&&, without requiring T to be + /// copy/move constructible. //*************************************************************************** - template + template ::value && + !etl::is_same::type, etl::optional>::value && + !etl::is_same::type, etl::in_place_t>::value && + !etl::is_same::type, etl::nullopt_t>::value && + etl::is_pod::type>::value, int>::type = 0> ETL_CONSTEXPR14 - optional(const T& value_) - : impl_t(value_) + optional(U&& value_) + : impl_t(etl::forward(value_)) { } //*************************************************************************** - /// Construct from value type. + /// Converting constructor from value type. //*************************************************************************** - template + template ::value && + !etl::is_same::type, etl::optional>::value && + !etl::is_same::type, etl::in_place_t>::value && + !etl::is_same::type, etl::nullopt_t>::value && + !etl::is_pod::type>::value, int>::type = 0> ETL_CONSTEXPR20_STL - optional(const T& value_) - : impl_t(value_) + optional(U&& value_) + : impl_t(etl::forward(value_)) { } #else @@ -1470,29 +1483,6 @@ namespace etl } #endif - -#if ETL_USING_CPP11 - //*************************************************************************** - /// Move construct from value type. - //*************************************************************************** - template - ETL_CONSTEXPR14 - optional(T&& value_) - : impl_t(etl::move(value_)) - { - } - - //*************************************************************************** - /// Move construct from value type. - //*************************************************************************** - template - ETL_CONSTEXPR20_STL - optional(T&& value_) - : impl_t(etl::move(value_)) - { - } -#endif - #if ETL_USING_CPP11 //*************************************************************************** /// Emplace construct from arguments. @@ -1641,25 +1631,35 @@ namespace etl #if ETL_USING_CPP11 //*************************************************************************** - /// Assignment operator from value type. + /// Converting assignment operator from value type. //*************************************************************************** - template + template ::value && + !etl::is_same::type, etl::optional>::value && + !etl::is_same::type, etl::nullopt_t>::value && + etl::is_pod::type>::value, int>::type = 0> ETL_CONSTEXPR14 - optional& operator =(const T& value_) + optional& operator =(U&& value_) { - impl_t::operator=(value_); + impl_t::operator=(etl::forward(value_)); return *this; } //*************************************************************************** - /// Assignment operator from value type. + /// Converting assignment operator from value type. //*************************************************************************** - template + template ::value && + !etl::is_same::type, etl::optional>::value && + !etl::is_same::type, etl::nullopt_t>::value && + !etl::is_pod::type>::value, int>::type = 0> ETL_CONSTEXPR20_STL - optional& operator =(const T& value_) + optional& operator =(U&& value_) { - impl_t::operator=(value_); + impl_t::operator=(etl::forward(value_)); return *this; } @@ -1675,32 +1675,6 @@ namespace etl } #endif -#if ETL_USING_CPP11 - //*************************************************************************** - /// Move assignment operator from value type. - //*************************************************************************** - template - ETL_CONSTEXPR14 - optional& operator =(T&& value_) - { - impl_t::operator=(etl::move(value_)); - - return *this; - } - - //*************************************************************************** - /// Move assignment operator from value type. - //*************************************************************************** - template - ETL_CONSTEXPR20_STL - optional& operator =(T&& value_) - { - impl_t::operator=(etl::move(value_)); - - return *this; - } -#endif - //*************************************************************************** /// Returns an iterator to the beginning of the optional. //*************************************************************************** diff --git a/test/test_optional.cpp b/test/test_optional.cpp index 6ca762a9..8968efd7 100644 --- a/test/test_optional.cpp +++ b/test/test_optional.cpp @@ -1118,5 +1118,46 @@ namespace CHECK_EQUAL(42, *opt); } + + //************************************************************************* + // GitHub issue #146: etl::optional doesn't compile with deleted copy constructor + //************************************************************************* +#if ETL_USING_CPP11 + struct Issue146_NonCopyable + { + Issue146_NonCopyable(int some) : _some(some) {} + Issue146_NonCopyable(const Issue146_NonCopyable&) = delete; + Issue146_NonCopyable(Issue146_NonCopyable&&) = delete; + Issue146_NonCopyable& operator=(const Issue146_NonCopyable&) = delete; + + int _some; + }; + + struct Issue146_Container + { + Issue146_Container(int a_val) : a(a_val) {} + Issue146_Container() : a(etl::nullopt) {} + + etl::optional a; + }; + + TEST(test_optional_issue_146_deleted_copy_ctor) + { + // etl::optional should compile when T has deleted copy/move constructors, + // as long as T is constructible from the given arguments. + Issue146_Container with_value(42); + Issue146_Container without_value; + + CHECK_TRUE(with_value.a.has_value()); + CHECK_EQUAL(42, with_value.a->_some); + + CHECK_FALSE(without_value.a.has_value()); + + // in_place construction should also work + etl::optional opt(etl::in_place_t{}, 99); + CHECK_TRUE(opt.has_value()); + CHECK_EQUAL(99, opt->_some); + } +#endif } } From 66e3d83a815e67a835b038daac11e69acaf93ebb Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 25 Mar 2026 13:16:36 +0100 Subject: [PATCH 23/31] Fix ETL_HAS_CONSTEXPR_ENDIANESS in unaligned_type.h (#1361) * Print test names at test time (#1343) * Fix ETL_HAS_CONSTEXPR_ENDIANESS in unaligned_type.h --------- Co-authored-by: John Wellbelove --- include/etl/unaligned_type.h | 24 +++++----- test/test_unaligned_type.cpp | 92 ++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 12 deletions(-) diff --git a/include/etl/unaligned_type.h b/include/etl/unaligned_type.h index 87519dbf..eed83f85 100644 --- a/include/etl/unaligned_type.h +++ b/include/etl/unaligned_type.h @@ -345,8 +345,8 @@ namespace etl { memcpy(store, &value, Size_); -#if ETL_HAS_CONSTEXPR_ENDIANESS - if ETL_IF_CONSTEXPR(Endian_ == etl::endianness::value()) +#if ETL_HAS_CONSTEXPR_ENDIANNESS + if ETL_IF_CONSTEXPR(Endian_ != etl::endianness::value()) #else if (Endian_ != etl::endianness::value()) #endif @@ -361,8 +361,8 @@ namespace etl { memcpy(&value, store, Size_); -#if ETL_HAS_CONSTEXPR_ENDIANESS - if ETL_IF_CONSTEXPR(Endian == etl::endianness::value()) +#if ETL_HAS_CONSTEXPR_ENDIANNESS + if ETL_IF_CONSTEXPR(Endian_ != etl::endianness::value()) #else if (Endian_ != etl::endianness::value()) #endif @@ -402,8 +402,8 @@ namespace etl { memcpy(store, &value, Size_); -#if ETL_HAS_CONSTEXPR_ENDIANESS - if ETL_IF_CONSTEXPR(Endian_ == etl::endianness::value()) +#if ETL_HAS_CONSTEXPR_ENDIANNESS + if ETL_IF_CONSTEXPR(Endian_ != etl::endianness::value()) #else if (Endian_ != etl::endianness::value()) #endif @@ -418,14 +418,14 @@ namespace etl { memcpy(&value, store, Size_); -#if ETL_HAS_CONSTEXPR_ENDIANESS - if ETL_IF_CONSTEXPR(Endian == etl::endianness::value()) +#if ETL_HAS_CONSTEXPR_ENDIANNESS + if ETL_IF_CONSTEXPR(Endian_ != etl::endianness::value()) #else - if (Endian_ != etl::endianness::value()) + if (Endian_ != etl::endianness::value()) #endif - { - etl::reverse(reinterpret_cast(&value), reinterpret_cast(&value) + Size_); - } + { + etl::reverse(reinterpret_cast(&value), reinterpret_cast(&value) + Size_); + } } //******************************* diff --git a/test/test_unaligned_type.cpp b/test/test_unaligned_type.cpp index f5be4d09..786b4ce8 100644 --- a/test/test_unaligned_type.cpp +++ b/test/test_unaligned_type.cpp @@ -1030,6 +1030,98 @@ namespace CHECK_EQUAL(0x12, bev0); CHECK_EQUAL(0x34, bev1); } + +#if ETL_HAS_CONSTEXPR_ENDIANNESS + //************************************************************************* + TEST(test_constexpr_endianness_integral_round_trip) + { + // Store a known value in LE, BE, and host-order unaligned types. + const uint32_t value = 0x12345678U; + + etl::le_uint32_t le_v(value); + etl::be_uint32_t be_v(value); + etl::host_uint32_t host_v(value); + + // All must read back the original value. + CHECK_EQUAL(value, uint32_t(le_v)); + CHECK_EQUAL(value, uint32_t(be_v)); + CHECK_EQUAL(value, uint32_t(host_v)); + + // Verify the storage byte order is correct. + // LE stores LSB first: 0x78, 0x56, 0x34, 0x12 + CHECK_EQUAL(0x78, int(le_v[0])); + CHECK_EQUAL(0x56, int(le_v[1])); + CHECK_EQUAL(0x34, int(le_v[2])); + CHECK_EQUAL(0x12, int(le_v[3])); + + // BE stores MSB first: 0x12, 0x34, 0x56, 0x78 + CHECK_EQUAL(0x12, int(be_v[0])); + CHECK_EQUAL(0x34, int(be_v[1])); + CHECK_EQUAL(0x56, int(be_v[2])); + CHECK_EQUAL(0x78, int(be_v[3])); + + // Host-order must match one of the above depending on the platform. + if (etl::endianness::value() == etl::endian::little) + { + CHECK_EQUAL(0x78, int(host_v[0])); + CHECK_EQUAL(0x56, int(host_v[1])); + CHECK_EQUAL(0x34, int(host_v[2])); + CHECK_EQUAL(0x12, int(host_v[3])); + } + else + { + CHECK_EQUAL(0x12, int(host_v[0])); + CHECK_EQUAL(0x34, int(host_v[1])); + CHECK_EQUAL(0x56, int(host_v[2])); + CHECK_EQUAL(0x78, int(host_v[3])); + } + } + + //************************************************************************* + TEST(test_constexpr_endianness_float_round_trip) + { + // Store a known float value in LE, BE, and host-order unaligned types. + const float value = 3.1415927f; + + etl::le_float_t le_v(value); + etl::be_float_t be_v(value); + etl::host_float_t host_v(value); + + // All must read back the original value. + CHECK_CLOSE(value, float(le_v), 0.0001f); + CHECK_CLOSE(value, float(be_v), 0.0001f); + CHECK_CLOSE(value, float(host_v), 0.0001f); + + // LE and BE storage bytes must be the reverse of each other. + CHECK_EQUAL(int(le_v[0]), int(be_v[3])); + CHECK_EQUAL(int(le_v[1]), int(be_v[2])); + CHECK_EQUAL(int(le_v[2]), int(be_v[1])); + CHECK_EQUAL(int(le_v[3]), int(be_v[0])); + } + + //************************************************************************* + TEST(test_constexpr_endianness_cross_endian_copy) + { + // Verify that converting between LE <-> BE via host works correctly. + const uint16_t value = 0xABCDU; + + etl::le_uint16_t le_v(value); + etl::be_uint16_t be_v(value); + etl::host_uint16_t host_from_le(le_v); + etl::host_uint16_t host_from_be(be_v); + + CHECK_EQUAL(value, uint16_t(host_from_le)); + CHECK_EQUAL(value, uint16_t(host_from_be)); + + // Round-trip: host -> le -> read back + etl::le_uint16_t le_from_host(host_from_le); + CHECK_EQUAL(value, uint16_t(le_from_host)); + + // Round-trip: host -> be -> read back + etl::be_uint16_t be_from_host(host_from_be); + CHECK_EQUAL(value, uint16_t(be_from_host)); + } +#endif } } From e7107b24f12c26ad0305a9f5c9bd904a965f4e08 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 25 Mar 2026 13:47:03 +0100 Subject: [PATCH 24/31] Fix tests on big endian (#1356) * Print test names at test time (#1343) * Fix tests on big endian --------- Co-authored-by: John Wellbelove --- test/test_crc8_nrsc5.cpp | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/test/test_crc8_nrsc5.cpp b/test/test_crc8_nrsc5.cpp index 45835e7a..9290152f 100644 --- a/test/test_crc8_nrsc5.cpp +++ b/test/test_crc8_nrsc5.cpp @@ -35,6 +35,8 @@ SOFTWARE. #include "etl/crc8_nrsc5.h" +#include + //***************************************************************************** // The results for these tests were created from https://crccalc.com/ //***************************************************************************** @@ -115,7 +117,15 @@ namespace TEST(test_crc8_nrsc5_4_add_range_endian) { std::vector data1 = { 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U, 0x08U }; - std::vector data2 = { 0x04030201UL, 0x08070605UL }; + std::vector data2; + if (etl::endianness::value() == etl::endian::little) + { + data2 = { 0x04030201UL, 0x08070605UL }; + } + else + { + data2 = { 0x01020304UL, 0x05060708UL }; + } std::vector data3 = { 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U }; uint8_t crc1 = etl::crc8_nrsc5_t4(data1.begin(), data1.end()); @@ -198,7 +208,15 @@ namespace TEST(test_crc8_nrsc5_16_add_range_endian) { std::vector data1 = { 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U, 0x08U }; - std::vector data2 = { 0x04030201UL, 0x08070605UL }; + std::vector data2; + if (etl::endianness::value() == etl::endian::little) + { + data2 = { 0x04030201UL, 0x08070605UL }; + } + else + { + data2 = { 0x01020304UL, 0x05060708UL }; + } std::vector data3 = { 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U }; uint8_t crc1 = etl::crc8_nrsc5_t16(data1.begin(), data1.end()); @@ -281,7 +299,15 @@ namespace TEST(test_crc8_nrsc5_256_add_range_endian) { std::vector data1 = { 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U, 0x08U }; - std::vector data2 = { 0x04030201UL, 0x08070605UL }; + std::vector data2; + if (etl::endianness::value() == etl::endian::little) + { + data2 = { 0x04030201UL, 0x08070605UL }; + } + else + { + data2 = { 0x01020304UL, 0x05060708UL }; + } std::vector data3 = { 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U }; uint8_t crc1 = etl::crc8_nrsc5(data1.begin(), data1.end()); From 7fdea7f7ae970380ab5541b973a8aaea37297676 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 25 Mar 2026 20:28:12 +0100 Subject: [PATCH 25/31] Adjustments in tuple.h from review (#1350) * Print test names at test time (#1343) * Adjustments in tuple.h from review --------- Co-authored-by: John Wellbelove --- include/etl/tuple.h | 86 ++++++++++++++++++++-------------------- test/test_tuple.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 45 deletions(-) diff --git a/include/etl/tuple.h b/include/etl/tuple.h index dde08d13..143ee12e 100644 --- a/include/etl/tuple.h +++ b/include/etl/tuple.h @@ -276,7 +276,7 @@ namespace etl /// Implicit conversion //********************************* template () == number_of_types()) && - (number_of_types() >= 1U) && + (number_of_types() >= 1U) && etl::is_convertible::value, int> = 0> ETL_CONSTEXPR14 tuple(tuple& other) @@ -290,7 +290,7 @@ namespace etl /// Explicit conversion //********************************* template () == number_of_types()) && - (number_of_types() >= 1U) && + (number_of_types() >= 1U) && !etl::is_convertible::value, int> = 0> ETL_CONSTEXPR14 explicit tuple(tuple& other) @@ -304,7 +304,7 @@ namespace etl /// Implicit conversion //********************************* template () == number_of_types()) && - (number_of_types() >= 1U) && + (number_of_types() >= 1U) && etl::is_convertible::value, int> = 0> ETL_CONSTEXPR14 tuple(const tuple& other) @@ -318,7 +318,7 @@ namespace etl /// Explicit conversion //********************************* template () == number_of_types()) && - (number_of_types() >= 1U) && + (number_of_types() >= 1U) && !etl::is_convertible::value, int> = 0> ETL_CONSTEXPR14 explicit tuple(const tuple& other) @@ -332,7 +332,7 @@ namespace etl /// Implicit conversion //********************************* template () == number_of_types()) && - (number_of_types() >= 1U) && + (number_of_types() >= 1U) && etl::is_convertible::value, int> = 0> ETL_CONSTEXPR14 tuple(tuple&& other) @@ -399,7 +399,7 @@ namespace etl //********************************* template >::value && (number_of_types() == number_of_types()) && - (number_of_types() >= 1U) && + (number_of_types() >= 1U) && etl::is_convertible::value, int> = 0> ETL_CONSTEXPR14 tuple(UHead&& head, UTail&&... tail) ETL_NOEXCEPT @@ -428,8 +428,8 @@ namespace etl /// Implicit conversion. //********************************* template () == 2U && - etl ::is_convertible::value && - etl ::is_convertible::value, int> = 0> + etl::is_convertible::value && + etl::is_convertible::value, int> = 0> ETL_CONSTEXPR14 tuple(ETL_OR_STD::pair& p) ETL_NOEXCEPT : base_type(p.second) @@ -442,8 +442,8 @@ namespace etl /// Explicit conversion. //********************************* template () == 2U && - (!etl ::is_convertible::value || - !etl ::is_convertible::value), int> = 0> + (!etl::is_convertible::value || + !etl::is_convertible::value), int> = 0> ETL_CONSTEXPR14 explicit tuple(ETL_OR_STD::pair& p) ETL_NOEXCEPT : base_type(p.second) @@ -456,8 +456,8 @@ namespace etl /// Implicit conversion. //********************************* template () == 2U && - etl ::is_convertible::value && - etl ::is_convertible::value, int> = 0> + etl::is_convertible::value && + etl::is_convertible::value, int> = 0> ETL_CONSTEXPR14 tuple(const ETL_OR_STD::pair& p) ETL_NOEXCEPT : base_type(p.second) @@ -470,8 +470,8 @@ namespace etl /// Explicit conversion. //********************************* template () == 2U && - (!etl ::is_convertible::value || - !etl ::is_convertible::value), int> = 0> + (!etl::is_convertible::value || + !etl::is_convertible::value), int> = 0> ETL_CONSTEXPR14 explicit tuple(const ETL_OR_STD::pair& p) ETL_NOEXCEPT : base_type(p.second) @@ -484,8 +484,8 @@ namespace etl /// Implicit conversion. //********************************* template () == 2U && - etl ::is_convertible::value && - etl ::is_convertible::value, int> = 0> + etl::is_convertible::value && + etl::is_convertible::value, int> = 0> ETL_CONSTEXPR14 tuple(ETL_OR_STD::pair&& p) ETL_NOEXCEPT : base_type(etl::forward(p.second)) @@ -498,8 +498,8 @@ namespace etl /// Explicit conversion. //********************************* template () == 2U && - (!etl ::is_convertible::value || - !etl ::is_convertible::value), int> = 0> + (!etl::is_convertible::value || + !etl::is_convertible::value), int> = 0> ETL_CONSTEXPR14 explicit tuple(ETL_OR_STD::pair&& p) ETL_NOEXCEPT : base_type(etl::forward(p.second)) @@ -512,12 +512,12 @@ namespace etl /// Implicit conversion. //********************************* template () == 2U && - etl ::is_convertible::value && - etl ::is_convertible::value, int> = 0> + etl::is_convertible::value && + etl::is_convertible::value, int> = 0> ETL_CONSTEXPR14 tuple(const ETL_OR_STD::pair&& p) ETL_NOEXCEPT - : base_type(etl::forward(p.second)) - , value(etl::forward(p.first)) + : base_type(etl::forward(p.second)) + , value(etl::forward(p.first)) { } @@ -526,12 +526,12 @@ namespace etl /// Explicit conversion. //********************************* template () == 2U && - (!etl ::is_convertible::value || - !etl ::is_convertible::value), int> = 0> + (!etl::is_convertible::value || + !etl::is_convertible::value), int> = 0> ETL_CONSTEXPR14 explicit tuple(const ETL_OR_STD::pair&& p) ETL_NOEXCEPT - : base_type(p.second) - , value(p.first) + : base_type(etl::forward(p.second)) + , value(etl::forward(p.first)) { } @@ -605,8 +605,8 @@ namespace etl ETL_CONSTEXPR14 tuple& operator =(const ETL_OR_STD::pair&& p) { - get_value() = p.first; - get_base().get_value() = p.second; + get_value() = etl::forward(p.first); + get_base().get_value() = etl::forward(p.second); return *this; } @@ -763,7 +763,7 @@ namespace etl //*************************************************************************** /// Extracts the element at Index from the tuple. - /// Index must be an integer value in sizeof...(TTypes)). + /// Index must be an integer value in [0, sizeof...(TTypes)). /// Returns a reference. //*************************************************************************** template @@ -782,7 +782,7 @@ namespace etl //*************************************************************************** /// Extracts the element at Index from the tuple. - /// Index must be an integer value in [?0?, sizeof...(TTypes)). + /// Index must be an integer value in [0, sizeof...(TTypes)). /// Returns a const reference. //*************************************************************************** template @@ -801,7 +801,7 @@ namespace etl //*************************************************************************** /// Extracts the element at Index from the tuple. - /// Index must be an integer value in [?0?, sizeof...(TTypes)). + /// Index must be an integer value in [0, sizeof...(TTypes)). /// Returns an rvalue reference. //*************************************************************************** template @@ -820,7 +820,7 @@ namespace etl //*************************************************************************** /// Extracts the element at Index from the tuple. - /// Index must be an integer value in [?0?, sizeof...(TTypes)). + /// Index must be an integer value in [0, sizeof...(TTypes)). /// Returns a const rvalue reference. //*************************************************************************** template @@ -1017,17 +1017,17 @@ namespace etl ETL_NODISCARD ETL_CONSTEXPR14 auto tuple_cat(Tuple1&& t1, Tuple2&& t2, Tuples&&... ts) - -> decltype(private_tuple::tuple_cat_impl(etl::forward(t1), - etl::make_index_sequence>::value>{}, - etl::forward(t2), - etl::make_index_sequence>::value>{})) + -> decltype(tuple_cat(private_tuple::tuple_cat_impl(etl::forward(t1), + etl::make_index_sequence>::value>{}, + etl::forward(t2), + etl::make_index_sequence>::value>{}), + etl::forward(ts)...)) { - auto concatenated = private_tuple::tuple_cat_impl(etl::forward(t1), - etl::make_index_sequence>::value>{}, - etl::forward(t2), - etl::make_index_sequence>::value>{}); - - return tuple_cat(etl::move(concatenated), etl::forward(ts)...); + return tuple_cat(private_tuple::tuple_cat_impl(etl::forward(t1), + etl::make_index_sequence>::value>{}, + etl::forward(t2), + etl::make_index_sequence>::value>{}), + etl::forward(ts)...); } #if ETL_USING_STL @@ -1317,7 +1317,7 @@ namespace std template struct tuple_element> { - using type = typename etl::nth_type_t; + using type = etl::nth_type_t; }; } diff --git a/test/test_tuple.cpp b/test/test_tuple.cpp index 918541e0..c7e7a667 100644 --- a/test/test_tuple.cpp +++ b/test/test_tuple.cpp @@ -35,6 +35,7 @@ SOFTWARE. #include #include +#include namespace { @@ -486,6 +487,56 @@ namespace CHECK_EQUAL(std::string("2"), d.value); } + //************************************************************************* + TEST(test_construct_from_rvalue_pair_implicit_conversion) + { + ETL_OR_STD::pair p(1, Data("2")); + + etl::tuple tp(etl::move(p)); + + int i = etl::get<0>(tp); + Data d = etl::get<1>(tp); + + CHECK_EQUAL(1, i); + CHECK_EQUAL(std::string("2"), d.value); + } + + //************************************************************************* + TEST(test_construct_from_rvalue_pair_explicit_conversion) + { + ETL_OR_STD::pair p(From(1), From(2)); + + etl::tuple tp(etl::move(p)); + + CHECK_EQUAL(1, etl::get<0>(tp).i); + CHECK_EQUAL(2, etl::get<1>(tp).i); + } + + //************************************************************************* + TEST(test_construct_from_const_rvalue_pair_implicit_conversion) + { + const ETL_OR_STD::pair p(1, Data("2")); + + etl::tuple tp(etl::move(p)); + + int i = etl::get<0>(tp); + Data d = etl::get<1>(tp); + + CHECK_EQUAL(1, i); + CHECK_EQUAL(std::string("2"), d.value); + } + + //************************************************************************* + TEST(test_construct_from_const_rvalue_pair_explicit_conversion) + { + const ETL_OR_STD::pair p(From(1), From(2)); + + etl::tuple tp(etl::move(p)); + + CHECK_EQUAL(1, etl::get<0>(tp).i); + CHECK_EQUAL(2, etl::get<1>(tp).i); + } + //************************************************************************* ETL_NODISCARD bool Get() { @@ -553,7 +604,7 @@ namespace } //************************************************************************* - TEST(test_tuple_cat) + TEST(test_tuple_cat_2) { etl::tuple tp1{1, 2.3}; etl::tuple tp2{4, Data("Data", 5)}; @@ -566,6 +617,46 @@ namespace CHECK_EQUAL(etl::get<3>(tp3), etl::get<1>(tp2)); } + //************************************************************************* + TEST(test_tuple_cat_3) + { + etl::tuple tp1{1, 2.3}; + etl::tuple tp2{4, Data("Data", 5)}; + etl::tuple tp3{true, 5}; + + auto tp4 = etl::tuple_cat(tp1, tp2, tp3); + static_assert(std::is_same>::value, "tp4 type mismatch"); + + CHECK_EQUAL(etl::get<0>(tp4), etl::get<0>(tp1)); + CHECK_EQUAL(etl::get<1>(tp4), etl::get<1>(tp1)); + CHECK_EQUAL(etl::get<2>(tp4), etl::get<0>(tp2)); + CHECK_EQUAL(etl::get<3>(tp4), etl::get<1>(tp2)); + CHECK_EQUAL(etl::get<4>(tp4), etl::get<0>(tp3)); + CHECK_EQUAL(etl::get<5>(tp4), etl::get<1>(tp3)); + } + + //************************************************************************* + TEST(test_tuple_cat_4) + { + etl::tuple tp1{1, 2.3}; + etl::tuple tp2{4, Data("Data", 5)}; + etl::tuple tp3{true, 5}; + etl::tuple tp4{1.01, 6, false}; + + auto tp5 = etl::tuple_cat(tp1, tp2, tp3, tp4); + static_assert(std::is_same>::value, "tp5 type mismatch"); + + CHECK_EQUAL(etl::get<0>(tp5), etl::get<0>(tp1)); + CHECK_EQUAL(etl::get<1>(tp5), etl::get<1>(tp1)); + CHECK_EQUAL(etl::get<2>(tp5), etl::get<0>(tp2)); + CHECK_EQUAL(etl::get<3>(tp5), etl::get<1>(tp2)); + CHECK_EQUAL(etl::get<4>(tp5), etl::get<0>(tp3)); + CHECK_EQUAL(etl::get<5>(tp5), etl::get<1>(tp3)); + CHECK_EQUAL(etl::get<6>(tp5), etl::get<0>(tp4)); + CHECK_EQUAL(etl::get<7>(tp5), etl::get<1>(tp4)); + CHECK_EQUAL(etl::get<8>(tp5), etl::get<2>(tp4)); + } + //************************************************************************* TEST(test_forward_as_tuple) { @@ -831,7 +922,7 @@ namespace etl::tuple tp(0, Data("")); - tp = static_cast&&>(p); + tp = etl::move(p); int i = etl::get<0>(tp); Data d = etl::get<1>(tp); From d5fc8d0cd16d32d1cdf3354a8fd4ccd96929a238 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 25 Mar 2026 21:48:41 +0100 Subject: [PATCH 26/31] Add code coverage report (#1357) * Print test names at test time (#1343) * Add code coverage report --- .github/workflows/coverage.yml | 72 ++++++++++++++++++++++++++++++ test/CMakeLists.txt | 11 ++++- test/run-coverage.sh | 81 ++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/coverage.yml create mode 100755 test/run-coverage.sh diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 00000000..79f9553e --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,72 @@ +name: coverage + +on: + push: + branches: [ master, pull-request/* ] + pull_request: + branches: [ master, pull-request/* ] + types: [opened, synchronize, reopened] + +# Allow only one concurrent deployment to GitHub Pages +concurrency: + group: coverage-${{ github.ref }} + cancel-in-progress: true + +# Grant GITHUB_TOKEN the minimum permissions needed at the workflow level +permissions: + contents: read + +jobs: + + coverage: + name: Generate Coverage Report + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y lcov llvm gcc g++ clang cmake + + - name: Build, test, and collect coverage + run: | + cd test + ./run-coverage.sh + + - name: Upload coverage report artifact + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: test/build-coverage/coverage/ + retention-days: 30 + + - name: Upload Pages artifact + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + uses: actions/upload-pages-artifact@v3 + with: + path: test/build-coverage/coverage/ + + deploy-pages: + name: Deploy to GitHub Pages + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + needs: coverage + runs-on: ubuntu-22.04 + + permissions: + pages: write + id-token: write + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + +# GitHub Repository settings +# -> Settings -> Pages +# -> Source: gh actions diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2bec7847..ddc22d82 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -380,6 +380,10 @@ target_compile_definitions(etl_tests PRIVATE -DETL_DEBUG) option(ETL_NO_STL "No STL" OFF) +set(EXTRA_COMPILE_OPTIONS "" CACHE STRING "Additional compiler options") +set(EXTRA_LINK_OPTIONS "" CACHE STRING "Additional linker options") +set(EXTRA_LINK_LIBS "" CACHE STRING "Additional libraries to link") + if (ETL_CXX_STANDARD MATCHES "98") message(STATUS "Compiling for C++98") set_property(TARGET etl_tests PROPERTY CXX_STANDARD 98) @@ -477,7 +481,9 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") -Wnull-dereference -Wextra-semi -g + ${EXTRA_COMPILE_OPTIONS} ) + target_link_options(etl_tests PRIVATE ${EXTRA_LINK_OPTIONS}) endif () if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -496,7 +502,9 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") -Wextra-semi-stmt -Wc++11-extra-semi -g + ${EXTRA_COMPILE_OPTIONS} ) + target_link_options(etl_tests PRIVATE ${EXTRA_LINK_OPTIONS}) endif () if ((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) @@ -522,7 +530,7 @@ target_include_directories(etl_tests ${PROJECT_SOURCE_DIR}/../include) add_subdirectory(UnitTest++) -target_link_libraries(etl_tests PRIVATE UnitTestpp) +target_link_libraries(etl_tests PRIVATE UnitTestpp ${EXTRA_LINK_LIBS}) # Enable the 'make test' CMake target using the executable defined above add_test(etl_unit_tests etl_tests) @@ -531,4 +539,3 @@ add_test(etl_unit_tests etl_tests) # define a target that will output all of the failing or passing tests # as they appear from UnitTest++ add_custom_target(test_verbose COMMAND ${CMAKE_CTEST_COMMAND} --verbose) - diff --git a/test/run-coverage.sh b/test/run-coverage.sh new file mode 100755 index 00000000..ef977f17 --- /dev/null +++ b/test/run-coverage.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# +# run-coverage.sh [gcc|clang] +# +# defaults: +# compiler: clang +# + +set -e + +export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc) + +# Choose gcc or clang via cmdline +if [ "$1" = "gcc" ] ; then + COMPILER=gcc +else + # default + COMPILER=clang +fi + +if [ "$COMPILER" = "gcc" ]; then + C_COMPILER=gcc + CXX_COMPILER=g++ + GCOV="$(command -v gcov)" || { echo "gcov not found in PATH"; exit 1; } + GCOV_ADD="" + EXTRA_LINK_OPTIONS="" + EXTRA_LINK_LIBS="gcov" +elif [ "$COMPILER" = "clang" ]; then + C_COMPILER=clang + CXX_COMPILER=clang++ + GCOV="$(command -v llvm-cov)" || { echo "llvm-cov not found in PATH"; exit 1; } + GCOV_ADD="--gcov-tool gcov" + EXTRA_LINK_OPTIONS="--coverage" + EXTRA_LINK_LIBS="" +else + echo "Unsupported compiler: $COMPILER" + exit 1 +fi + +BUILD=build-coverage + +rm -rf -- "${BUILD:?}" +mkdir -p "$BUILD" +cd "$BUILD" || exit 1 +touch total.info + +for CXXSTD in 11 14 17 20 23; do + for NOSTL in OFF ON; do + rm -rf CMakeFiles + cmake -DEXTRA_COMPILE_OPTIONS="--coverage" \ + -DEXTRA_LINK_OPTIONS="$EXTRA_LINK_OPTIONS" \ + -DEXTRA_LINK_LIBS="$EXTRA_LINK_LIBS" \ + -DCMAKE_C_COMPILER=$C_COMPILER \ + -DCMAKE_CXX_COMPILER=$CXX_COMPILER \ + -DNO_STL=$NOSTL \ + -DETL_USE_TYPE_TRAITS_BUILTINS=OFF \ + -DETL_USER_DEFINED_TYPE_TRAITS=OFF \ + -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=OFF \ + -DETL_OPTIMISATION=-O0 \ + -DETL_CXX_STANDARD=$CXXSTD \ + -DETL_ENABLE_SANITIZER=Off \ + -DETL_MESSAGES_ARE_NOT_VIRTUAL=OFF \ + -DETL_USE_BUILTIN_MEM_FUNCTIONS=ON .. + cmake --build . + ./etl_tests + lcov --gcov-tool "$GCOV" $GCOV_ADD --capture --directory CMakeFiles/etl_tests.dir \ + --rc "geninfo_unexecuted_blocks=1" --output-file coverage.info --include '*/include/etl/*' --rc "lcov_branch_coverage=1" \ + --ignore-errors inconsistent \ + --ignore-errors mismatch + + lcov -a total.info -a coverage.info -o total.info --rc "lcov_branch_coverage=1" \ + --ignore-errors inconsistent \ + --ignore-errors corrupt \ + --ignore-errors empty + done +done + +genhtml total.info --output-directory coverage --rc "genhtml_branch_coverage=1" --branch-coverage -t $COMPILER \ + --ignore-errors inconsistent + +cd .. From 2f6a3e04aa8e043054f29502c5dcc52669f952cb Mon Sep 17 00:00:00 2001 From: Timon Zijnge <47081647+tzijnge@users.noreply.github.com> Date: Thu, 26 Mar 2026 08:56:50 +0100 Subject: [PATCH 27/31] Manchester big endian support (#1353) * manchester * Added manchester code and test * manchester * Formatting and added missing file * manchester * Some functions can only be constexpr since C++14 * manchester * Manchester decode and some refactoring * manchester * Added some missing typenames * manchester * constexpr void function not allowed in C++11 * manchester * condition on static_assert tests * manchester * revert CMakeLists.txt * Using ETL_STATIC_ASSERT * Some cleanup * manchester * Added static_assert message * manchester * Added compile time tests * manchester * Added invert manchester * Some refactoring * manchester * Disable test for now * Move ETL_NODISCARD before static * manchester * Test for valid_span * manchester * Remove redundant (?) storage specifiers for template specializations. Storage specifier already given in base template * manchester * refactoring to get rid of specialized template functions in template class * manchester * cleanup * manchester * Added documentation comments * Some refactoring * manchester * introducing namespace detail_manchester * manchester * Some refactoring * Update tests * manchester * Some refactoring * Removed possible undefined behavior by refactoring encode_span * constexpr version of encode_span * Static assertion for rare case where code doesn't work because CHAR_BIT is not the same as the number of bits in uint_least8_t * manchester * renamed valid to is_valid * manchester * renamed is_valid_span to is_valid * Using etl exceptions in ETL_ASSERT * manchester * Removed _fast functions * merged encode_in_place with encode and decode_in_place with decode * removed _span to create normal overloads of encode and decode for span * Some renaming and minor refactoring * manchester * Fix build issues * manchester * Conditionally compile manchester_decoded * Update test_manchester.cpp Removed redundant semicolon * #1258 Manchester coding * Formatting * consistency: hex literals with lower case 0x * #1258 Manchester coding * Moved copyright to top of file * Make constexpr encode/decode span functions equal for little and big endian platforms * #1258 Manchester coding * Added missing include * Added missing 8bit/64bit guards * Fixed is_valid for big endian platforms * #1258 Manchester coding * private memcpy alias * #1258 Manchester coding * Review comments * #1258 Manchester coding * Cleanup * Fix build error * #1258 Manchester coding * Add manchester documentation * #1258 Manchester coding * Preparation for GitHub pages * #1324 Manchester documentation * Some small fixes * Print test names at test time (#1343) * IGN-280 biphasic amplitude as float * Add big-endian devcontainer * manchester * fixed the configuration to work with GitHub Codespaces. The changes use cross-compilation with QEMU emulation instead of trying to use a native s390x container. * manchester * Made manchester work for big-endian * Some updates to the container * Manchester big-endian support * Cleanup * Manchester big-endian support * add sourcedirectory * Enable running with ctest * Manchester big-endian support * Update documentation * Manchester big-endian support * QA * Manchester big-endian support * QA * Enable testing with ctest with cross-compiler (#5) * Enable testing with ctest and with cross-compiler * Clean up includes in manchester.h --------- Co-authored-by: Timon Zijnge --------- Co-authored-by: Timon Zijnge Co-authored-by: Roland Reichwein --- .devcontainer/s390x/Dockerfile | 40 ++++++ .devcontainer/s390x/devcontainer.json | 29 +++++ .devcontainer/s390x/toolchain-s390x.cmake | 21 +++ docs/manchester.md | 3 +- include/etl/manchester.h | 151 ++++++---------------- test/CMakeLists.txt | 3 +- test/test_manchester.cpp | 102 ++++++++++++--- 7 files changed, 219 insertions(+), 130 deletions(-) create mode 100644 .devcontainer/s390x/Dockerfile create mode 100644 .devcontainer/s390x/devcontainer.json create mode 100644 .devcontainer/s390x/toolchain-s390x.cmake diff --git a/.devcontainer/s390x/Dockerfile b/.devcontainer/s390x/Dockerfile new file mode 100644 index 00000000..21a6289c --- /dev/null +++ b/.devcontainer/s390x/Dockerfile @@ -0,0 +1,40 @@ +# s390x Big-Endian Test Environment for ETL +# Uses QEMU user-mode emulation to run s390x binaries on x64 host +FROM debian:trixie + +# Avoid prompts from apt +ENV DEBIAN_FRONTEND=noninteractive + +# Install QEMU user-mode emulation and s390x cross-compilation tools +RUN dpkg --add-architecture s390x && \ + apt-get update && apt-get install -y --no-install-recommends\ + qemu-user-static \ + qemu-user \ + binfmt-support \ + gcc-s390x-linux-gnu \ + g++-s390x-linux-gnu \ + cmake \ + make \ + ninja-build \ + git \ + wget \ + file \ + libc6:s390x \ + libstdc++6:s390x \ + && rm -rf /var/lib/apt/lists/* + +# Set working directory +WORKDIR /workspaces/etl + +# Verify QEMU and cross-compilation setup +RUN echo "=== Host Architecture ===" && \ + uname -m && \ + echo "" && \ + echo "=== s390x Cross Compiler ===" && \ + s390x-linux-gnu-gcc --version && \ + echo "" && \ + echo "=== QEMU s390x ===" && \ + qemu-s390x-static --version | head -n1 + +# Default command +CMD ["/bin/bash"] diff --git a/.devcontainer/s390x/devcontainer.json b/.devcontainer/s390x/devcontainer.json new file mode 100644 index 00000000..8b7f8235 --- /dev/null +++ b/.devcontainer/s390x/devcontainer.json @@ -0,0 +1,29 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/cpp +{ + "name": "s390x Big Endian (Debian)", + "build": { + "dockerfile": "./Dockerfile", + "context": "." + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools" + ], + "settings": { + "cmake.sourceDirectory": "${workspaceFolder}/test", + "cmake.configureArgs": [ + "-DCMAKE_TOOLCHAIN_FILE=${workspaceFolder}/.devcontainer/s390x/toolchain-s390x.cmake", + "-DBUILD_TESTS=ON", + "-DNO_STL=OFF", + "-DETL_CXX_STANDARD=17" + ], + "cmake.buildDirectory": "${workspaceFolder}/build-s390x", + "cmake.generator": "Ninja" + } + } + }, + "remoteUser": "root" +} diff --git a/.devcontainer/s390x/toolchain-s390x.cmake b/.devcontainer/s390x/toolchain-s390x.cmake new file mode 100644 index 00000000..706884d9 --- /dev/null +++ b/.devcontainer/s390x/toolchain-s390x.cmake @@ -0,0 +1,21 @@ +# CMake toolchain file for s390x cross-compilation +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR s390x) + +# Specify the cross compiler +set(CMAKE_C_COMPILER s390x-linux-gnu-gcc) +set(CMAKE_CXX_COMPILER s390x-linux-gnu-g++) +set(CMAKE_AR s390x-linux-gnu-ar) +set(CMAKE_RANLIB s390x-linux-gnu-ranlib) +set(CMAKE_STRIP s390x-linux-gnu-strip) + +# Search for programs in the build host directories +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# For libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Set QEMU for running tests +set(CMAKE_CROSSCOMPILING_EMULATOR /usr/bin/qemu-s390x-static CACHE FILEPATH "Path to the emulator for cross-compiled binaries") diff --git a/docs/manchester.md b/docs/manchester.md index 06c0cf7d..d73cb958 100644 --- a/docs/manchester.md +++ b/docs/manchester.md @@ -13,8 +13,9 @@ Efficient Manchester encoding and decoding of data. The Manchester code represen - Normal and inverted Manchester encoding - Support for multiple encoding chunk sizes: 8-bit, 16-bit and 32-bit - Span-based operations or chunk-based operations -- Constexpr functions for compile-time encoding/decoding (8-bit chunk size only) +- Constexpr functions for compile-time encoding/decoding - Validation of encoded data +- Chunked span I/O uses little-endian byte order for multi-byte chunks, independent of host platform endianness ## Algorithm background diff --git a/include/etl/manchester.h b/include/etl/manchester.h index 4c35deb8..fd6e83b4 100644 --- a/include/etl/manchester.h +++ b/include/etl/manchester.h @@ -32,10 +32,8 @@ SOFTWARE. #define ETL_MANCHESTER_INCLUDED #include "platform.h" -#include "endianness.h" #include "span.h" #include "static_assert.h" -#include ///\defgroup manchester manchester /// Manchester encoding and decoding @@ -172,18 +170,37 @@ namespace etl }; //************************************************************************* - /// Alias for memcpy. etl::mem_copy is not suitable for the Manchester - /// algorithm because all memory copies are between different types. This - /// alias is a way to respect ETL_USING_BUILTIN_MEMCPY while using the - /// memcpy function signature + /// Read a multi-byte value from a span in little-endian byte order. + ///\tparam T The type to read. + ///\param bytes The span to read from. + ///\param index The starting index in the span. + ///\return The value read from the span. //************************************************************************* - inline void* memcpy(void* dest, const void* src, std::size_t count) ETL_NOEXCEPT + template + static ETL_CONSTEXPR14 T read_little_endian(etl::span bytes, size_t index) { -#if ETL_USING_BUILTIN_MEMCPY - return __builtin_memcpy(dest, src, count); -#else - return ::memcpy(dest, src, count); -#endif + T value = 0; + for (size_t j = 0; j < sizeof(T); ++j) + { + value |= static_cast(bytes[index + j]) << (j * CHAR_BIT); + } + return value; + } + + //************************************************************************* + /// Write a multi-byte value to a span in little-endian byte order. + ///\tparam T The type to write. + ///\param bytes The span to write to. + ///\param index The starting index in the span. + ///\param value The value to write. + //************************************************************************* + template + static ETL_CONSTEXPR14 void write_little_endian(etl::span bytes, size_t index, T value) + { + for (size_t j = 0; j < sizeof(T); ++j) + { + bytes[index + j] = static_cast(value >> (j * CHAR_BIT)); + } } } // namespace private_manchester @@ -297,44 +314,13 @@ namespace etl #endif //************************************************************************* - /// Encode a span of data with the selected chunk size. - ///\param source The source data to encode. - ///\param destination The destination buffer for encoded data. - ///\tparam TChunk The chunk size for encoding (default: uint_least8_t). - //************************************************************************* - template - static typename etl::enable_if::value, void>::type - encode(etl::span decoded, etl::span encoded) - { - typedef TChunk TDecoded; - typedef typename etl::private_manchester::encoded::type TEncoded; - - ETL_ASSERT(encoded.size() >= decoded.size() * 2, ETL_ERROR(manchester_invalid_size)); - ETL_ASSERT(decoded.size() % sizeof(TDecoded) == 0, ETL_ERROR(manchester_invalid_size)); - - size_t dest_index = 0; - size_t source_index = 0; - for (size_t i = 0; i < decoded.size() / sizeof(TDecoded); ++i) - { - TDecoded decoded_value = 0; - etl::private_manchester::memcpy(&decoded_value, &decoded[source_index], sizeof(TDecoded)); - const TEncoded encoded_value = encode(decoded_value); - etl::private_manchester::memcpy(&encoded[dest_index], &encoded_value, sizeof(TEncoded)); - - source_index += sizeof(TDecoded); - dest_index += sizeof(TEncoded); - } - } - - //************************************************************************* - /// Encode a span of data with the minimum chunk size. This version is - /// constexpr so that it can be used to encode data at compile time. + /// Encode a span of data with the specified chunk size. ///\param source The source data to encode. ///\param destination The destination buffer for encoded data. ///\tparam TChunk The chunk size for encoding (default: uint_least8_t). //************************************************************************* template - static ETL_CONSTEXPR14 typename etl::enable_if::value, void>::type encode(etl::span decoded, etl::span encoded) + static ETL_CONSTEXPR14 void encode(etl::span decoded, etl::span encoded) { typedef TChunk TDecoded; typedef typename etl::private_manchester::encoded::type TEncoded; @@ -346,17 +332,9 @@ namespace etl size_t source_index = 0; for (size_t i = 0; i < decoded.size() / sizeof(TDecoded); ++i) { - const TEncoded encoded_value = encode(decoded[source_index]); - if (etl::endianness::value() == etl::endian::little) - { - encoded[dest_index] = static_cast(encoded_value); - encoded[dest_index + 1] = static_cast(encoded_value >> CHAR_BIT); - } - else - { - encoded[dest_index] = static_cast(encoded_value >> CHAR_BIT); - encoded[dest_index + 1] = static_cast(encoded_value); - } + const TDecoded decoded_value = private_manchester::read_little_endian(decoded, source_index); + const TEncoded encoded_value = encode(decoded_value); + private_manchester::write_little_endian(encoded, dest_index, encoded_value); source_index += sizeof(TDecoded); dest_index += sizeof(TEncoded); @@ -426,14 +404,13 @@ namespace etl #endif //************************************************************************* - /// Decode a span of data using specified chunk type. + /// Decode a span of data using the specified chunk type. ///\param source The source encoded data to decode. ///\param destination The destination buffer for decoded data. - ///\tparam TChunk The chunk type for decoding. + ///\tparam TChunk The chunk type for decoding (default: uint16_t). //************************************************************************* - template - static typename etl::enable_if::type>::value, void>::type - decode(etl::span encoded, etl::span decoded) + template ::type> + static ETL_CONSTEXPR14 void decode(etl::span encoded, etl::span decoded) { typedef typename private_manchester::decoded::type TDecoded; typedef TChunk TEncoded; @@ -445,53 +422,15 @@ namespace etl size_t source_index = 0; for (size_t i = 0; i < encoded.size() / sizeof(TEncoded); ++i) { - TChunk encoded_value = 0; - etl::private_manchester::memcpy(&encoded_value, &encoded[source_index], sizeof(TEncoded)); + const TEncoded encoded_value = private_manchester::read_little_endian(encoded, source_index); const TDecoded decoded_value = decode(encoded_value); - etl::private_manchester::memcpy(&decoded[dest_index], &decoded_value, sizeof(TDecoded)); + private_manchester::write_little_endian(decoded, dest_index, decoded_value); source_index += sizeof(TEncoded); dest_index += sizeof(TDecoded); } } - //************************************************************************* - /// Decode a span of data using the smallest chunk type. This version is - /// constexpr so that it can be used to decode data at compile time. - ///\param source The source encoded data to decode. - ///\param destination The destination buffer for decoded data. - ///\tparam TChunk The chunk type for decoding (default type). - //************************************************************************* - template ::type> - static ETL_CONSTEXPR14 typename etl::enable_if::type>::value, void>::type - decode(etl::span encoded, etl::span decoded) - { - typedef uint_least8_t TDecoded; - - ETL_ASSERT(decoded.size() * 2 >= encoded.size(), ETL_ERROR(manchester_invalid_size)); - ETL_ASSERT(encoded.size() % sizeof(TChunk) == 0, ETL_ERROR(manchester_invalid_size)); - - size_t dest_index = 0; - size_t source_index = 0; - for (size_t i = 0; i < encoded.size() / sizeof(TChunk); ++i) - { - TChunk encoded_value{}; - if (etl::endianness::value() == etl::endian::little) - { - encoded_value = static_cast((encoded[source_index + 1] << CHAR_BIT) | encoded[source_index]); - } - else - { - encoded_value = static_cast((encoded[source_index] << CHAR_BIT) | encoded[source_index + 1]); - } - - decoded[dest_index] = decode(encoded_value); - - source_index += sizeof(TChunk); - dest_index += sizeof(TDecoded); - } - } - //************************************************************************* // Validation functions //************************************************************************* @@ -521,15 +460,7 @@ namespace etl for (size_t i = 0; i < encoded.size(); i += sizeof(uint16_t)) { - uint16_t chunk{}; - if (etl::endianness::value() == etl::endian::little) - { - chunk = static_cast((encoded[i + 1] << CHAR_BIT) | encoded[i]); - } - else - { - chunk = static_cast((encoded[i] << CHAR_BIT) | encoded[i + 1]); - } + const uint16_t chunk = private_manchester::read_little_endian(encoded, i); if (!is_valid(chunk)) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ddc22d82..daa3c5c7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -532,8 +532,9 @@ target_include_directories(etl_tests add_subdirectory(UnitTest++) target_link_libraries(etl_tests PRIVATE UnitTestpp ${EXTRA_LINK_LIBS}) +enable_testing() # Enable the 'make test' CMake target using the executable defined above -add_test(etl_unit_tests etl_tests) +add_test(NAME etl_unit_tests COMMAND etl_tests) # Since ctest will only show you the results of the single executable # define a target that will output all of the failing or passing tests diff --git a/test/test_manchester.cpp b/test/test_manchester.cpp index 0334712f..bd357435 100644 --- a/test/test_manchester.cpp +++ b/test/test_manchester.cpp @@ -71,12 +71,26 @@ SUITE(test_manchester) } #if ETL_USING_CPP14 - constexpr etl::array manchester_encoded(etl::span decoded) + constexpr etl::array manchester_encoded_uint8(etl::span decoded) { etl::array encoded{0, 0, 0, 0, 0, 0, 0, 0}; etl::manchester::encode(decoded, encoded); return encoded; } + + constexpr etl::array manchester_encoded_uint16(etl::span decoded) + { + etl::array encoded{0, 0, 0, 0, 0, 0, 0, 0}; + etl::manchester::encode(decoded, encoded); + return encoded; + } + + constexpr etl::array manchester_encoded_uint32(etl::span decoded) + { + etl::array encoded{0, 0, 0, 0, 0, 0, 0, 0}; + etl::manchester::encode(decoded, encoded); + return encoded; + } #endif TEST(encode_span) @@ -105,18 +119,41 @@ SUITE(test_manchester) CHECK_TRUE(encoded0 == encoded1); CHECK_TRUE(encoded0 == encoded2); CHECK_TRUE(encoded0 == encoded3); + } #if ETL_USING_CPP14 - static_assert(manchester_encoded(decoded)[0] == 0xAA, "Compile time encoding on range failed"); - static_assert(manchester_encoded(decoded)[1] == 0xAA, "Compile time encoding on range failed"); - static_assert(manchester_encoded(decoded)[2] == 0x55, "Compile time encoding on range failed"); - static_assert(manchester_encoded(decoded)[3] == 0x55, "Compile time encoding on range failed"); - static_assert(manchester_encoded(decoded)[4] == 0xA9, "Compile time encoding on range failed"); - static_assert(manchester_encoded(decoded)[5] == 0xAA, "Compile time encoding on range failed"); - static_assert(manchester_encoded(decoded)[6] == 0xAA, "Compile time encoding on range failed"); - static_assert(manchester_encoded(decoded)[7] == 0x6A, "Compile time encoding on range failed"); -#endif + TEST(encode_span_constexpr) + { + constexpr etl::array decoded{0x00, 0xFF, 0x01, 0x80}; + + static_assert(manchester_encoded_uint8(decoded)[0] == 0xAA, "Compile time encoding with uint8_t failed"); + static_assert(manchester_encoded_uint8(decoded)[1] == 0xAA, "Compile time encoding with uint8_t failed"); + static_assert(manchester_encoded_uint8(decoded)[2] == 0x55, "Compile time encoding with uint8_t failed"); + static_assert(manchester_encoded_uint8(decoded)[3] == 0x55, "Compile time encoding with uint8_t failed"); + static_assert(manchester_encoded_uint8(decoded)[4] == 0xA9, "Compile time encoding with uint8_t failed"); + static_assert(manchester_encoded_uint8(decoded)[5] == 0xAA, "Compile time encoding with uint8_t failed"); + static_assert(manchester_encoded_uint8(decoded)[6] == 0xAA, "Compile time encoding with uint8_t failed"); + static_assert(manchester_encoded_uint8(decoded)[7] == 0x6A, "Compile time encoding with uint8_t failed"); + + static_assert(manchester_encoded_uint16(decoded)[0] == 0xAA, "Compile time encoding with uint16_t failed"); + static_assert(manchester_encoded_uint16(decoded)[1] == 0xAA, "Compile time encoding with uint16_t failed"); + static_assert(manchester_encoded_uint16(decoded)[2] == 0x55, "Compile time encoding with uint16_t failed"); + static_assert(manchester_encoded_uint16(decoded)[3] == 0x55, "Compile time encoding with uint16_t failed"); + static_assert(manchester_encoded_uint16(decoded)[4] == 0xA9, "Compile time encoding with uint16_t failed"); + static_assert(manchester_encoded_uint16(decoded)[5] == 0xAA, "Compile time encoding with uint16_t failed"); + static_assert(manchester_encoded_uint16(decoded)[6] == 0xAA, "Compile time encoding with uint16_t failed"); + static_assert(manchester_encoded_uint16(decoded)[7] == 0x6A, "Compile time encoding with uint16_t failed"); + + static_assert(manchester_encoded_uint32(decoded)[0] == 0xAA, "Compile time encoding with uint32_t failed"); + static_assert(manchester_encoded_uint32(decoded)[1] == 0xAA, "Compile time encoding with uint32_t failed"); + static_assert(manchester_encoded_uint32(decoded)[2] == 0x55, "Compile time encoding with uint32_t failed"); + static_assert(manchester_encoded_uint32(decoded)[3] == 0x55, "Compile time encoding with uint32_t failed"); + static_assert(manchester_encoded_uint32(decoded)[4] == 0xA9, "Compile time encoding with uint32_t failed"); + static_assert(manchester_encoded_uint32(decoded)[5] == 0xAA, "Compile time encoding with uint32_t failed"); + static_assert(manchester_encoded_uint32(decoded)[6] == 0xAA, "Compile time encoding with uint32_t failed"); + static_assert(manchester_encoded_uint32(decoded)[7] == 0x6A, "Compile time encoding with uint32_t failed"); } +#endif TEST(encode_span_inverted) { @@ -233,12 +270,26 @@ SUITE(test_manchester) } #if ETL_USING_CPP14 - constexpr etl::array manchester_decoded(etl::span encoded) + constexpr etl::array manchester_decoded_uint16(etl::span encoded) { etl::array decoded{0, 0, 0, 0}; etl::manchester::decode(encoded, decoded); return decoded; } + + constexpr etl::array manchester_decoded_uint32(etl::span encoded) + { + etl::array decoded{0, 0, 0, 0}; + etl::manchester::decode(encoded, decoded); + return decoded; + } + + constexpr etl::array manchester_decoded_uint64(etl::span encoded) + { + etl::array decoded{0, 0, 0, 0}; + etl::manchester::decode(encoded, decoded); + return decoded; + } #endif TEST(decode_span) @@ -260,18 +311,33 @@ SUITE(test_manchester) CHECK_EQUAL(0x01, decoded0[2]); CHECK_EQUAL(0x80, decoded0[3]); -#if ETL_USING_CPP14 - static_assert(manchester_decoded(encoded)[0] == 0x00, "Compile time decoding on range failed"); - static_assert(manchester_decoded(encoded)[1] == 0xFF, "Compile time decoding on range failed"); - static_assert(manchester_decoded(encoded)[2] == 0x01, "Compile time decoding on range failed"); - static_assert(manchester_decoded(encoded)[3] == 0x80, "Compile time decoding on range failed"); -#endif - CHECK_TRUE(decoded0 == decoded1); CHECK_TRUE(decoded0 == decoded2); CHECK_TRUE(decoded0 == decoded3); } +#if ETL_USING_CPP14 + TEST(decode_span_constexpr) + { + constexpr etl::array encoded{0xAA, 0xAA, 0x55, 0x55, 0xA9, 0xAA, 0xAA, 0x6A}; + + static_assert(manchester_decoded_uint16(encoded)[0] == 0x00, "Compile time decoding with uint16_t failed"); + static_assert(manchester_decoded_uint16(encoded)[1] == 0xFF, "Compile time decoding with uint16_t failed"); + static_assert(manchester_decoded_uint16(encoded)[2] == 0x01, "Compile time decoding with uint16_t failed"); + static_assert(manchester_decoded_uint16(encoded)[3] == 0x80, "Compile time decoding with uint16_t failed"); + + static_assert(manchester_decoded_uint32(encoded)[0] == 0x00, "Compile time decoding with uint32_t failed"); + static_assert(manchester_decoded_uint32(encoded)[1] == 0xFF, "Compile time decoding with uint32_t failed"); + static_assert(manchester_decoded_uint32(encoded)[2] == 0x01, "Compile time decoding with uint32_t failed"); + static_assert(manchester_decoded_uint32(encoded)[3] == 0x80, "Compile time decoding with uint32_t failed"); + + static_assert(manchester_decoded_uint64(encoded)[0] == 0x00, "Compile time decoding with uint64_t failed"); + static_assert(manchester_decoded_uint64(encoded)[1] == 0xFF, "Compile time decoding with uint64_t failed"); + static_assert(manchester_decoded_uint64(encoded)[2] == 0x01, "Compile time decoding with uint64_t failed"); + static_assert(manchester_decoded_uint64(encoded)[3] == 0x80, "Compile time decoding with uint64_t failed"); + } +#endif + TEST(decode_span_inverted) { etl::array encoded{0x55, 0x55, 0xAA, 0xAA, 0x56, 0x55, 0x55, 0x95}; From add42b6c875420882944d7e946dc22a1ecea9e25 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 26 Mar 2026 09:29:05 +0100 Subject: [PATCH 28/31] Mark uninitialized use from std library (#1349) * Print test names at test time (#1343) * Mark uninitialized use from std library Similar to other cases, adds compiler pragmas against warnings caused by std library in optimized builds of tests --------- Co-authored-by: John Wellbelove --- include/etl/generators/message_packet_generator.h | 2 ++ include/etl/message_packet.h | 2 ++ include/etl/string_utilities.h | 2 ++ test/test_vector.cpp | 8 ++++++++ test/test_vector_non_trivial.cpp | 2 ++ 5 files changed, 16 insertions(+) diff --git a/include/etl/generators/message_packet_generator.h b/include/etl/generators/message_packet_generator.h index 36430509..59f3b7e5 100644 --- a/include/etl/generators/message_packet_generator.h +++ b/include/etl/generators/message_packet_generator.h @@ -375,6 +375,7 @@ namespace etl } #include "private/diagnostic_pop.h" +#include "private/diagnostic_uninitialized_push.h" //******************************************** template bool add_new_message_type(etl::imessage&& msg) @@ -390,6 +391,7 @@ namespace etl return false; } } +#include "private/diagnostic_pop.h" typename etl::aligned_storage::type data; bool valid; diff --git a/include/etl/message_packet.h b/include/etl/message_packet.h index 24f1e157..ef419bc4 100644 --- a/include/etl/message_packet.h +++ b/include/etl/message_packet.h @@ -363,6 +363,7 @@ namespace etl } #include "private/diagnostic_pop.h" +#include "private/diagnostic_uninitialized_push.h" //******************************************** template bool add_new_message_type(etl::imessage&& msg) @@ -378,6 +379,7 @@ namespace etl return false; } } +#include "private/diagnostic_pop.h" typename etl::aligned_storage::type data; bool valid; diff --git a/include/etl/string_utilities.h b/include/etl/string_utilities.h index 64e0365e..9da91c91 100644 --- a/include/etl/string_utilities.h +++ b/include/etl/string_utilities.h @@ -713,6 +713,7 @@ namespace etl //*************************************************************************** /// get_token //*************************************************************************** +#include "private/diagnostic_uninitialized_push.h" template etl::optional get_token(const TInput& input, typename TInput::const_pointer delimiters, const etl::optional& last_view, bool ignore_empty_tokens) { @@ -755,6 +756,7 @@ namespace etl return etl::optional(view); } +#include "private/diagnostic_pop.h" //*************************************************************************** /// get_token_list diff --git a/test/test_vector.cpp b/test/test_vector.cpp index c1e679a4..b99b11c7 100644 --- a/test/test_vector.cpp +++ b/test/test_vector.cpp @@ -513,6 +513,7 @@ namespace } //************************************************************************* +#include "etl/private/diagnostic_uninitialized_push.h" TEST_FIXTURE(SetupFixture, test_front) { Compare_Data compare_data(initial_data.begin(), initial_data.end()); @@ -523,8 +524,10 @@ namespace Data emptyData; CHECK_THROW(emptyData.front(), etl::vector_out_of_bounds); } +#include "etl/private/diagnostic_pop.h" //************************************************************************* +#include "etl/private/diagnostic_uninitialized_push.h" TEST_FIXTURE(SetupFixture, test_front_const) { const Compare_Data compare_data(initial_data.begin(), initial_data.end()); @@ -535,6 +538,7 @@ namespace const Data emptyData; CHECK_THROW(emptyData.front(), etl::vector_out_of_bounds); } +#include "etl/private/diagnostic_pop.h" //************************************************************************* TEST_FIXTURE(SetupFixture, test_back) @@ -998,6 +1002,7 @@ namespace } //************************************************************************* +#include "etl/private/diagnostic_uninitialized_push.h" TEST_FIXTURE(SetupFixture, test_insert_position_n_value_outofbounds) { const int INITIAL_VALUE = 0; @@ -1007,6 +1012,7 @@ namespace CHECK_THROW(data.insert(data2.end(), 1, INITIAL_VALUE);, etl::vector_out_of_bounds); } +#include "etl/private/diagnostic_pop.h" //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_position_n_value_excess) @@ -1062,6 +1068,7 @@ namespace } //************************************************************************* +#include "etl/private/diagnostic_uninitialized_push.h" TEST_FIXTURE(SetupFixture, test_insert_position_range_out_of_bounds) { Data data; @@ -1069,6 +1076,7 @@ namespace CHECK_THROW(data.insert(data2.end(), insert_data.cbegin(), insert_data.cend());, etl::vector_out_of_bounds); } +#include "etl/private/diagnostic_pop.h" //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_position_range_excess) diff --git a/test/test_vector_non_trivial.cpp b/test/test_vector_non_trivial.cpp index 99772202..e2a4d085 100644 --- a/test/test_vector_non_trivial.cpp +++ b/test/test_vector_non_trivial.cpp @@ -1022,6 +1022,7 @@ namespace } //************************************************************************* +#include "etl/private/diagnostic_array_bounds_push.h" TEST(test_emplace_out_of_range_before_begin) { DataNDC data; @@ -1032,6 +1033,7 @@ namespace CHECK_THROW(data.emplace(it, INITIAL_VALUE), etl::vector_out_of_bounds); } +#include "etl/private/diagnostic_pop.h" //************************************************************************* #include "etl/private/diagnostic_array_bounds_push.h" From 31b87b541989bbd7da8e11c1cc0ce8f477115c8d Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 26 Mar 2026 09:56:17 +0100 Subject: [PATCH 29/31] 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 From 2f242e37f219cb784ffc8ee3ef7da0f8784708c1 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 26 Mar 2026 10:46:20 +0100 Subject: [PATCH 30/31] Restrict etl::atomic for general types (#1359) * Print test names at test time (#1343) * Restrict etl::atomic for general types Needs adding is_copy_assignable and is_move_assignable, and adjustments to is_trivially_copyable and is_assignable * Resolve mutable T value vs. volatile qualified methods * Remove volatile method overloads They are deprecated in C++20 because they don't work as users expect anyway. MSVC hinted for this. --------- Co-authored-by: John Wellbelove --- include/etl/atomic/atomic_gcc_sync.h | 193 ++---------------- .../etl/generators/type_traits_generator.h | 105 +++++++++- include/etl/type_traits.h | 105 +++++++++- test/test_atomic.cpp | 124 +++++++++++ test/test_type_traits.cpp | 60 ++++++ 5 files changed, 406 insertions(+), 181 deletions(-) diff --git a/include/etl/atomic/atomic_gcc_sync.h b/include/etl/atomic/atomic_gcc_sync.h index de7b8d5a..fab54974 100644 --- a/include/etl/atomic/atomic_gcc_sync.h +++ b/include/etl/atomic/atomic_gcc_sync.h @@ -807,6 +807,13 @@ namespace etl { public: + ETL_STATIC_ASSERT((etl::is_trivially_copyable::value), "atomic requires that T is trivially copyable"); + ETL_STATIC_ASSERT((etl::is_copy_constructible::value), "atomic requires that T is copy constructible"); + ETL_STATIC_ASSERT((etl::is_copy_assignable::value), "atomic requires that T is copy assignable"); + ETL_STATIC_ASSERT((etl::is_move_constructible::value), "atomic requires that T is move constructible"); + ETL_STATIC_ASSERT((etl::is_move_assignable::value), "atomic requires that T is move assignable"); + ETL_STATIC_ASSERT((etl::is_same::type>::value), "atomic requires that T is not const or volatile"); + atomic() : flag(0) , value(T()) @@ -827,13 +834,6 @@ namespace etl return v; } - T operator =(T v) volatile - { - store(v); - - return v; - } - // Conversion operator operator T () const { @@ -844,26 +844,12 @@ namespace etl return result; } - operator T() volatile const - { - ETL_BUILTIN_LOCK; - T result = value; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Is lock free? bool is_lock_free() const { return false; } - bool is_lock_free() const volatile - { - return false; - } - // Store void store(T v, etl::memory_order order = etl::memory_order_seq_cst) { @@ -873,25 +859,6 @@ namespace etl ETL_BUILTIN_UNLOCK; } - void store(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - (void)order; - ETL_BUILTIN_LOCK; - value = v; - ETL_BUILTIN_UNLOCK; - } - - // Load - T load(etl::memory_order order = etl::memory_order_seq_cst) const volatile - { - (void)order; - ETL_BUILTIN_LOCK; - T result = value; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Load T load(etl::memory_order order = etl::memory_order_seq_cst) const { @@ -915,17 +882,6 @@ namespace etl return result; } - T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - (void)order; - ETL_BUILTIN_LOCK; - T result = value; - value = v; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Compare exchange weak bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) { @@ -947,26 +903,6 @@ namespace etl return result; } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - bool result; - - (void)order; - ETL_BUILTIN_LOCK; - if (memcmp(&value, &expected, sizeof(T)) == 0) - { - value = desired; - result = true; - } - else - { - result = false; - } - ETL_BUILTIN_UNLOCK; - - return result; - } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) { (void)success; @@ -974,13 +910,6 @@ namespace etl return compare_exchange_weak(expected, desired); } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile - { - (void)success; - (void)failure; - return compare_exchange_weak(expected, desired); - } - // Compare exchange strong bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) { @@ -988,12 +917,6 @@ namespace etl return compare_exchange_weak(expected, desired); } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - (void)order; - return compare_exchange_weak(expected, desired); - } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) { (void)success; @@ -1001,15 +924,11 @@ namespace etl return compare_exchange_weak(expected, desired); } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile - { - (void)success; - (void)failure; - return compare_exchange_weak(expected, desired); - } - private: + atomic& operator =(const atomic&) ETL_DELETE; + atomic& operator =(const atomic&) volatile ETL_DELETE; + mutable char flag; mutable T value; }; @@ -1995,6 +1914,13 @@ namespace etl { public: + ETL_STATIC_ASSERT((etl::is_trivially_copyable::value), "atomic requires that T is trivially copyable"); + ETL_STATIC_ASSERT((etl::is_copy_constructible::value), "atomic requires that T is copy constructible"); + ETL_STATIC_ASSERT((etl::is_copy_assignable::value), "atomic requires that T is copy assignable"); + ETL_STATIC_ASSERT((etl::is_move_constructible::value), "atomic requires that T is move constructible"); + ETL_STATIC_ASSERT((etl::is_move_assignable::value), "atomic requires that T is move assignable"); + ETL_STATIC_ASSERT((etl::is_same::type>::value), "atomic requires that T is not const or volatile"); + atomic() : flag(0) , value(T()) @@ -2015,13 +1941,6 @@ namespace etl return v; } - T operator =(T v) volatile - { - store(v); - - return v; - } - // Conversion operator operator T () const { @@ -2032,26 +1951,12 @@ namespace etl return result; } - operator T() volatile const - { - ETL_BUILTIN_LOCK; - T result = value; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Is lock free? bool is_lock_free() const { return false; } - bool is_lock_free() const volatile - { - return false; - } - // Store void store(T v, etl::memory_order order = etl::memory_order_seq_cst) { @@ -2060,23 +1965,6 @@ namespace etl ETL_BUILTIN_UNLOCK; } - void store(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - ETL_BUILTIN_LOCK; - value = v; - ETL_BUILTIN_UNLOCK; - } - - // Load - T load(etl::memory_order order = etl::memory_order_seq_cst) const volatile - { - ETL_BUILTIN_LOCK; - T result = value; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Load T load(etl::memory_order order = etl::memory_order_seq_cst) const { @@ -2098,16 +1986,6 @@ namespace etl return result; } - T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - ETL_BUILTIN_LOCK; - T result = value; - value = v; - ETL_BUILTIN_UNLOCK; - - return result; - } - // Compare exchange weak bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) { @@ -2128,58 +2006,27 @@ namespace etl return result; } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - bool result; - - ETL_BUILTIN_LOCK; - if (memcmp(&value, &expected, sizeof(T)) == 0) - { - value = desired; - result = true; - } - else - { - result = false; - } - ETL_BUILTIN_UNLOCK; - - return result; - } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) { return compare_exchange_weak(expected, desired); } - bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile - { - return compare_exchange_weak(expected, desired); - } - // Compare exchange strong bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) { return compare_exchange_weak(expected, desired); } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile - { - return compare_exchange_weak(expected, desired); - } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) { return compare_exchange_weak(expected, desired); } - bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile - { - return compare_exchange_weak(expected, desired); - } - private: + atomic& operator =(const atomic&) ETL_DELETE; + atomic& operator =(const atomic&) volatile ETL_DELETE; + mutable char flag; mutable T value; }; diff --git a/include/etl/generators/type_traits_generator.h b/include/etl/generators/type_traits_generator.h index 373b5d0e..843ce343 100644 --- a/include/etl/generators/type_traits_generator.h +++ b/include/etl/generators/type_traits_generator.h @@ -1929,6 +1929,16 @@ typedef integral_constant true_type; template using is_move_constructible = std::is_move_constructible; + //********************************************* + // is_copy_assignable + template + using is_copy_assignable = std::is_copy_assignable; + + //********************************************* + // is_move_assignable + template + using is_move_assignable = std::is_move_assignable; + //********************************************* // is_trivially_constructible #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED @@ -1974,6 +1984,9 @@ typedef integral_constant true_type; #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED template using is_trivially_copyable = std::is_trivially_copyable; +#elif ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + template + using is_trivially_copyable = etl::bool_constant<__is_trivially_copyable(T)>; #else template using is_trivially_copyable = etl::bool_constant::value || etl::is_pointer::value>; @@ -2033,6 +2046,29 @@ typedef integral_constant true_type; { }; + //********************************************* + // is_copy_assignable + template + struct is_copy_assignable : public etl::is_assignable::type, + typename etl::add_lvalue_reference::type> + { + }; + + //********************************************* + // is_move_assignable +#if ETL_USING_CPP11 + template + struct is_move_assignable : public etl::is_assignable::type, + typename etl::add_rvalue_reference::type> + { + }; +#else + template + struct is_move_assignable : public etl::is_assignable::type, T> + { + }; +#endif + #if ETL_USING_CPP11 //********************************************* // is_trivially_constructible @@ -2107,11 +2143,7 @@ typedef integral_constant true_type; template struct is_trivially_copyable { -#if defined(ETL_COMPILER_GCC) - static ETL_CONSTANT bool value = __has_trivial_copy(T); -#else static ETL_CONSTANT bool value = __is_trivially_copyable(T); -#endif }; #elif defined(ETL_USER_DEFINED_TYPE_TRAITS) && !defined(ETL_USE_TYPE_TRAITS_BUILTINS) @@ -2182,6 +2214,32 @@ typedef integral_constant true_type; template struct is_move_constructible; + //********************************************* + // is_copy_assignable + template ::value || etl::is_pointer::value> + struct is_copy_assignable; + + template + struct is_copy_assignable : public etl::true_type + { + }; + + template + struct is_copy_assignable; + + //********************************************* + // is_move_assignable + template ::value || etl::is_pointer::value> + struct is_move_assignable; + + template + struct is_move_assignable : public etl::true_type + { + }; + + template + struct is_move_assignable; + //********************************************* // is_trivially_constructible template ::value || etl::is_pointer::value> @@ -2257,7 +2315,11 @@ typedef integral_constant true_type; //********************************************* // is_assignable template +#if ETL_USING_BUILTIN_IS_ASSIGNABLE + struct is_assignable : public etl::bool_constant<__is_assignable(T1, T2)> +#else struct is_assignable : public etl::bool_constant<(etl::is_arithmetic::value || etl::is_pointer::value) && (etl::is_arithmetic::value || etl::is_pointer::value)> +#endif { }; @@ -2311,6 +2373,31 @@ typedef integral_constant true_type; }; #endif + //********************************************* + // is_copy_assignable + template + struct is_copy_assignable : public etl::is_assignable::type, + typename etl::add_lvalue_reference::type> + { + }; + +#if ETL_USING_CPP11 + //********************************************* + // is_move_assignable + template + struct is_move_assignable : public etl::is_assignable::type, + typename etl::add_rvalue_reference::type> + { + }; +#else + //********************************************* + // is_move_assignable + template + struct is_move_assignable : public etl::bool_constant::value || etl::is_pointer::value> + { + }; +#endif + //********************************************* // is_trivially_constructible template @@ -2342,7 +2429,11 @@ typedef integral_constant true_type; //********************************************* // is_trivially_copyable template +#if ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + struct is_trivially_copyable : public etl::bool_constant<__is_trivially_copyable(T)> +#else struct is_trivially_copyable : public etl::bool_constant::value || etl::is_pointer::value> +#endif { }; @@ -2389,6 +2480,12 @@ typedef integral_constant true_type; template inline constexpr bool is_move_constructible_v = etl::is_move_constructible::value; + template + inline constexpr bool is_copy_assignable_v = etl::is_copy_assignable::value; + + template + inline constexpr bool is_move_assignable_v = etl::is_move_assignable::value; + template inline constexpr bool is_trivially_constructible_v = etl::is_trivially_constructible::value; diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 521aaf48..34462e61 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -1922,6 +1922,16 @@ typedef integral_constant true_type; template using is_move_constructible = std::is_move_constructible; + //********************************************* + // is_copy_assignable + template + using is_copy_assignable = std::is_copy_assignable; + + //********************************************* + // is_move_assignable + template + using is_move_assignable = std::is_move_assignable; + //********************************************* // is_trivially_constructible #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED @@ -1967,6 +1977,9 @@ typedef integral_constant true_type; #if ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED template using is_trivially_copyable = std::is_trivially_copyable; +#elif ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + template + using is_trivially_copyable = etl::bool_constant<__is_trivially_copyable(T)>; #else template using is_trivially_copyable = etl::bool_constant::value || etl::is_pointer::value>; @@ -2026,6 +2039,29 @@ typedef integral_constant true_type; { }; + //********************************************* + // is_copy_assignable + template + struct is_copy_assignable : public etl::is_assignable::type, + typename etl::add_lvalue_reference::type> + { + }; + + //********************************************* + // is_move_assignable +#if ETL_USING_CPP11 + template + struct is_move_assignable : public etl::is_assignable::type, + typename etl::add_rvalue_reference::type> + { + }; +#else + template + struct is_move_assignable : public etl::is_assignable::type, T> + { + }; +#endif + #if ETL_USING_CPP11 //********************************************* // is_trivially_constructible @@ -2100,11 +2136,7 @@ typedef integral_constant true_type; template struct is_trivially_copyable { -#if defined(ETL_COMPILER_GCC) - static ETL_CONSTANT bool value = __has_trivial_copy(T); -#else static ETL_CONSTANT bool value = __is_trivially_copyable(T); -#endif }; #elif defined(ETL_USER_DEFINED_TYPE_TRAITS) && !defined(ETL_USE_TYPE_TRAITS_BUILTINS) @@ -2175,6 +2207,32 @@ typedef integral_constant true_type; template struct is_move_constructible; + //********************************************* + // is_copy_assignable + template ::value || etl::is_pointer::value> + struct is_copy_assignable; + + template + struct is_copy_assignable : public etl::true_type + { + }; + + template + struct is_copy_assignable; + + //********************************************* + // is_move_assignable + template ::value || etl::is_pointer::value> + struct is_move_assignable; + + template + struct is_move_assignable : public etl::true_type + { + }; + + template + struct is_move_assignable; + //********************************************* // is_trivially_constructible template ::value || etl::is_pointer::value> @@ -2250,7 +2308,11 @@ typedef integral_constant true_type; //********************************************* // is_assignable template +#if ETL_USING_BUILTIN_IS_ASSIGNABLE + struct is_assignable : public etl::bool_constant<__is_assignable(T1, T2)> +#else struct is_assignable : public etl::bool_constant<(etl::is_arithmetic::value || etl::is_pointer::value) && (etl::is_arithmetic::value || etl::is_pointer::value)> +#endif { }; @@ -2304,6 +2366,31 @@ typedef integral_constant true_type; }; #endif + //********************************************* + // is_copy_assignable + template + struct is_copy_assignable : public etl::is_assignable::type, + typename etl::add_lvalue_reference::type> + { + }; + +#if ETL_USING_CPP11 + //********************************************* + // is_move_assignable + template + struct is_move_assignable : public etl::is_assignable::type, + typename etl::add_rvalue_reference::type> + { + }; +#else + //********************************************* + // is_move_assignable + template + struct is_move_assignable : public etl::bool_constant::value || etl::is_pointer::value> + { + }; +#endif + //********************************************* // is_trivially_constructible template @@ -2335,7 +2422,11 @@ typedef integral_constant true_type; //********************************************* // is_trivially_copyable template +#if ETL_USING_BUILTIN_IS_TRIVIALLY_COPYABLE + struct is_trivially_copyable : public etl::bool_constant<__is_trivially_copyable(T)> +#else struct is_trivially_copyable : public etl::bool_constant::value || etl::is_pointer::value> +#endif { }; @@ -2382,6 +2473,12 @@ typedef integral_constant true_type; template inline constexpr bool is_move_constructible_v = etl::is_move_constructible::value; + template + inline constexpr bool is_copy_assignable_v = etl::is_copy_assignable::value; + + template + inline constexpr bool is_move_assignable_v = etl::is_move_assignable::value; + template inline constexpr bool is_trivially_constructible_v = etl::is_trivially_constructible::value; diff --git a/test/test_atomic.cpp b/test/test_atomic.cpp index a7442975..cdb79de0 100644 --- a/test/test_atomic.cpp +++ b/test/test_atomic.cpp @@ -711,6 +711,130 @@ namespace CHECK_EQUAL(compare.load(), test.load()); } + //************************************************************************* + TEST(test_atomic_non_scalar_trivially_copyable_struct) + { + struct Data + { + int x; + int y; + + bool operator ==(const Data& other) const + { + return (x == other.x) && (y == other.y); + } + }; + + // Default construction + etl::atomic test; + + Data d1 = { 1, 2 }; + Data d2 = { 3, 4 }; + Data d3 = { 5, 6 }; + + // Store and load + test.store(d1); + Data loaded = test.load(); + CHECK_EQUAL(d1.x, loaded.x); + CHECK_EQUAL(d1.y, loaded.y); + + // Assignment operator + test = d2; + loaded = test.load(); + CHECK_EQUAL(d2.x, loaded.x); + CHECK_EQUAL(d2.y, loaded.y); + + // Conversion operator + test.store(d1); + Data converted = static_cast(test); + CHECK_EQUAL(d1.x, converted.x); + CHECK_EQUAL(d1.y, converted.y); + + // Exchange + test.store(d1); + Data old_val = test.exchange(d2); + CHECK_EQUAL(d1.x, old_val.x); + CHECK_EQUAL(d1.y, old_val.y); + loaded = test.load(); + CHECK_EQUAL(d2.x, loaded.x); + CHECK_EQUAL(d2.y, loaded.y); + + // Compare exchange weak - pass (expected matches) + test.store(d1); + Data expected = d1; + bool result = test.compare_exchange_weak(expected, d3); + CHECK_TRUE(result); + loaded = test.load(); + CHECK_EQUAL(d3.x, loaded.x); + CHECK_EQUAL(d3.y, loaded.y); + + // Compare exchange weak - fail (expected does not match) + test.store(d1); + expected = d2; + result = test.compare_exchange_weak(expected, d3); + CHECK_FALSE(result); + loaded = test.load(); + CHECK_EQUAL(d1.x, loaded.x); + CHECK_EQUAL(d1.y, loaded.y); + + // Compare exchange weak with two memory order args - pass + test.store(d1); + expected = d1; + result = test.compare_exchange_weak(expected, d2, etl::memory_order_seq_cst, etl::memory_order_seq_cst); + CHECK_TRUE(result); + loaded = test.load(); + CHECK_EQUAL(d2.x, loaded.x); + CHECK_EQUAL(d2.y, loaded.y); + + // Compare exchange strong - pass (expected matches) + test.store(d1); + expected = d1; + result = test.compare_exchange_strong(expected, d3); + CHECK_TRUE(result); + loaded = test.load(); + CHECK_EQUAL(d3.x, loaded.x); + CHECK_EQUAL(d3.y, loaded.y); + + // Compare exchange strong - fail (expected does not match) + test.store(d1); + expected = d2; + result = test.compare_exchange_strong(expected, d3); + CHECK_FALSE(result); + loaded = test.load(); + CHECK_EQUAL(d1.x, loaded.x); + CHECK_EQUAL(d1.y, loaded.y); + + // Compare exchange strong with two memory order args - pass + test.store(d1); + expected = d1; + result = test.compare_exchange_strong(expected, d2, etl::memory_order_seq_cst, etl::memory_order_seq_cst); + CHECK_TRUE(result); + loaded = test.load(); + CHECK_EQUAL(d2.x, loaded.x); + CHECK_EQUAL(d2.y, loaded.y); + + // Explicit memory order on store/load + test.store(d3, etl::memory_order_release); + loaded = test.load(etl::memory_order_acquire); + CHECK_EQUAL(d3.x, loaded.x); + CHECK_EQUAL(d3.y, loaded.y); + + // Exchange with explicit memory order + test.store(d1); + old_val = test.exchange(d2, etl::memory_order_acq_rel); + CHECK_EQUAL(d1.x, old_val.x); + CHECK_EQUAL(d1.y, old_val.y); + loaded = test.load(); + CHECK_EQUAL(d2.x, loaded.x); + CHECK_EQUAL(d2.y, loaded.y); + + // Value construction + etl::atomic test2(d1); + loaded = test2.load(); + CHECK_EQUAL(d1.x, loaded.x); + CHECK_EQUAL(d1.y, loaded.y); + } + //************************************************************************* #if REALTIME_TEST diff --git a/test/test_type_traits.cpp b/test/test_type_traits.cpp index 73d83c22..fa320c4c 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -210,6 +210,8 @@ using etl::is_assignable; using etl::is_constructible; using etl::is_copy_constructible; using etl::is_move_constructible; +using etl::is_copy_assignable; +using etl::is_move_assignable; //************************* template <> @@ -227,6 +229,16 @@ struct etl::is_move_constructible : public etl::false_type { }; +template <> +struct etl::is_copy_assignable : public etl::true_type +{ +}; + +template <> +struct etl::is_move_assignable : public etl::true_type +{ +}; + //************************* template <> struct etl::is_assignable : public etl::true_type @@ -243,6 +255,16 @@ struct etl::is_move_constructible : public etl::true_type { }; +template <> +struct etl::is_copy_assignable : public etl::false_type +{ +}; + +template <> +struct etl::is_move_assignable : public etl::true_type +{ +}; + //************************* template <> struct etl::is_assignable : public etl::true_type @@ -258,6 +280,16 @@ template <> struct etl::is_move_constructible : public etl::true_type { }; + +template <> +struct etl::is_copy_assignable : public etl::true_type +{ +}; + +template <> +struct etl::is_move_assignable : public etl::true_type +{ +}; #endif namespace @@ -1296,6 +1328,34 @@ namespace #endif } + //************************************************************************* + TEST(test_is_copy_assignable) + { + #if ETL_USING_CPP17 + CHECK((etl::is_copy_assignable_v) == (std::is_copy_assignable_v)); + CHECK((etl::is_copy_assignable_v) == (std::is_copy_assignable_v)); + CHECK((etl::is_copy_assignable_v) == (std::is_copy_assignable_v)); + #else + CHECK((etl::is_copy_assignable::value) == (std::is_copy_assignable::value)); + CHECK((etl::is_copy_assignable::value) == (std::is_copy_assignable::value)); + CHECK((etl::is_copy_assignable::value) == (std::is_copy_assignable::value)); + #endif + } + + //************************************************************************* + TEST(test_is_move_assignable) + { + #if ETL_USING_CPP17 + CHECK((etl::is_move_assignable_v) == (std::is_move_assignable_v)); + CHECK((etl::is_move_assignable_v) == (std::is_move_assignable_v)); + CHECK((etl::is_move_assignable_v) == (std::is_move_assignable_v)); + #else + CHECK((etl::is_move_assignable::value) == (std::is_move_assignable::value)); + CHECK((etl::is_move_assignable::value) == (std::is_move_assignable::value)); + CHECK((etl::is_move_assignable::value) == (std::is_move_assignable::value)); + #endif + } + //************************************************************************* TEST(test_is_trivially_constructible) { From ab03ea114fa59bc9ace96e5f75063da0e1bd8be5 Mon Sep 17 00:00:00 2001 From: Bram Meijer <48760013+Brambovich@users.noreply.github.com> Date: Fri, 27 Mar 2026 09:11:45 +0100 Subject: [PATCH 31/31] Fix compilation issue when including etl/chrono next to etl/to_string. (#1365) * Print test names at test time (#1343) * Fix namespace issue of floor/round in to_string_helper --------- Co-authored-by: Roland Reichwein Co-authored-by: John Wellbelove --- include/etl/private/to_string_helper.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/etl/private/to_string_helper.h b/include/etl/private/to_string_helper.h index 2c3cbed5..9b097ca8 100644 --- a/include/etl/private/to_string_helper.h +++ b/include/etl/private/to_string_helper.h @@ -338,11 +338,11 @@ namespace etl } // Find the integral part of the floating point - T f_integral = floor(etl::absolute(value)); + T f_integral = ::floor(etl::absolute(value)); uworkspace_t integral = static_cast(f_integral); // Find the fractional part of the floating point. - uworkspace_t fractional = static_cast(round((etl::absolute(value) - f_integral) * multiplier)); + uworkspace_t fractional = static_cast(::round((etl::absolute(value) - f_integral) * multiplier)); // Check for a rounding carry to the integral. if (fractional == multiplier)