diff --git a/include/etl/algorithm.h b/include/etl/algorithm.h index 8b4960cf..eacf331b 100644 --- a/include/etl/algorithm.h +++ b/include/etl/algorithm.h @@ -2352,6 +2352,61 @@ 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; + } } //***************************************************************************** diff --git a/test/test_algorithm.cpp b/test/test_algorithm.cpp index 9cddff39..01e537d2 100644 --- a/test/test_algorithm.cpp +++ b/test/test_algorithm.cpp @@ -3264,6 +3264,129 @@ 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); + } + //************************************************************************* struct generator {