//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 // // template S, // weakly_incrementable O1, weakly_incrementable O2, // class Proj = identity, indirect_unary_predicate> Pred> // requires indirectly_copyable && indirectly_copyable // constexpr partition_copy_result // partition_copy(I first, S last, O1 out_true, O2 out_false, Pred pred, // Proj proj = {}); // Since C++20 // // template, Proj>> Pred> // requires indirectly_copyable, O1> && // indirectly_copyable, O2> // constexpr partition_copy_result, O1, O2> // partition_copy(R&& r, O1 out_true, O2 out_false, Pred pred, Proj proj = {}); // Since C++20 #include #include #include #include #include #include #include "almost_satisfies_types.h" #include "counting_predicates.h" #include "counting_projection.h" #include "test_iterators.h" struct UnaryPred { bool operator()(int) const; }; // Test constraints of the (iterator, sentinel) overload. // ====================================================== template concept HasPartitionCopyIter = requires(InIter&& input, Sent&& sent, Output1&& output1, Output2&& output2, Pred&& pred) { std::ranges::partition_copy(std::forward(input), std::forward(sent), std::forward(output1), std::forward(output2), std::forward(pred)); }; static_assert(HasPartitionCopyIter); // !input_iterator static_assert(!HasPartitionCopyIter); static_assert(!HasPartitionCopyIter); static_assert(!HasPartitionCopyIter); // !sentinel_for static_assert(!HasPartitionCopyIter); static_assert(!HasPartitionCopyIter); // !weakly_incrementable static_assert(!HasPartitionCopyIter); // !weakly_incrementable static_assert(!HasPartitionCopyIter); // !indirect_unary_predicate> static_assert(!HasPartitionCopyIter); static_assert(!HasPartitionCopyIter); struct Uncopyable { Uncopyable(int&&); Uncopyable(const int&) = delete; }; // !indirectly_copyable static_assert(!HasPartitionCopyIter); // !indirectly_copyable static_assert(!HasPartitionCopyIter); // Test constraints of the (range) overload. // ========================================= template concept HasPartitionCopyRange = requires(InputRange&& input, Output1&& output1, Output2&& output2, Pred&& pred) { std::ranges::partition_copy(std::forward(input), std::forward(output1), std::forward(output2), std::forward(pred)); }; template using R = UncheckedRange; static_assert(HasPartitionCopyRange, int*, int*, UnaryPred>); // !input_iterator static_assert(!HasPartitionCopyRange); static_assert(!HasPartitionCopyRange); static_assert(!HasPartitionCopyRange); // !weakly_incrementable static_assert(!HasPartitionCopyRange, WeaklyIncrementableNotMovable>); // !weakly_incrementable static_assert(!HasPartitionCopyRange, int*, WeaklyIncrementableNotMovable>); // !indirect_unary_predicate> static_assert(!HasPartitionCopyRange, int*, int*, IndirectUnaryPredicateNotPredicate>); static_assert(!HasPartitionCopyRange, int*, int*, IndirectUnaryPredicateNotCopyConstructible>); // !indirectly_copyable static_assert(!HasPartitionCopyRange, Uncopyable*>); // !indirectly_copyable static_assert(!HasPartitionCopyRange, int*, Uncopyable*>); static_assert(std::is_same_v, std::ranges::in_out_out_result>); template constexpr void test_one(std::array input, Pred pred, std::array expected_true, std::array expected_false) { static_assert(N2 + N3 == N1); using ResultT = std::ranges::partition_copy_result; auto begin = input.data(); auto end = input.data() + input.size(); { // (iterator, sentinel) overload. std::array out1; std::array out2; std::same_as decltype(auto) result = std::ranges::partition_copy( Iter(begin), Sent(Iter(end)), OutIter1(out1.data()), OutIter2(out2.data()), pred); assert(base(result.in) == input.data() + input.size()); assert(base(result.out1) == out1.data() + expected_true.size()); assert(base(result.out2) == out2.data() + expected_false.size()); assert(std::ranges::equal(out1, expected_true)); assert(std::ranges::equal(out2, expected_false)); } { // (range) overload. std::ranges::subrange range{Iter(begin), Sent(Iter(end))}; std::array out1; std::array out2; std::same_as decltype(auto) result = std::ranges::partition_copy( range, OutIter1(out1.data()), OutIter2(out2.data()), pred); assert(base(result.in) == input.data() + input.size()); assert(base(result.out1) == out1.data() + expected_true.size()); assert(base(result.out2) == out2.data() + expected_false.size()); assert(std::ranges::equal(out1, expected_true)); assert(std::ranges::equal(out2, expected_false)); } } template constexpr void test_iterators_in_sent_out1_out2() { auto is_odd = [](int x) { return x % 2 != 0; }; // Empty sequence. test_one({}, is_odd, {}, {}); // 1-element sequence, the element satisfies the predicate. test_one({1}, is_odd, {1}, {}); // 1-element sequence, the element doesn't satisfy the predicate. test_one({2}, is_odd, {}, {2}); // 2-element sequence, not in order. test_one({2, 1}, is_odd, {1}, {2}); // 2-element sequence, already in order. test_one({1, 2}, is_odd, {1}, {2}); // 3-element sequence. test_one({2, 1, 3}, is_odd, {1, 3}, {2}); // Longer sequence. test_one({2, 1, 3, 6, 8, 4, 11, 5}, is_odd, {1, 3, 11, 5}, {2, 6, 8, 4}); // Longer sequence with duplicates. test_one({2, 1, 3, 6, 2, 8, 1, 6}, is_odd, {1, 3, 1}, {2, 6, 2, 8, 6}); // All elements are the same and satisfy the predicate. test_one({1, 1, 1}, is_odd, {1, 1, 1}, {}); // All elements are the same and don't satisfy the predicate. test_one({2, 2, 2}, is_odd, {}, {2, 2, 2}); // Already partitioned. test_one({1, 3, 5, 4, 6, 8}, is_odd, {1, 3, 5}, {4, 6, 8}); // Reverse-partitioned. test_one({4, 6, 8, 1, 3, 5}, is_odd, {1, 3, 5}, {4, 6, 8}); // Repeating pattern. test_one({1, 2, 1, 2, 1, 2}, is_odd, {1, 1, 1}, {2, 2, 2}); auto is_negative = [](int x) { return x < 0; }; // Different comparator. test_one({-3, 5, 7, -6, 2}, is_negative, {-3, -6}, {5, 7, 2}); } template constexpr void test_iterators_in_sent_out1() { test_iterators_in_sent_out1_out2>(); test_iterators_in_sent_out1_out2>(); test_iterators_in_sent_out1_out2(); } template constexpr void test_iterators_in_sent() { test_iterators_in_sent_out1>(); test_iterators_in_sent_out1>(); test_iterators_in_sent_out1>(); test_iterators_in_sent_out1(); } template constexpr void test_iterators_in() { if constexpr (std::sentinel_for) { test_iterators_in_sent(); } test_iterators_in_sent>(); } constexpr void test_iterators() { // Note: deliberately testing with only the weakest and "strongest" iterator types to minimize combinatorial // explosion. test_iterators_in>(); test_iterators_in(); } constexpr bool test() { test_iterators(); { // A custom projection works. const std::array in = {1, 3, 9, -2, -5, -8}; auto is_negative = [](int x) { return x < 0; }; auto negate = [](int x) { return -x; }; const std::array expected_negative = {-2, -5, -8}; const std::array expected_positive = {1, 3, 9}; { // (iterator, sentinel) overload. { std::array out1, out2; std::ranges::partition_copy(in.begin(), in.end(), out1.begin(), out2.begin(), is_negative); assert(out1 == expected_negative); assert(out2 == expected_positive); } { std::array out1, out2; std::ranges::partition_copy(in.begin(), in.end(), out1.begin(), out2.begin(), is_negative, negate); assert(out1 == expected_positive); assert(out2 == expected_negative); } } { // (range) overload. { std::array out1, out2; std::ranges::partition_copy(in, out1.begin(), out2.begin(), is_negative); assert(out1 == expected_negative); assert(out2 == expected_positive); } { std::array out1, out2; std::ranges::partition_copy(in, out1.begin(), out2.begin(), is_negative, negate); assert(out1 == expected_positive); assert(out2 == expected_negative); } } } { // Complexity: Exactly `last - first` applications of `pred` and `proj`. { const std::array in = {-2, -5, -8, -11, -10, -5, 1, 3, 9, 6, 8, 2, 4, 2}; auto is_negative = [](int x) { return x < 0; }; std::array out1; std::array out2; int pred_count = 0, proj_count = 0; counting_predicate pred(is_negative, pred_count); counting_projection proj(proj_count); auto expected = static_cast(in.size()); { std::ranges::partition_copy(in.begin(), in.end(), out1.begin(), out2.begin(), pred, proj); assert(pred_count == expected); assert(proj_count == expected); pred_count = proj_count = 0; } { std::ranges::partition_copy(in, out1.begin(), out2.begin(), pred, proj); assert(pred_count == expected); assert(proj_count == expected); pred_count = proj_count = 0; } } } return true; } int main(int, char**) { test(); static_assert(test()); return 0; }