1 | //===----------------------------------------------------------------------===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // UNSUPPORTED: c++03, c++11, c++14, c++17 |
10 | |
11 | // <algorithm> |
12 | |
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 |
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 |
28 | |
29 | #include <algorithm> |
30 | #include <array> |
31 | #include <concepts> |
32 | #include <functional> |
33 | #include <ranges> |
34 | #include <utility> |
35 | |
36 | #include "almost_satisfies_types.h" |
37 | #include "counting_predicates.h" |
38 | #include "counting_projection.h" |
39 | #include "test_iterators.h" |
40 | |
41 | struct UnaryPred { bool operator()(int) const; }; |
42 | |
43 | // Test constraints of the (iterator, sentinel) overload. |
44 | // ====================================================== |
45 | |
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)); |
51 | }; |
52 | |
53 | static_assert(HasPartitionCopyIter<int*, int*, int*, int*, UnaryPred>); |
54 | |
55 | // !input_iterator<I> |
56 | static_assert(!HasPartitionCopyIter<InputIteratorNotDerivedFrom>); |
57 | static_assert(!HasPartitionCopyIter<InputIteratorNotIndirectlyReadable>); |
58 | static_assert(!HasPartitionCopyIter<InputIteratorNotInputOrOutputIterator>); |
59 | |
60 | // !sentinel_for<S, I> |
61 | static_assert(!HasPartitionCopyIter<int*, SentinelForNotSemiregular>); |
62 | static_assert(!HasPartitionCopyIter<int*, SentinelForNotWeaklyEqualityComparableWith>); |
63 | |
64 | // !weakly_incrementable<O1> |
65 | static_assert(!HasPartitionCopyIter<int*, int*, WeaklyIncrementableNotMovable>); |
66 | |
67 | // !weakly_incrementable<O2> |
68 | static_assert(!HasPartitionCopyIter<int*, int*, int*, WeaklyIncrementableNotMovable>); |
69 | |
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>); |
73 | |
74 | struct Uncopyable { |
75 | Uncopyable(int&&); |
76 | Uncopyable(const int&) = delete; |
77 | }; |
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*>); |
82 | |
83 | // Test constraints of the (range) overload. |
84 | // ========================================= |
85 | |
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)); |
91 | }; |
92 | |
93 | template <class T> |
94 | using R = UncheckedRange<T>; |
95 | |
96 | static_assert(HasPartitionCopyRange<R<int*>, int*, int*, UnaryPred>); |
97 | |
98 | // !input_iterator<I> |
99 | static_assert(!HasPartitionCopyRange<InputRangeNotDerivedFrom>); |
100 | static_assert(!HasPartitionCopyRange<InputRangeNotIndirectlyReadable>); |
101 | static_assert(!HasPartitionCopyRange<InputRangeNotInputOrOutputIterator>); |
102 | |
103 | // !weakly_incrementable<O1> |
104 | static_assert(!HasPartitionCopyRange<R<int*>, WeaklyIncrementableNotMovable>); |
105 | |
106 | // !weakly_incrementable<O2> |
107 | static_assert(!HasPartitionCopyRange<R<int*>, int*, WeaklyIncrementableNotMovable>); |
108 | |
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>); |
112 | |
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*>); |
117 | |
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>>); |
120 | |
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>; |
126 | |
127 | auto begin = input.data(); |
128 | auto end = input.data() + input.size(); |
129 | |
130 | { // (iterator, sentinel) overload. |
131 | std::array<int, N2> out1; |
132 | std::array<int, N3> out2; |
133 | |
134 | std::same_as<ResultT> decltype(auto) result = std::ranges::partition_copy( |
135 | Iter(begin), Sent(Iter(end)), OutIter1(out1.data()), OutIter2(out2.data()), pred); |
136 | |
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()); |
140 | |
141 | assert(std::ranges::equal(out1, expected_true)); |
142 | assert(std::ranges::equal(out2, expected_false)); |
143 | } |
144 | |
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; |
149 | |
150 | std::same_as<ResultT> decltype(auto) result = std::ranges::partition_copy( |
151 | range, OutIter1(out1.data()), OutIter2(out2.data()), pred); |
152 | |
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()); |
156 | |
157 | assert(std::ranges::equal(out1, expected_true)); |
158 | assert(std::ranges::equal(out2, expected_false)); |
159 | } |
160 | } |
161 | |
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; }; |
165 | |
166 | // Empty sequence. |
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}); |
178 | // Longer sequence. |
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}); |
192 | |
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}); |
196 | } |
197 | |
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*>(); |
203 | } |
204 | |
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*>(); |
211 | } |
212 | |
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>(); |
217 | } |
218 | test_iterators_in_sent<InIter, sentinel_wrapper<InIter>>(); |
219 | } |
220 | |
221 | constexpr void test_iterators() { |
222 | // Note: deliberately testing with only the weakest and "strongest" iterator types to minimize combinatorial |
223 | // explosion. |
224 | test_iterators_in<cpp20_input_iterator<int*>>(); |
225 | test_iterators_in<int*>(); |
226 | } |
227 | |
228 | constexpr bool test() { |
229 | test_iterators(); |
230 | |
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}; |
237 | |
238 | { // (iterator, sentinel) overload. |
239 | { |
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); |
244 | } |
245 | { |
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); |
250 | } |
251 | } |
252 | |
253 | { // (range) overload. |
254 | { |
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); |
259 | } |
260 | { |
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); |
265 | } |
266 | } |
267 | } |
268 | |
269 | { // Complexity: Exactly `last - first` applications of `pred` and `proj`. |
270 | { |
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; |
275 | |
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()); |
280 | |
281 | { |
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; |
286 | } |
287 | |
288 | { |
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; |
293 | } |
294 | } |
295 | } |
296 | |
297 | return true; |
298 | } |
299 | |
300 | int main(int, char**) { |
301 | test(); |
302 | static_assert(test()); |
303 | |
304 | return 0; |
305 | } |
306 | |