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 // <algorithm>
10
11 // UNSUPPORTED: c++03, c++11, c++14, c++17
12 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
13
14 // template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
15 // indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
16 // constexpr ranges::minmax_element_result<I> ranges::minmax_element(I first, S last, Comp comp = {}, Proj proj = {});
17 //
18 // template<forward_range R, class Proj = identity,
19 // indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
20 // constexpr ranges::minmax_element_result<borrowed_iterator_t<R>>
21 // ranges::minmax_element(R&& r, Comp comp = {}, Proj proj = {});
22
23 #include <algorithm>
24 #include <array>
25 #include <cassert>
26 #include <functional>
27 #include <ranges>
28
29 #include "almost_satisfies_types.h"
30 #include "test_iterators.h"
31
32 template <class T>
33 concept HasMinMaxElementR = requires(T t) { std::ranges::minmax_element(t); };
34
35 struct NoLessThanOp {};
36 struct NotTotallyOrdered {
37 int i;
operator <NotTotallyOrdered38 bool operator<(const NotTotallyOrdered& o) const { return i < o.i; }
39 };
40
41 static_assert(HasMinMaxElementR<int (&)[10]>); // make sure HasMinMaxElementR works with an array
42 static_assert(!HasMinMaxElementR<NoLessThanOp (&)[10]>);
43 static_assert(!HasMinMaxElementR<NotTotallyOrdered (&)[10]>);
44
45 static_assert(HasMinMaxElementR<std::initializer_list<int>>); // make sure HasMinMaxElementR works with an initializer_list
46 static_assert(!HasMinMaxElementR<std::initializer_list<NoLessThanOp>>);
47 static_assert(!HasMinMaxElementR<std::initializer_list<NotTotallyOrdered>>);
48 static_assert(!HasMinMaxElementR<InputRangeNotDerivedFrom>);
49 static_assert(!HasMinMaxElementR<InputRangeNotIndirectlyReadable>);
50 static_assert(!HasMinMaxElementR<InputRangeNotInputOrOutputIterator>);
51 static_assert(!HasMinMaxElementR<InputRangeNotSentinelSemiregular>);
52 static_assert(!HasMinMaxElementR<InputRangeNotSentinelEqualityComparableWith>);
53
54 template <class It, class Sent = sentinel_wrapper<It>>
55 concept HasMinMaxElementIt = requires(It it, Sent sent) { std::ranges::minmax_element(it, sent); };
56 static_assert(HasMinMaxElementIt<int*>); // make sure HasMinMaxElementIt works
57 static_assert(!HasMinMaxElementIt<InputIteratorNotDerivedFrom>);
58 static_assert(!HasMinMaxElementIt<InputIteratorNotIndirectlyReadable>);
59 static_assert(!HasMinMaxElementIt<InputIteratorNotInputOrOutputIterator>);
60 static_assert(!HasMinMaxElementIt<int*, SentinelForNotSemiregular>);
61 static_assert(!HasMinMaxElementIt<int*, SentinelForNotWeaklyEqualityComparableWith>);
62
63 static_assert(std::is_same_v<std::ranges::minmax_element_result<int>, std::ranges::min_max_result<int>>);
64
65 template <class It>
test_iterators(std::initializer_list<int> a,int expectedMin,int expectedMax)66 constexpr void test_iterators(std::initializer_list<int> a, int expectedMin, int expectedMax) {
67 using Expected = std::ranges::minmax_element_result<It>;
68 const int* first = a.begin();
69 const int* last = a.end();
70 {
71 std::same_as<Expected> auto it = std::ranges::minmax_element(It(first), It(last));
72 assert(base(it.min) == first + expectedMin);
73 assert(base(it.max) == first + expectedMax);
74 }
75 {
76 using Sent = sentinel_wrapper<It>;
77 std::same_as<Expected> auto it = std::ranges::minmax_element(It(first), Sent(It(last)));
78 assert(base(it.min) == first + expectedMin);
79 assert(base(it.max) == first + expectedMax);
80 }
81 {
82 auto range = std::ranges::subrange(It(first), It(last));
83 std::same_as<Expected> auto it = std::ranges::minmax_element(range);
84 assert(base(it.min) == first + expectedMin);
85 assert(base(it.max) == first + expectedMax);
86 }
87 {
88 using Sent = sentinel_wrapper<It>;
89 auto range = std::ranges::subrange(It(first), Sent(It(last)));
90 std::same_as<Expected> auto it = std::ranges::minmax_element(range);
91 assert(base(it.min) == first + expectedMin);
92 assert(base(it.max) == first + expectedMax);
93 }
94 }
95
96 template <class It>
test_iterators()97 constexpr bool test_iterators() {
98 test_iterators<It>({}, 0, 0);
99 test_iterators<It>({1}, 0, 0);
100 test_iterators<It>({1, 2}, 0, 1);
101 test_iterators<It>({2, 1}, 1, 0);
102 test_iterators<It>({2, 1, 2}, 1, 2);
103 test_iterators<It>({2, 1, 1}, 1, 0);
104 test_iterators<It>({2, 2, 1}, 2, 1);
105
106 return true;
107 }
108
test_borrowed_range_and_sentinel()109 constexpr void test_borrowed_range_and_sentinel() {
110 int a[] = {7, 6, 1, 3, 5, 1, 2, 4};
111
112 std::ranges::minmax_element_result<int*> ret = std::ranges::minmax_element(std::views::all(a));
113 assert(ret.min == a + 2);
114 assert(ret.max == a + 0);
115 assert(*ret.min == 1);
116 assert(*ret.max == 7);
117 }
118
test_comparator()119 constexpr void test_comparator() {
120 int a[] = {7, 6, 9, 3, 5, 1, 2, 4};
121 std::ranges::minmax_element_result<int*> ret = std::ranges::minmax_element(a, std::ranges::greater{});
122 assert(ret.min == a + 2);
123 assert(ret.max == a + 5);
124 assert(*ret.min == 9);
125 assert(*ret.max == 1);
126 }
127
test_projection()128 constexpr void test_projection() {
129 {
130 int a[] = {7, 6, 9, 3, 5, 1, 2, 4};
131 std::ranges::minmax_element_result<int*> ret =
132 std::ranges::minmax_element(a, std::ranges::less{}, [](int i) { return i == 5 ? -100 : i; });
133 assert(ret.min == a + 4);
134 assert(ret.max == a + 2);
135 assert(*ret.min == 5);
136 assert(*ret.max == 9);
137 }
138 {
139 int a[] = {7, 6, 9, 3, 5, 1, 2, 4};
140 std::ranges::minmax_element_result<int*> ret =
141 std::ranges::minmax_element(a, std::less<int*>{}, [](int& i) { return &i; });
142 assert(ret.min == a + 0);
143 assert(ret.max == a + 7);
144 assert(*ret.min == 7);
145 assert(*ret.max == 4);
146 }
147 }
148
149 struct Immobile {
150 int i;
151
ImmobileImmobile152 constexpr Immobile(int i_) : i(i_) {}
153 Immobile(const Immobile&) = delete;
154 Immobile(Immobile&&) = delete;
155
156 auto operator<=>(const Immobile&) const = default;
157 };
158
test_immobile()159 constexpr void test_immobile() {
160 {
161 Immobile arr[]{1, 2, 3};
162 auto ret = std::ranges::minmax_element(arr);
163 assert(ret.min == arr + 0);
164 assert(ret.max == arr + 2);
165 }
166 {
167 Immobile arr[]{1, 2, 3};
168 auto ret = std::ranges::minmax_element(arr, arr + 3);
169 assert(ret.min == arr + 0);
170 assert(ret.max == arr + 2);
171 }
172 }
173
test_dangling()174 constexpr void test_dangling() {
175 int compares = 0;
176 int projections = 0;
177 auto comparator = [&](int a, int b) {
178 ++compares;
179 return a < b;
180 };
181 auto projection = [&](int a) {
182 ++projections;
183 return a;
184 };
185 [[maybe_unused]] std::same_as<std::ranges::minmax_element_result<std::ranges::dangling>> auto ret =
186 std::ranges::minmax_element(std::array{1, 2, 3}, comparator, projection);
187 assert(compares == 3);
188 assert(projections == 6);
189 }
190
test()191 constexpr bool test() {
192 test_iterators<forward_iterator<const int*>>();
193 test_iterators<bidirectional_iterator<const int*>>();
194 test_iterators<random_access_iterator<const int*>>();
195 test_iterators<contiguous_iterator<const int*>>();
196 test_iterators<const int*>();
197 test_iterators<int*>();
198
199 test_borrowed_range_and_sentinel();
200 test_comparator();
201 test_projection();
202 test_dangling();
203
204 { // check that std::invoke is used
205 {
206 struct S {
207 int i;
208 };
209 S b[3] = {S{2}, S{1}, S{3}};
210 std::same_as<std::ranges::minmax_element_result<S*>> auto ret = std::ranges::minmax_element(b, {}, &S::i);
211 assert(ret.min->i == 1);
212 assert(ret.max->i == 3);
213 assert(ret.min == b + 1);
214 assert(ret.max == b + 2);
215 }
216 {
217 struct S {
218 int i;
219 };
220 S b[3] = {S{2}, S{1}, S{3}};
221 std::same_as<std::ranges::minmax_element_result<S*>> auto ret = std::ranges::minmax_element(b, b + 3, {}, &S::i);
222 assert(ret.min->i == 1);
223 assert(ret.max->i == 3);
224 assert(ret.min == b + 1);
225 assert(ret.max == b + 2);
226 }
227 }
228
229 { // check that the leftmost value for min an rightmost for max are returned
230 {
231 struct S {
232 int comp;
233 int other;
234 };
235 S a[] = { {0, 0}, {0, 2}, {0, 1} };
236 auto ret = std::ranges::minmax_element(a, a + 3, {}, &S::comp);
237 assert(ret.min->comp == 0);
238 assert(ret.max->comp == 0);
239 assert(ret.min->other == 0);
240 assert(ret.max->other == 1);
241 }
242 {
243 struct S {
244 int comp;
245 int other;
246 };
247 S a[] = { {0, 0}, {0, 2}, {0, 1} };
248 auto ret = std::ranges::minmax_element(a, {}, &S::comp);
249 assert(ret.min->comp == 0);
250 assert(ret.max->comp == 0);
251 assert(ret.min->other == 0);
252 assert(ret.max->other == 1);
253 }
254 }
255
256 { // check that an empty range works
257 {
258 std::array<int, 0> a = {};
259 auto ret = std::ranges::minmax_element(a.begin(), a.end());
260 assert(ret.min == a.begin());
261 assert(ret.max == a.begin());
262 }
263 {
264 std::array<int, 0> a = {};
265 auto ret = std::ranges::minmax_element(a);
266 assert(ret.min == a.begin());
267 assert(ret.max == a.begin());
268 }
269 }
270
271 { // check in ascending order
272 {
273 int a[] = {1, 2, 3};
274 auto ret = std::ranges::minmax_element(a, a + 3);
275 assert(ret.min == a + 0);
276 assert(ret.max == a + 2);
277 assert(*ret.min == 1);
278 assert(*ret.max == 3);
279 }
280 {
281 int a[] = {1, 2, 3};
282 auto ret = std::ranges::minmax_element(a);
283 assert(ret.min == a + 0);
284 assert(ret.max == a + 2);
285 assert(*ret.min == 1);
286 assert(*ret.max == 3);
287 }
288 }
289
290 { // check in descending order
291 {
292 int a[] = {3, 2, 1};
293 auto ret = std::ranges::minmax_element(a, a + 3);
294 assert(ret.min == a + 2);
295 assert(ret.max == a + 0);
296 assert(*ret.min == 1);
297 assert(*ret.max == 3);
298 }
299 {
300 int a[] = {3, 2, 1};
301 auto ret = std::ranges::minmax_element(a);
302 assert(ret.min == a + 2);
303 assert(ret.max == a + 0);
304 assert(*ret.min == 1);
305 assert(*ret.max == 3);
306 }
307 }
308
309 return true;
310 }
311
main(int,char **)312 int main(int, char**) {
313 test();
314 static_assert(test());
315
316 return 0;
317 }
318