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<bidirectional_iterator I, sentinel_for<I> S, class Proj = identity,
14 // indirect_unary_predicate<projected<I, Proj>> Pred>
15 // requires permutable<I>
16 // subrange<I> stable_partition(I first, S last, Pred pred, Proj proj = {}); // Since C++20
18 // template<bidirectional_range R, class Proj = identity,
19 // indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
20 // requires permutable<iterator_t<R>>
21 // borrowed_subrange_t<R> stable_partition(R&& r, Pred pred, Proj proj = {}); // Since C++20
29 #include "almost_satisfies_types.h"
30 #include "test_iterators.h"
32 struct UnaryPred
{ bool operator()(int) const; };
34 // Test constraints of the (iterator, sentinel) overload.
35 // ======================================================
37 template <class Iter
= int*, class Sent
= int*, class Pred
= UnaryPred
>
38 concept HasStablePartitionIter
=
39 requires(Iter
&& iter
, Sent
&& sent
, Pred
&& pred
) {
40 std::ranges::stable_partition(std::forward
<Iter
>(iter
), std::forward
<Sent
>(sent
), std::forward
<Pred
>(pred
));
43 static_assert(HasStablePartitionIter
<int*, int*, UnaryPred
>);
45 // !bidirectional_iterator<I>
46 static_assert(!HasStablePartitionIter
<BidirectionalIteratorNotDerivedFrom
>);
47 static_assert(!HasStablePartitionIter
<BidirectionalIteratorNotDecrementable
>);
49 // !sentinel_for<S, I>
50 static_assert(!HasStablePartitionIter
<int*, SentinelForNotSemiregular
>);
51 static_assert(!HasStablePartitionIter
<int*, SentinelForNotWeaklyEqualityComparableWith
>);
53 // !indirect_unary_predicate<projected<I, Proj>>
54 static_assert(!HasStablePartitionIter
<int*, int*, IndirectUnaryPredicateNotPredicate
>);
55 static_assert(!HasStablePartitionIter
<int*, int*, IndirectUnaryPredicateNotCopyConstructible
>);
58 static_assert(!HasStablePartitionIter
<PermutableNotForwardIterator
>);
59 static_assert(!HasStablePartitionIter
<PermutableNotSwappable
>);
61 // Test constraints of the (range) overload.
62 // =========================================
64 template <class Range
, class Pred
>
65 concept HasStablePartitionRange
=
66 requires(Range
&& range
, Pred
&& pred
) {
67 std::ranges::stable_partition(std::forward
<Range
>(range
), std::forward
<Pred
>(pred
));
71 using R
= UncheckedRange
<T
>;
73 static_assert(HasStablePartitionRange
<R
<int*>, UnaryPred
>);
75 // !bidirectional_range<R>
76 static_assert(!HasStablePartitionRange
<BidirectionalRangeNotDerivedFrom
, UnaryPred
>);
77 static_assert(!HasStablePartitionRange
<BidirectionalRangeNotDecrementable
, UnaryPred
>);
79 // !indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
80 static_assert(!HasStablePartitionRange
<R
<int*>, IndirectUnaryPredicateNotPredicate
>);
81 static_assert(!HasStablePartitionRange
<R
<int*>, IndirectUnaryPredicateNotCopyConstructible
>);
83 // !permutable<iterator_t<R>>
84 static_assert(!HasStablePartitionRange
<R
<PermutableNotForwardIterator
>, UnaryPred
>);
85 static_assert(!HasStablePartitionRange
<R
<PermutableNotSwappable
>, UnaryPred
>);
87 template <class Iter
, class Sent
, std::size_t N
, class Pred
>
88 void test_one(std::array
<int, N
> input
, Pred pred
, std::size_t partition_point
, std::array
<int, N
> expected
) {
89 auto neg_pred
= [&](int x
) { return !pred(x
); };
91 { // (iterator, sentinel) overload.
92 auto partitioned
= input
;
93 auto b
= Iter(partitioned
.data());
94 auto e
= Sent(Iter(partitioned
.data() + partitioned
.size()));
96 std::same_as
<std::ranges::subrange
<Iter
>> decltype(auto) result
= std::ranges::stable_partition(b
, e
, pred
);
98 assert(partitioned
== expected
);
99 assert(base(result
.begin()) == partitioned
.data() + partition_point
);
100 assert(base(result
.end()) == partitioned
.data() + partitioned
.size());
102 assert(std::ranges::all_of(b
, result
.begin(), pred
));
103 assert(std::ranges::all_of(result
.begin(), e
, neg_pred
));
106 { // (range) overload.
107 auto partitioned
= input
;
108 auto b
= Iter(partitioned
.data());
109 auto e
= Sent(Iter(partitioned
.data() + partitioned
.size()));
110 auto range
= std::ranges::subrange(b
, e
);
112 std::same_as
<std::ranges::subrange
<Iter
>> decltype(auto) result
= std::ranges::stable_partition(range
, pred
);
114 assert(partitioned
== expected
);
115 assert(base(result
.begin()) == partitioned
.data() + partition_point
);
116 assert(base(result
.end()) == partitioned
.data() + partitioned
.size());
118 assert(std::ranges::all_of(b
, result
.begin(), pred
));
119 assert(std::ranges::all_of(result
.begin(), e
, neg_pred
));
123 template <class Iter
, class Sent
>
124 void test_iterators_2() {
125 auto is_odd
= [](int x
) { return x
% 2 != 0; };
128 test_one
<Iter
, Sent
, 0>({}, is_odd
, 0, {});
129 // 1-element sequence, the element satisfies the predicate.
130 test_one
<Iter
, Sent
, 1>({1}, is_odd
, 1, {1});
131 // 1-element sequence, the element doesn't satisfy the predicate.
132 test_one
<Iter
, Sent
, 1>({2}, is_odd
, 0, {2});
133 // 2-element sequence, not in order.
134 test_one
<Iter
, Sent
, 2>({2, 1}, is_odd
, 1, {1, 2});
135 // 2-element sequence, already in order.
136 test_one
<Iter
, Sent
, 2>({1, 2}, is_odd
, 1, {1, 2});
137 // 3-element sequence.
138 test_one
<Iter
, Sent
, 3>({2, 1, 3}, is_odd
, 2, {1, 3, 2});
140 test_one
<Iter
, Sent
, 8>({2, 1, 3, 6, 8, 4, 11, 5}, is_odd
, 4, {1, 3, 11, 5, 2, 6, 8, 4});
141 // Longer sequence with duplicates.
142 test_one
<Iter
, Sent
, 8>({2, 1, 3, 6, 2, 8, 1, 6}, is_odd
, 3, {1, 3, 1, 2, 6, 2, 8, 6});
143 // All elements are the same and satisfy the predicate.
144 test_one
<Iter
, Sent
, 3>({1, 1, 1}, is_odd
, 3, {1, 1, 1});
145 // All elements are the same and don't satisfy the predicate.
146 test_one
<Iter
, Sent
, 3>({2, 2, 2}, is_odd
, 0, {2, 2, 2});
147 // Already partitioned.
148 test_one
<Iter
, Sent
, 6>({1, 3, 5, 4, 6, 8}, is_odd
, 3, {1, 3, 5, 4, 6, 8});
149 // Reverse-partitioned.
150 test_one
<Iter
, Sent
, 6>({4, 6, 8, 1, 3, 5}, is_odd
, 3, {1, 3, 5, 4, 6, 8});
151 // Repeating pattern.
152 test_one
<Iter
, Sent
, 6>({1, 2, 1, 2, 1, 2}, is_odd
, 3, {1, 1, 1, 2, 2, 2});
154 auto is_negative
= [](int x
) { return x
< 0; };
155 // Different comparator.
156 test_one
<Iter
, Sent
, 5>({-3, 5, 7, -6, 2}, is_negative
, 2, {-3, -6, 5, 7, 2});
159 template <class Iter
>
160 void test_iterators_1() {
161 test_iterators_2
<Iter
, Iter
>();
162 test_iterators_2
<Iter
, sentinel_wrapper
<Iter
>>();
165 void test_iterators() {
166 test_iterators_1
<bidirectional_iterator
<int*>>();
167 test_iterators_1
<random_access_iterator
<int*>>();
168 test_iterators_1
<contiguous_iterator
<int*>>();
169 test_iterators_1
<int*>();
175 { // The algorithm is stable (equivalent elements remain in the same order).
176 struct OrderedValue
{
178 double original_order
;
179 bool operator==(const OrderedValue
&) const = default;
182 auto is_odd
= [](OrderedValue x
) { return x
.value
% 2 != 0; };
184 using V
= OrderedValue
;
185 using Array
= std::array
<V
, 20>;
187 V
{10, 2.1}, {12, 2.2}, {3, 1.1}, {5, 1.2}, {3, 1.3}, {3, 1.4}, {11, 1.5}, {12, 2.3}, {4, 2.4}, {4, 2.5},
188 {4, 2.6}, {1, 1.6}, {6, 2.7}, {3, 1.7}, {10, 2.8}, {8, 2.9}, {12, 2.10}, {1, 1.8}, {1, 1.9}, {5, 1.10}
191 V
{3, 1.1}, {5, 1.2}, {3, 1.3}, {3, 1.4}, {11, 1.5}, {1, 1.6}, {3, 1.7}, {1, 1.8}, {1, 1.9}, {5, 1.10},
192 {10, 2.1}, {12, 2.2}, {12, 2.3}, {4, 2.4}, {4, 2.5}, {4, 2.6}, {6, 2.7}, {10, 2.8}, {8, 2.9}, {12, 2.10}
197 std::ranges::stable_partition(in
.begin(), in
.end(), is_odd
);
198 assert(in
== expected
);
203 std::ranges::stable_partition(in
, is_odd
);
204 assert(in
== expected
);
208 { // A custom projection works.
209 const std::array input
= {1, -1};
210 auto is_negative
= [](int x
) { return x
< 0; };
211 auto negate
= [](int x
) { return -x
; };
212 const std::array expected_no_proj
= {-1, 1};
213 const std::array expected_with_proj
= {1, -1};
215 { // (iterator, sentinel) overload.
218 std::ranges::partition(in
.begin(), in
.end(), is_negative
);
219 assert(in
== expected_no_proj
);
223 std::ranges::partition(in
.begin(), in
.end(), is_negative
, negate
);
224 assert(in
== expected_with_proj
);
228 { // (range) overload.
231 std::ranges::partition(in
, is_negative
);
232 assert(in
== expected_no_proj
);
236 std::ranges::partition(in
, is_negative
, negate
);
237 assert(in
== expected_with_proj
);
243 int main(int, char**) {
245 // Note: `stable_partition` is not `constexpr`.