1 //===----------------------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
13 // template<input_iterator I, sentinel_for<I> S,
14 // weakly_incrementable O1, weakly_incrementable O2,
15 // class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred>
16 // requires indirectly_copyable<I, O1> && indirectly_copyable<I, O2>
17 // constexpr partition_copy_result<I, O1, O2>
18 // partition_copy(I first, S last, O1 out_true, O2 out_false, Pred pred,
19 // Proj proj = {}); // Since C++20
21 // template<input_range R, weakly_incrementable O1, weakly_incrementable O2,
22 // class Proj = identity,
23 // indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
24 // requires indirectly_copyable<iterator_t<R>, O1> &&
25 // indirectly_copyable<iterator_t<R>, O2>
26 // constexpr partition_copy_result<borrowed_iterator_t<R>, O1, O2>
27 // partition_copy(R&& r, O1 out_true, O2 out_false, Pred pred, Proj proj = {}); // Since C++20
36 #include "almost_satisfies_types.h"
37 #include "counting_predicates.h"
38 #include "counting_projection.h"
39 #include "test_iterators.h"
41 struct UnaryPred
{ bool operator()(int) const; };
43 // Test constraints of the (iterator, sentinel) overload.
44 // ======================================================
46 template <class InIter
= int*, class Sent
= int*, class Output1
= int*, class Output2
= int*, class Pred
= UnaryPred
>
47 concept HasPartitionCopyIter
=
48 requires(InIter
&& input
, Sent
&& sent
, Output1
&& output1
, Output2
&& output2
, Pred
&& pred
) {
49 std::ranges::partition_copy(std::forward
<InIter
>(input
), std::forward
<Sent
>(sent
),
50 std::forward
<Output1
>(output1
), std::forward
<Output2
>(output2
), std::forward
<Pred
>(pred
));
53 static_assert(HasPartitionCopyIter
<int*, int*, int*, int*, UnaryPred
>);
56 static_assert(!HasPartitionCopyIter
<InputIteratorNotDerivedFrom
>);
57 static_assert(!HasPartitionCopyIter
<InputIteratorNotIndirectlyReadable
>);
58 static_assert(!HasPartitionCopyIter
<InputIteratorNotInputOrOutputIterator
>);
60 // !sentinel_for<S, I>
61 static_assert(!HasPartitionCopyIter
<int*, SentinelForNotSemiregular
>);
62 static_assert(!HasPartitionCopyIter
<int*, SentinelForNotWeaklyEqualityComparableWith
>);
64 // !weakly_incrementable<O1>
65 static_assert(!HasPartitionCopyIter
<int*, int*, WeaklyIncrementableNotMovable
>);
67 // !weakly_incrementable<O2>
68 static_assert(!HasPartitionCopyIter
<int*, int*, int*, WeaklyIncrementableNotMovable
>);
70 // !indirect_unary_predicate<projected<I, Proj>>
71 static_assert(!HasPartitionCopyIter
<int*, int*, int*, int*, IndirectUnaryPredicateNotPredicate
>);
72 static_assert(!HasPartitionCopyIter
<int*, int*, int*, int*, IndirectUnaryPredicateNotCopyConstructible
>);
76 Uncopyable(const int&) = delete;
78 // !indirectly_copyable<I, O1>
79 static_assert(!HasPartitionCopyIter
<int*, int*, Uncopyable
*>);
80 // !indirectly_copyable<I, O2>
81 static_assert(!HasPartitionCopyIter
<int*, int*, int*, Uncopyable
*>);
83 // Test constraints of the (range) overload.
84 // =========================================
86 template <class InputRange
, class Output1
= int*, class Output2
= int*, class Pred
= UnaryPred
>
87 concept HasPartitionCopyRange
=
88 requires(InputRange
&& input
, Output1
&& output1
, Output2
&& output2
, Pred
&& pred
) {
89 std::ranges::partition_copy(std::forward
<InputRange
>(input
),
90 std::forward
<Output1
>(output1
), std::forward
<Output2
>(output2
), std::forward
<Pred
>(pred
));
94 using R
= UncheckedRange
<T
>;
96 static_assert(HasPartitionCopyRange
<R
<int*>, int*, int*, UnaryPred
>);
99 static_assert(!HasPartitionCopyRange
<InputRangeNotDerivedFrom
>);
100 static_assert(!HasPartitionCopyRange
<InputRangeNotIndirectlyReadable
>);
101 static_assert(!HasPartitionCopyRange
<InputRangeNotInputOrOutputIterator
>);
103 // !weakly_incrementable<O1>
104 static_assert(!HasPartitionCopyRange
<R
<int*>, WeaklyIncrementableNotMovable
>);
106 // !weakly_incrementable<O2>
107 static_assert(!HasPartitionCopyRange
<R
<int*>, int*, WeaklyIncrementableNotMovable
>);
109 // !indirect_unary_predicate<projected<I, Proj>>
110 static_assert(!HasPartitionCopyRange
<R
<int*>, int*, int*, IndirectUnaryPredicateNotPredicate
>);
111 static_assert(!HasPartitionCopyRange
<R
<int*>, int*, int*, IndirectUnaryPredicateNotCopyConstructible
>);
113 // !indirectly_copyable<I, O1>
114 static_assert(!HasPartitionCopyRange
<R
<int*>, Uncopyable
*>);
115 // !indirectly_copyable<I, O2>
116 static_assert(!HasPartitionCopyRange
<R
<int*>, int*, Uncopyable
*>);
118 static_assert(std::is_same_v
<std::ranges::partition_copy_result
<int, int, int>,
119 std::ranges::in_out_out_result
<int, int, int>>);
121 template <class Iter
, class Sent
, class OutIter1
, class OutIter2
, std::size_t N1
, size_t N2
, size_t N3
, class Pred
>
122 constexpr void test_one(std::array
<int, N1
> input
, Pred pred
, std::array
<int, N2
> expected_true
,
123 std::array
<int, N3
> expected_false
) {
124 static_assert(N2
+ N3
== N1
);
125 using ResultT
= std::ranges::partition_copy_result
<Iter
, OutIter1
, OutIter2
>;
127 auto begin
= input
.data();
128 auto end
= input
.data() + input
.size();
130 { // (iterator, sentinel) overload.
131 std::array
<int, N2
> out1
;
132 std::array
<int, N3
> out2
;
134 std::same_as
<ResultT
> decltype(auto) result
= std::ranges::partition_copy(
135 Iter(begin
), Sent(Iter(end
)), OutIter1(out1
.begin()), OutIter2(out2
.begin()), pred
);
137 assert(base(result
.in
) == input
.data() + input
.size());
138 assert(base(result
.out1
) == out1
.data() + expected_true
.size());
139 assert(base(result
.out2
) == out2
.data() + expected_false
.size());
141 assert(std::ranges::equal(out1
, expected_true
));
142 assert(std::ranges::equal(out2
, expected_false
));
145 { // (range) overload.
146 std::ranges::subrange range
{Iter(begin
), Sent(Iter(end
))};
147 std::array
<int, N2
> out1
;
148 std::array
<int, N3
> out2
;
150 std::same_as
<ResultT
> decltype(auto) result
= std::ranges::partition_copy(
151 range
, OutIter1(out1
.begin()), OutIter2(out2
.begin()), pred
);
153 assert(base(result
.in
) == input
.data() + input
.size());
154 assert(base(result
.out1
) == out1
.data() + expected_true
.size());
155 assert(base(result
.out2
) == out2
.data() + expected_false
.size());
157 assert(std::ranges::equal(out1
, expected_true
));
158 assert(std::ranges::equal(out2
, expected_false
));
162 template <class InIter
, class Sent
, class Out1
, class Out2
>
163 constexpr void test_iterators_in_sent_out1_out2() {
164 auto is_odd
= [](int x
) { return x
% 2 != 0; };
167 test_one
<InIter
, Sent
, Out1
, Out2
, 0, 0, 0>({}, is_odd
, {}, {});
168 // 1-element sequence, the element satisfies the predicate.
169 test_one
<InIter
, Sent
, Out1
, Out2
, 1, 1, 0>({1}, is_odd
, {1}, {});
170 // 1-element sequence, the element doesn't satisfy the predicate.
171 test_one
<InIter
, Sent
, Out1
, Out2
, 1, 0, 1>({2}, is_odd
, {}, {2});
172 // 2-element sequence, not in order.
173 test_one
<InIter
, Sent
, Out1
, Out2
, 2, 1, 1>({2, 1}, is_odd
, {1}, {2});
174 // 2-element sequence, already in order.
175 test_one
<InIter
, Sent
, Out1
, Out2
, 2, 1, 1>({1, 2}, is_odd
, {1}, {2});
176 // 3-element sequence.
177 test_one
<InIter
, Sent
, Out1
, Out2
, 3, 2, 1>({2, 1, 3}, is_odd
, {1, 3}, {2});
179 test_one
<InIter
, Sent
, Out1
, Out2
, 8, 4, 4>({2, 1, 3, 6, 8, 4, 11, 5}, is_odd
, {1, 3, 11, 5}, {2, 6, 8, 4});
180 // Longer sequence with duplicates.
181 test_one
<InIter
, Sent
, Out1
, Out2
, 8, 3, 5>({2, 1, 3, 6, 2, 8, 1, 6}, is_odd
, {1, 3, 1}, {2, 6, 2, 8, 6});
182 // All elements are the same and satisfy the predicate.
183 test_one
<InIter
, Sent
, Out1
, Out2
, 3, 3, 0>({1, 1, 1}, is_odd
, {1, 1, 1}, {});
184 // All elements are the same and don't satisfy the predicate.
185 test_one
<InIter
, Sent
, Out1
, Out2
, 3, 0, 3>({2, 2, 2}, is_odd
, {}, {2, 2, 2});
186 // Already partitioned.
187 test_one
<InIter
, Sent
, Out1
, Out2
, 6, 3, 3>({1, 3, 5, 4, 6, 8}, is_odd
, {1, 3, 5}, {4, 6, 8});
188 // Reverse-partitioned.
189 test_one
<InIter
, Sent
, Out1
, Out2
, 6, 3, 3>({4, 6, 8, 1, 3, 5}, is_odd
, {1, 3, 5}, {4, 6, 8});
190 // Repeating pattern.
191 test_one
<InIter
, Sent
, Out1
, Out2
, 6, 3, 3>({1, 2, 1, 2, 1, 2}, is_odd
, {1, 1, 1}, {2, 2, 2});
193 auto is_negative
= [](int x
) { return x
< 0; };
194 // Different comparator.
195 test_one
<InIter
, Sent
, Out1
, Out2
, 5, 2, 3>({-3, 5, 7, -6, 2}, is_negative
, {-3, -6}, {5, 7, 2});
198 template <class InIter
, class Sent
, class Out1
>
199 constexpr void test_iterators_in_sent_out1() {
200 test_iterators_in_sent_out1_out2
<InIter
, Sent
, Out1
, cpp20_output_iterator
<int*>>();
201 test_iterators_in_sent_out1_out2
<InIter
, Sent
, Out1
, random_access_iterator
<int*>>();
202 test_iterators_in_sent_out1_out2
<InIter
, Sent
, Out1
, int*>();
205 template <class InIter
, class Sent
>
206 constexpr void test_iterators_in_sent() {
207 test_iterators_in_sent_out1
<InIter
, Sent
, cpp17_output_iterator
<int*>>();
208 test_iterators_in_sent_out1
<InIter
, Sent
, cpp20_output_iterator
<int*>>();
209 test_iterators_in_sent_out1
<InIter
, Sent
, random_access_iterator
<int*>>();
210 test_iterators_in_sent_out1
<InIter
, Sent
, int*>();
213 template <class InIter
>
214 constexpr void test_iterators_in() {
215 if constexpr (std::sentinel_for
<InIter
, InIter
>) {
216 test_iterators_in_sent
<InIter
, InIter
>();
218 test_iterators_in_sent
<InIter
, sentinel_wrapper
<InIter
>>();
221 constexpr void test_iterators() {
222 // Note: deliberately testing with only the weakest and "strongest" iterator types to minimize combinatorial
224 test_iterators_in
<cpp20_input_iterator
<int*>>();
225 test_iterators_in
<int*>();
228 constexpr bool test() {
231 { // A custom projection works.
232 const std::array in
= {1, 3, 9, -2, -5, -8};
233 auto is_negative
= [](int x
) { return x
< 0; };
234 auto negate
= [](int x
) { return -x
; };
235 const std::array expected_negative
= {-2, -5, -8};
236 const std::array expected_positive
= {1, 3, 9};
238 { // (iterator, sentinel) overload.
240 std::array
<int, 3> out1
, out2
;
241 std::ranges::partition_copy(in
.begin(), in
.end(), out1
.begin(), out2
.begin(), is_negative
);
242 assert(out1
== expected_negative
);
243 assert(out2
== expected_positive
);
246 std::array
<int, 3> out1
, out2
;
247 std::ranges::partition_copy(in
.begin(), in
.end(), out1
.begin(), out2
.begin(), is_negative
, negate
);
248 assert(out1
== expected_positive
);
249 assert(out2
== expected_negative
);
253 { // (range) overload.
255 std::array
<int, 3> out1
, out2
;
256 std::ranges::partition_copy(in
, out1
.begin(), out2
.begin(), is_negative
);
257 assert(out1
== expected_negative
);
258 assert(out2
== expected_positive
);
261 std::array
<int, 3> out1
, out2
;
262 std::ranges::partition_copy(in
, out1
.begin(), out2
.begin(), is_negative
, negate
);
263 assert(out1
== expected_positive
);
264 assert(out2
== expected_negative
);
269 { // Complexity: Exactly `last - first` applications of `pred` and `proj`.
271 const std::array in
= {-2, -5, -8, -11, -10, -5, 1, 3, 9, 6, 8, 2, 4, 2};
272 auto is_negative
= [](int x
) { return x
< 0; };
273 std::array
<int, 6> out1
;
274 std::array
<int, 8> out2
;
276 int pred_count
= 0, proj_count
= 0;
277 counting_predicate
pred(is_negative
, pred_count
);
278 counting_projection
proj(proj_count
);
279 auto expected
= static_cast<int>(in
.size());
282 std::ranges::partition_copy(in
.begin(), in
.end(), out1
.begin(), out2
.begin(), pred
, proj
);
283 assert(pred_count
== expected
);
284 assert(proj_count
== expected
);
285 pred_count
= proj_count
= 0;
289 std::ranges::partition_copy(in
, out1
.begin(), out2
.begin(), pred
, proj
);
290 assert(pred_count
== expected
);
291 assert(proj_count
== expected
);
292 pred_count
= proj_count
= 0;
300 int main(int, char**) {
302 static_assert(test());