aboutsummaryrefslogtreecommitdiff
path: root/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp')
-rw-r--r--libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp208
1 files changed, 204 insertions, 4 deletions
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp
index 216ae6a1b98e..bb60109e7735 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp
@@ -28,14 +28,214 @@
#include <ranges>
#include "almost_satisfies_types.h"
+#include "counting_predicates.h"
+#include "counting_projection.h"
#include "test_iterators.h"
-// TODO: SFINAE tests.
+template <class Iter = int*, class Sent = int*, class Comp = std::ranges::equal_to, class Proj = std::identity>
+concept HasUniqueIter =
+ requires(Iter&& iter, Sent&& sent, Comp&& comp, Proj&& proj) {
+ std::ranges::unique(
+ std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Comp>(comp), std::forward<Proj>(proj));
+ };
+
+static_assert(HasUniqueIter<int*, int*>);
+
+// !permutable<I>
+static_assert(!HasUniqueIter<PermutableNotForwardIterator>);
+static_assert(!HasUniqueIter<PermutableNotSwappable>);
+
+// !sentinel_for<S, I>
+static_assert(!HasUniqueIter<int*, SentinelForNotSemiregular>);
+
+// !indirect_equivalence_relation<Comp, projected<I, Proj>>
+static_assert(!HasUniqueIter<int*, int*, ComparatorNotCopyable<int>>);
+
+template <class Range, class Comp = std::ranges::equal_to, class Proj = std::identity>
+concept HasUniqueRange =
+ requires(Range&& range, Comp&& comp, Proj&& proj) {
+ std::ranges::unique(std::forward<Range>(range), std::forward<Comp>(comp), std::forward<Proj>(proj));
+ };
+
+template <class T>
+using R = UncheckedRange<T>;
+
+static_assert(HasUniqueRange<R<int*>>);
+
+// !forward_range<R>
+static_assert(!HasUniqueRange<ForwardRangeNotDerivedFrom>);
+static_assert(!HasUniqueRange<ForwardRangeNotIncrementable>);
+
+// permutable<ranges::iterator_t<R>>
+static_assert(!HasUniqueRange<R<PermutableNotForwardIterator>>);
+static_assert(!HasUniqueRange<R<PermutableNotSwappable>>);
+
+// !indirect_equivalence_relation<Comp, projected<ranges::iterator_t<R>, Proj>>
+static_assert(!HasUniqueRange<R<int*>, ComparatorNotCopyable<int>>);
+
+template <class Iter, template <class> class SentWrapper, std::size_t N1, std::size_t N2>
+constexpr void testUniqueImpl(std::array<int, N1> input, std::array<int, N2> expected) {
+ using Sent = SentWrapper<Iter>;
+
+ // iterator overload
+ {
+ auto in = input;
+ std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
+ std::ranges::unique(Iter{in.data()}, Sent{Iter{in.data() + in.size()}});
+ assert(std::ranges::equal(std::ranges::subrange<Iter>{Iter{in.data()}, result.begin()}, expected));
+ assert(base(result.end()) == in.data() + in.size());
+ }
+
+ // range overload
+ {
+ auto in = input;
+ std::ranges::subrange r{Iter{in.data()}, Sent{Iter{in.data() + in.size()}}};
+ std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::unique(r);
+ assert(std::ranges::equal(std::ranges::subrange<Iter>{Iter{in.data()}, result.begin()}, expected));
+ assert(base(result.end()) == in.data() + in.size());
+ }
+}
+
+template <class Iter, template <class> class SentWrapper>
+constexpr void testImpl() {
+ // no consecutive elements
+ {
+ std::array in{1, 2, 3, 2, 1};
+ std::array expected{1, 2, 3, 2, 1};
+ testUniqueImpl<Iter, SentWrapper>(in, expected);
+ }
+
+ // one group of consecutive elements
+ {
+ std::array in{2, 3, 3, 3, 4, 3};
+ std::array expected{2, 3, 4, 3};
+ testUniqueImpl<Iter, SentWrapper>(in, expected);
+ }
+
+ // multiple groups of consecutive elements
+ {
+ std::array in{2, 3, 3, 3, 4, 3, 3, 5, 5, 5};
+ std::array expected{2, 3, 4, 3, 5};
+ testUniqueImpl<Iter, SentWrapper>(in, expected);
+ }
+
+ // all the same
+ {
+ std::array in{1, 1, 1, 1, 1, 1};
+ std::array expected{1};
+ testUniqueImpl<Iter, SentWrapper>(in, expected);
+ }
+
+ // empty range
+ {
+ std::array<int, 0> in{};
+ std::array<int, 0> expected{};
+ testUniqueImpl<Iter, SentWrapper>(in, expected);
+ }
+
+ // single element range
+ std::array in{1};
+ std::array expected{1};
+ testUniqueImpl<Iter, SentWrapper>(in, expected);
+}
+
+template <template <class> class SentWrapper>
+constexpr void withAllPermutationsOfIter() {
+ testImpl<forward_iterator<int*>, SentWrapper>();
+ testImpl<bidirectional_iterator<int*>, SentWrapper>();
+ testImpl<random_access_iterator<int*>, SentWrapper>();
+ testImpl<contiguous_iterator<int*>, SentWrapper>();
+ testImpl<int*, SentWrapper>();
+}
constexpr bool test() {
- // TODO: main tests.
- // TODO: A custom comparator works.
- // TODO: A custom projection works.
+ withAllPermutationsOfIter<std::type_identity_t>();
+ withAllPermutationsOfIter<sentinel_wrapper>();
+
+ struct Data {
+ int data;
+ };
+
+ // Test custom comparator
+ {
+ std::array input{Data{4}, Data{8}, Data{8}, Data{8}};
+ std::array expected{Data{4}, Data{8}};
+ const auto comp = [](const Data& x, const Data& y) { return x.data == y.data; };
+
+ // iterator overload
+ {
+ auto in = input;
+ auto result = std::ranges::unique(in.begin(), in.end(), comp);
+ assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end(), comp));
+ assert(base(result.end()) == in.end());
+ }
+
+ // range overload
+ {
+ auto in = input;
+ auto result = std::ranges::unique(in, comp);
+ assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end(), comp));
+ assert(base(result.end()) == in.end());
+ }
+ }
+
+ // Test custom projection
+ {
+ std::array input{Data{4}, Data{8}, Data{8}, Data{8}};
+ std::array expected{Data{4}, Data{8}};
+
+ const auto proj = &Data::data;
+
+ // iterator overload
+ {
+ auto in = input;
+ auto result = std::ranges::unique(in.begin(), in.end(), {}, proj);
+ assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end(), {}, proj, proj));
+ assert(base(result.end()) == in.end());
+ }
+
+ // range overload
+ {
+ auto in = input;
+ auto result = std::ranges::unique(in, {}, proj);
+ assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end(), {}, proj, proj));
+ assert(base(result.end()) == in.end());
+ }
+ }
+
+ // Complexity: For nonempty ranges, exactly (last - first) - 1 applications of the corresponding predicate
+ // and no more than twice as many applications of any projection.
+ {
+ std::array input{1, 2, 3, 3, 3, 4, 3, 3, 5, 5, 6, 6, 1};
+ std::array expected{1, 2, 3, 4, 3, 5, 6, 1};
+ // iterator overload
+ {
+ auto in = input;
+ int numberOfComp = 0;
+ int numberOfProj = 0;
+ auto result = std::ranges::unique(
+ in.begin(),
+ in.end(),
+ counting_predicate{std::ranges::equal_to{}, numberOfComp},
+ counting_projection{numberOfProj});
+ assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end()));
+ assert(base(result.end()) == in.end());
+ assert(numberOfComp == in.size() - 1);
+ assert(numberOfProj <= static_cast<int>(2 * (in.size() - 1)));
+ }
+ // range overload
+ {
+ auto in = input;
+ int numberOfComp = 0;
+ int numberOfProj = 0;
+ auto result = std::ranges::unique(
+ in, counting_predicate{std::ranges::equal_to{}, numberOfComp}, counting_projection{numberOfProj});
+ assert(std::ranges::equal(in.begin(), result.begin(), expected.begin(), expected.end()));
+ assert(base(result.end()) == in.end());
+ assert(numberOfComp == in.size() - 1);
+ assert(numberOfProj <= static_cast<int>(2 * (in.size() - 1)));
+ }
+ }
return true;
}