diff options
Diffstat (limited to 'libcxx/test/std')
9 files changed, 638 insertions, 21 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; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique_copy.pass.cpp index 8ccff619b811..ac7965c8eb2c 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique_copy.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/ranges_unique_copy.pass.cpp @@ -36,15 +36,409 @@ #include <ranges> #include "almost_satisfies_types.h" +#include "counting_predicates.h" +#include "counting_projection.h" +#include "MoveOnly.h" #include "test_iterators.h" -// TODO: SFINAE tests. +template < + class InIter = int*, + class Sent = int*, + class OutIter = int*, + class Comp = std::ranges::equal_to, + class Proj = std::identity> +concept HasUniqueCopyIter = + requires(InIter&& in, Sent&& sent, OutIter&& out, Comp&& comp, Proj&& proj) { + std::ranges::unique_copy( + std::forward<InIter>(in), + std::forward<Sent>(sent), + std::forward<OutIter>(out), + std::forward<Comp>(comp), + std::forward<Proj>(proj)); + }; + +static_assert(HasUniqueCopyIter<int*, int*, int*>); + +// !input_iterator<I> +static_assert(!HasUniqueCopyIter<InputIteratorNotDerivedFrom, sentinel_wrapper<InputIteratorNotDerivedFrom>>); + +// !sentinel_for<S, I> +static_assert(!HasUniqueCopyIter<int*, SentinelForNotSemiregular>); + +// !weakly_incrementable<O> +static_assert(!HasUniqueCopyIter<int*, int*, WeaklyIncrementableNotMovable>); + +// !indirect_equivalence_relation<Comp, projected<I, Proj>> +static_assert(!HasUniqueCopyIter<int*, int*, int*, ComparatorNotCopyable<int>>); + +// !indirectly_copyable<I, O> +static_assert(!HasUniqueCopyIter<const int*, const int*, const int*>); + +// forward_iterator<I> +// !(input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>) +// !indirectly_copyable_storable<I, O> +struct AssignableFromMoveOnly { + int data; + constexpr AssignableFromMoveOnly& operator=(MoveOnly const& m) { + data = m.get(); + return *this; + } +}; +static_assert(HasUniqueCopyIter<MoveOnly*, MoveOnly*, AssignableFromMoveOnly*>); +// because: +static_assert(std::forward_iterator<MoveOnly*>); +static_assert(!std::same_as<std::iter_value_t<MoveOnly*>, std::iter_value_t<AssignableFromMoveOnly*>>); +static_assert(!std::indirectly_copyable_storable<MoveOnly*, AssignableFromMoveOnly*>); + +// !forward_iterator<I> +// (input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>) +// !indirectly_copyable_storable<I, O> +struct CopyAssignableNotCopyConstructible { + int data; + constexpr CopyAssignableNotCopyConstructible(int i = 0) : data(i) {} + CopyAssignableNotCopyConstructible(const CopyAssignableNotCopyConstructible&) = delete; + CopyAssignableNotCopyConstructible& operator=(const CopyAssignableNotCopyConstructible&) = default; + friend constexpr bool + operator==(CopyAssignableNotCopyConstructible const&, CopyAssignableNotCopyConstructible const&) = default; +}; + +using InputAndOutputIterator = cpp17_input_iterator<CopyAssignableNotCopyConstructible*>; +static_assert(std::input_iterator<InputAndOutputIterator>); +static_assert(std::output_iterator<InputAndOutputIterator, CopyAssignableNotCopyConstructible>); + +static_assert( + HasUniqueCopyIter< + cpp20_input_iterator<CopyAssignableNotCopyConstructible*>, + sentinel_wrapper<cpp20_input_iterator<CopyAssignableNotCopyConstructible*>>, + InputAndOutputIterator>); +// because: +static_assert(!std::forward_iterator< cpp20_input_iterator<CopyAssignableNotCopyConstructible*>>); +static_assert( + std::input_iterator<InputAndOutputIterator> && + std::same_as<std::iter_value_t<cpp20_input_iterator<CopyAssignableNotCopyConstructible*>>, + std::iter_value_t<InputAndOutputIterator>>); +static_assert( + !std::indirectly_copyable_storable< + cpp20_input_iterator<CopyAssignableNotCopyConstructible*>, + InputAndOutputIterator>); + +// !forward_iterator<I> +// !(input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>) +// indirectly_copyable_storable<I, O> +static_assert( + HasUniqueCopyIter< + cpp20_input_iterator<int*>, + sentinel_wrapper<cpp20_input_iterator<int*>>, + cpp20_output_iterator<int*>>); +// because: +static_assert(!std::forward_iterator<cpp20_input_iterator<int*>>); +static_assert(!std::input_iterator<cpp20_output_iterator<int*>>); +static_assert(std::indirectly_copyable_storable<cpp20_input_iterator<int*>, cpp20_output_iterator<int*>>); + +// !forward_iterator<I> +// !(input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>) +// !indirectly_copyable_storable<I, O> +static_assert( + !HasUniqueCopyIter< + cpp20_input_iterator<MoveOnly*>, + sentinel_wrapper<cpp20_input_iterator<MoveOnly*>>, + cpp20_output_iterator<AssignableFromMoveOnly*>>); +// because: +static_assert(!std::forward_iterator<cpp20_input_iterator<MoveOnly*>>); +static_assert(!std::input_iterator<cpp20_output_iterator<MoveOnly*>>); +static_assert( + !std:: + indirectly_copyable_storable<cpp20_input_iterator<MoveOnly*>, cpp20_output_iterator<AssignableFromMoveOnly*>>); + +template < class Range, class OutIter = int*, class Comp = std::ranges::equal_to, class Proj = std::identity> +concept HasUniqueCopyRange = + requires(Range&& range, OutIter&& out, Comp&& comp, Proj&& proj) { + std::ranges::unique_copy( + std::forward<Range>(range), std::forward<OutIter>(out), std::forward<Comp>(comp), std::forward<Proj>(proj)); + }; + +template <class T> +using R = UncheckedRange<T>; + +static_assert(HasUniqueCopyRange<R<int*>, int*>); + +// !input_range<R> +static_assert(!HasUniqueCopyRange<R<InputIteratorNotDerivedFrom>>); + +// !weakly_incrementable<O> +static_assert(!HasUniqueCopyIter<R<int*>, WeaklyIncrementableNotMovable>); + +// !indirect_equivalence_relation<Comp, projected<I, Proj>> +static_assert(!HasUniqueCopyIter<R<int*>, int*, ComparatorNotCopyable<int>>); + +// !indirectly_copyable<I, O> +static_assert(!HasUniqueCopyIter<R<const int*>, const int*>); + +// !forward_iterator<iterator_t<R>> +// !(input_iterator<O> && same_as<range_value_t<R>, iter_value_t<O>>) +// !indirectly_copyable_storable<iterator_t<R>, O> +static_assert(!HasUniqueCopyIter< R<cpp20_input_iterator<MoveOnly*>>, cpp20_output_iterator<AssignableFromMoveOnly*>>); + +template <class InIter, class OutIter, template <class> class SentWrapper, std::size_t N1, std::size_t N2> +constexpr void testUniqueCopyImpl(std::array<int, N1> in, std::array<int, N2> expected) { + using Sent = SentWrapper<InIter>; + + // iterator overload + { + std::array<int, N2> out; + std::same_as<std::ranges::unique_copy_result<InIter, OutIter>> decltype(auto) result = + std::ranges::unique_copy(InIter{in.data()}, Sent{InIter{in.data() + in.size()}}, OutIter{out.begin()}); + assert(std::ranges::equal(out, expected)); + assert(base(result.in) == in.data() + in.size()); + assert(base(result.out) == out.data() + out.size()); + } + + // range overload + { + std::array<int, N2> out; + std::ranges::subrange r{InIter{in.data()}, Sent{InIter{in.data() + in.size()}}}; + std::same_as<std::ranges::unique_copy_result<InIter, OutIter>> decltype(auto) result = + std::ranges::unique_copy(r, OutIter{out.begin()}); + assert(std::ranges::equal(out, expected)); + assert(base(result.in) == in.data() + in.size()); + assert(base(result.out) == out.data() + out.size()); + } +} + +template <class InIter, class OutIter, 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}; + testUniqueCopyImpl<InIter, OutIter, SentWrapper>(in, expected); + } + + // one group of consecutive elements + { + std::array in{2, 3, 3, 3, 4, 3}; + std::array expected{2, 3, 4, 3}; + testUniqueCopyImpl<InIter, OutIter, 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}; + testUniqueCopyImpl<InIter, OutIter, SentWrapper>(in, expected); + } + + // all the same + { + std::array in{1, 1, 1, 1, 1, 1}; + std::array expected{1}; + testUniqueCopyImpl<InIter, OutIter, SentWrapper>(in, expected); + } + + // empty range + { + std::array<int, 0> in{}; + std::array<int, 0> expected{}; + testUniqueCopyImpl<InIter, OutIter, SentWrapper>(in, expected); + } +} + +template <class OutIter, template <class> class SentWrapper> +constexpr void withAllPermutationsOfInIter() { + testImpl<cpp20_input_iterator<int*>, OutIter, sentinel_wrapper>(); + testImpl<forward_iterator<int*>, OutIter, SentWrapper>(); + testImpl<bidirectional_iterator<int*>, OutIter, SentWrapper>(); + testImpl<random_access_iterator<int*>, OutIter, SentWrapper>(); + testImpl<contiguous_iterator<int*>, OutIter, SentWrapper>(); + testImpl<int*, OutIter, SentWrapper>(); +} + +template <template <class> class SentWrapper> +constexpr void withAllPermutationsOfInIterAndOutIter() { + withAllPermutationsOfInIter<cpp20_output_iterator<int*>, SentWrapper>(); + withAllPermutationsOfInIter<forward_iterator<int*>, SentWrapper>(); + withAllPermutationsOfInIter<bidirectional_iterator<int*>, SentWrapper>(); + withAllPermutationsOfInIter<random_access_iterator<int*>, SentWrapper>(); + withAllPermutationsOfInIter<contiguous_iterator<int*>, SentWrapper>(); + withAllPermutationsOfInIter<int*, SentWrapper>(); +} constexpr bool test() { - // TODO: main tests. - // TODO: A custom comparator works. - // TODO: A custom projection works. + withAllPermutationsOfInIterAndOutIter<std::type_identity_t>(); + withAllPermutationsOfInIterAndOutIter<sentinel_wrapper>(); + + // Test the overload that re-reads from the input iterator + // forward_iterator<I> + // !(input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>) + // !indirectly_copyable_storable<I, O> + { + MoveOnly in[5] = {1, 3, 3, 3, 1}; + // iterator overload + { + AssignableFromMoveOnly out[3] = {}; + auto result = std::ranges::unique_copy(in, in + 5, out); + assert(std::ranges::equal(out, std::array{1, 3, 1}, {}, &AssignableFromMoveOnly::data)); + assert(result.in == in + 5); + assert(result.out == out + 3); + } + // range overload + { + AssignableFromMoveOnly out[3] = {}; + auto result = std::ranges::unique_copy(std::ranges::subrange{in, in + 5}, out); + assert(std::ranges::equal(out, std::array{1, 3, 1}, {}, &AssignableFromMoveOnly::data)); + assert(result.in == in + 5); + assert(result.out == out + 3); + } + } + + // Test the overload that re-reads from the output iterator + // !forward_iterator<I> + // (input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>) + // !indirectly_copyable_storable<I, O> + { + using InIter = cpp20_input_iterator<CopyAssignableNotCopyConstructible*>; + using Sent = sentinel_wrapper<InIter>; + CopyAssignableNotCopyConstructible in[6] = {1, 1, 2, 2, 3, 3}; + // iterator overload + { + CopyAssignableNotCopyConstructible out[3]; + auto result = std::ranges::unique_copy(InIter{in}, Sent{InIter{in + 6}}, InputAndOutputIterator{out}); + assert(std::ranges::equal(out, std::array{1, 2, 3}, {}, &CopyAssignableNotCopyConstructible::data)); + assert(base(result.in) == in + 6); + assert(base(result.out) == out + 3); + } + // range overload + { + CopyAssignableNotCopyConstructible out[3]; + auto r = std::ranges::subrange(InIter{in}, Sent{InIter{in + 6}}); + auto result = std::ranges::unique_copy(r, InputAndOutputIterator{out}); + assert(std::ranges::equal(out, std::array{1, 2, 3}, {}, &CopyAssignableNotCopyConstructible::data)); + assert(base(result.in) == in + 6); + assert(base(result.out) == out + 3); + } + } + + // Test the overload that reads from the temporary copy of the value + // !forward_iterator<I> + // !(input_iterator<O> && same_as<iter_value_t<I>, iter_value_t<O>>) + // indirectly_copyable_storable<I, O> + { + using InIter = cpp20_input_iterator<int*>; + using Sent = sentinel_wrapper<InIter>; + int in[4] = {1, 1, 1, 2}; + // iterator overload + { + int out[2]; + auto result = std::ranges::unique_copy(InIter{in}, Sent{InIter{in + 4}}, cpp20_output_iterator<int*>{out}); + assert(std::ranges::equal(out, std::array{1, 2})); + assert(base(result.in) == in + 4); + assert(base(result.out) == out + 2); + } + // range overload + { + int out[2]; + auto r = std::ranges::subrange(InIter{in}, Sent{InIter{in + 4}}); + auto result = std::ranges::unique_copy(r, cpp20_output_iterator<int*>{out}); + assert(std::ranges::equal(out, std::array{1, 2})); + assert(base(result.in) == in + 4); + assert(base(result.out) == out + 2); + } + } + + struct Data { + int data; + }; + + // Test custom comparator + { + std::array in{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 + { + std::array<Data, 2> out; + auto result = std::ranges::unique_copy(in.begin(), in.end(), out.begin(), comp); + assert(std::ranges::equal(out, expected, comp)); + assert(base(result.in) == in.begin() + 4); + assert(base(result.out) == out.begin() + 2); + } + + // range overload + { + std::array<Data, 2> out; + auto result = std::ranges::unique_copy(in, out.begin(), comp); + assert(std::ranges::equal(out, expected, comp)); + assert(base(result.in) == in.begin() + 4); + assert(base(result.out) == out.begin() + 2); + } + } + + // Test custom projection + { + std::array in{Data{4}, Data{8}, Data{8}, Data{8}}; + std::array expected{Data{4}, Data{8}}; + + const auto proj = &Data::data; + + // iterator overload + { + std::array<Data, 2> out; + auto result = std::ranges::unique_copy(in.begin(), in.end(), out.begin(), {}, proj); + assert(std::ranges::equal(out, expected, {}, proj, proj)); + assert(base(result.in) == in.begin() + 4); + assert(base(result.out) == out.begin() + 2); + } + + // range overload + { + std::array<Data, 2> out; + auto result = std::ranges::unique_copy(in, out.begin(), {}, proj); + assert(std::ranges::equal(out, expected, {}, proj, proj)); + assert(base(result.in) == in.begin() + 4); + assert(base(result.out) == out.begin() + 2); + } + } + // Exactly last - first - 1 applications of the corresponding predicate and no + // more than twice as many applications of any projection. + { + std::array in{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 + { + std::array<int, 8> out; + int numberOfComp = 0; + int numberOfProj = 0; + auto result = std::ranges::unique_copy( + in.begin(), + in.end(), + out.begin(), + counting_predicate{std::ranges::equal_to{}, numberOfComp}, + counting_projection{numberOfProj}); + assert(std::ranges::equal(out, expected)); + assert(base(result.in) == in.end()); + assert(base(result.out) == out.end()); + assert(numberOfComp == in.size() - 1); + assert(numberOfProj <= static_cast<int>(2 * (in.size() - 1))); + } + // range overload + { + std::array<int, 8> out; + int numberOfComp = 0; + int numberOfProj = 0; + auto result = std::ranges::unique_copy( + in, + out.begin(), + counting_predicate{std::ranges::equal_to{}, numberOfComp}, + counting_projection{numberOfProj}); + assert(std::ranges::equal(out, expected)); + assert(base(result.in) == in.end()); + assert(base(result.out) == out.end()); + assert(numberOfComp == in.size() - 1); + assert(numberOfProj <= static_cast<int>(2 * (in.size() - 1))); + } + } return true; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/unique_copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/unique_copy.pass.cpp index bda304cd7493..bcf9c99e24c2 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/unique_copy.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.unique/unique_copy.pass.cpp @@ -19,9 +19,21 @@ #include <algorithm> #include <cassert> +#include "MoveOnly.h" #include "test_macros.h" #include "test_iterators.h" +struct AssignableFromMoveOnly { + AssignableFromMoveOnly(int i) : data(i) {} + AssignableFromMoveOnly() : data(0) {} + int data; + AssignableFromMoveOnly& operator=(MoveOnly const& m) { + data = m.get(); + return *this; + } + bool operator==(AssignableFromMoveOnly const& rhs) const { return data == rhs.data; } +}; + #if TEST_STD_VER > 17 TEST_CONSTEXPR bool test_constexpr() { int ia[] = {0, 1, 2, 2, 4}; @@ -107,6 +119,7 @@ test() int main(int, char**) { + test<cpp17_input_iterator<const int*>, cpp17_input_iterator<int*> >(); test<cpp17_input_iterator<const int*>, cpp17_output_iterator<int*> >(); test<cpp17_input_iterator<const int*>, forward_iterator<int*> >(); test<cpp17_input_iterator<const int*>, bidirectional_iterator<int*> >(); @@ -137,6 +150,16 @@ int main(int, char**) test<const int*, random_access_iterator<int*> >(); test<const int*, int*>(); + // Move only inputs + { + MoveOnly in[5] = {1, 3, 3, 3, 1}; + AssignableFromMoveOnly out[3] = {}; + auto result = std::unique_copy(in, in + 5, out); + AssignableFromMoveOnly expected[3] = {1, 3, 1}; + assert(std::equal(out, out + 3, expected)); + assert(result == out + 3); + } + #if TEST_STD_VER > 17 static_assert(test_constexpr()); #endif diff --git a/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp b/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp index a694376ca6ec..875ca35b1265 100644 --- a/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp @@ -43,7 +43,7 @@ static_assert(std::is_same_v<in_out_result<int, long>, uninitialized_copy_result static_assert(std::is_same_v<in_out_result<int, long>, uninitialized_copy_n_result<int, long>>); static_assert(std::is_same_v<in_out_result<int, long>, uninitialized_move_result<int, long>>); static_assert(std::is_same_v<in_out_result<int, long>, uninitialized_move_n_result<int, long>>); -// static_assert(std::is_same_v<in_out_result<int, long>, unique_copy_result<int, long>>); +static_assert(std::is_same_v<in_out_result<int, long>, unique_copy_result<int, long>>); static_assert(std::is_same_v<in_in_out_result<int, long, char>, binary_transform_result<int, long, char>>); static_assert(std::is_same_v<in_in_out_result<int, long, char>, merge_result<int, long, char>>); diff --git a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp index e147c875902c..b8625f5a3f44 100644 --- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp @@ -88,7 +88,7 @@ constexpr bool test_all() { using std::ranges::set_union_result; using std::ranges::swap_ranges_result; using std::ranges::unary_transform_result; - //using std::ranges::unique_copy_result; + using std::ranges::unique_copy_result; auto unary_pred = [](int i) { return i > 0; }; auto binary_pred = [](int i, int j) { return i < j; }; @@ -117,7 +117,7 @@ constexpr bool test_all() { dangling_1st(std::ranges::partition_point, in, unary_pred); dangling_1st(std::ranges::lower_bound, in, x); dangling_1st(std::ranges::upper_bound, in, x); - //dangling_1st(std::ranges::equal_range, in, x); + dangling_1st(std::ranges::equal_range, in, x); dangling_1st(std::ranges::min_element, in); dangling_1st(std::ranges::max_element, in); dangling_1st<minmax_result<dangling>>(std::ranges::minmax_element, in); @@ -157,7 +157,7 @@ constexpr bool test_all() { dangling_both<swap_ranges_result<dangling, dangling>>(std::ranges::swap_ranges, in, in2); dangling_1st<reverse_copy_result<dangling, int*>>(std::ranges::reverse_copy, in, out); dangling_1st<rotate_copy_result<dangling, int*>>(std::ranges::rotate_copy, in, mid, out); - //dangling_1st<unique_copy_result<dangling, int*>>(std::ranges::unique_copy, in, out); + dangling_1st<unique_copy_result<dangling, int*>>(std::ranges::unique_copy, in, out); dangling_1st<partition_copy_result<dangling, int*, int*>>(std::ranges::partition_copy, in, out, out2, unary_pred); //dangling_1st<partial_sort_copy_result<dangling, int*>>(std::ranges::partial_sort_copy, in, in2); //dangling_2nd<partial_sort_copy_result<int*, dangling>>(std::ranges::partial_sort_copy, in, in2); @@ -184,7 +184,7 @@ constexpr bool test_all() { //dangling_1st(std::ranges::rotate, in, mid); if (!std::is_constant_evaluated()) // `shuffle` isn't `constexpr`. dangling_1st(std::ranges::shuffle, in, rand_gen()); - //dangling_1st(std::ranges::unique, in); + dangling_1st(std::ranges::unique, in); dangling_1st(std::ranges::partition, in, unary_pred); if (!std::is_constant_evaluated()) dangling_1st(std::ranges::stable_partition, in, unary_pred); diff --git a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp index eeeec9c1f57f..0d1e3990b3b1 100644 --- a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp @@ -115,7 +115,7 @@ constexpr bool test_all() { //test(std::ranges::remove_copy_if, in, out, unary_pred); test(std::ranges::replace_if, in, unary_pred, x); //test(std::ranges::replace_copy_if, in, out, unary_pred, x); - //test(std::ranges::unique_copy, in, out, binary_pred); + test(std::ranges::unique_copy, in, out, binary_pred); test(std::ranges::partition_copy, in, out, out2, unary_pred); //test(std::ranges::partial_sort_copy, in, in2, binary_pred); test(std::ranges::merge, in, in2, out, binary_pred); @@ -124,7 +124,7 @@ constexpr bool test_all() { test(std::ranges::set_symmetric_difference, in, in2, out, binary_pred); test(std::ranges::set_union, in, in2, out, binary_pred); test(std::ranges::remove_if, in, unary_pred); - //test(std::ranges::unique, in, binary_pred); + test(std::ranges::unique, in, binary_pred); test(std::ranges::partition, in, unary_pred); if (!std::is_constant_evaluated()) test(std::ranges::stable_partition, in, unary_pred); diff --git a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp index aa0b15db6dee..c6fa8598e613 100644 --- a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp @@ -140,7 +140,7 @@ constexpr bool test_all() { // `reverse_copy` has neither a projection nor a predicate. // `rotate_copy` has neither a projection nor a predicate. // `sample` has no requirement that the given generator be invoked via `std::invoke`. - //test(std::ranges::unique_copy, in, out, &Foo::binary_pred, &Bar::val); + test(std::ranges::unique_copy, in, out, &Foo::binary_pred, &Bar::val); test(std::ranges::partition_copy, in, out, out2, &Foo::unary_pred, &Bar::val); //test(std::ranges::partial_sort_copy, in, in2, &Foo::binary_pred, &Bar::val); test(std::ranges::merge, in, in2, out, &Foo::binary_pred, &Bar::val, &Bar::val); @@ -153,7 +153,7 @@ constexpr bool test_all() { // `reverse` has neither a projection nor a predicate. // `rotate` has neither a projection nor a predicate. // `shuffle` has neither a projection nor a predicate. - //test(std::ranges::unique, in, &Foo::binary_pred, &Bar::val); + test(std::ranges::unique, in, &Foo::binary_pred, &Bar::val); test(std::ranges::partition, in, &Foo::unary_pred, &Bar::val); if (!std::is_constant_evaluated()) test(std::ranges::stable_partition, in, &Foo::unary_pred, &Bar::val); diff --git a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp index 4013065eb42a..191b4953f025 100644 --- a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp @@ -136,7 +136,7 @@ constexpr void run_tests() { if constexpr (std::copyable<T>) { test(std::ranges::reverse_copy, in, out); test_mid(std::ranges::rotate_copy, in, mid, out); - //test(std::ranges::unique_copy, in, out); + test(std::ranges::unique_copy, in, out); test(std::ranges::partition_copy, in, out, out2, unary_pred); //test_mid(std::ranges::partial_sort_copy, in, in2); test(std::ranges::merge, in, in2, out); @@ -153,7 +153,7 @@ constexpr void run_tests() { test(std::ranges::shuffle, in, rand_gen()); //if (!std::is_constant_evaluated()) // test(std::ranges::sample, in, out, count, rand_gen()); - //test(std::ranges::unique, in); + test(std::ranges::unique, in); test(std::ranges::partition, in, unary_pred); // TODO(ranges): `stable_partition` requires `ranges::rotate` to be implemented. //if (!std::is_constant_evaluated()) diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp index 1fc6dfb89c4b..eac6eece9ac7 100644 --- a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp +++ b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp @@ -145,8 +145,8 @@ static_assert(test(std::ranges::stable_sort, a)); //static_assert(test(std::ranges::starts_with, a, a)); static_assert(test(std::ranges::swap_ranges, a, a)); static_assert(test(std::ranges::transform, a, a, triple)); -//static_assert(test(std::ranges::unique, a)); -//static_assert(test(std::ranges::unique_copy, a, a)); +static_assert(test(std::ranges::unique, a)); +static_assert(test(std::ranges::unique_copy, a, a)); static_assert(test(std::ranges::upper_bound, a, 42)); // [memory.syn] |