xref: /llvm-project/libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.max.pass.cpp (revision aff3cdc604b01d3db1674e8d1402a5e5727fa087)
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 
13 // template<class T, class Proj = identity,
14 //          indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
15 //   constexpr const T& ranges::max(const T& a, const T& b, Comp comp = {}, Proj proj = {});
16 //
17 // template<copyable T, class Proj = identity,
18 //          indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
19 //   constexpr T ranges::max(initializer_list<T> r, Comp comp = {}, Proj proj = {});
20 //
21 // template<input_range R, class Proj = identity,
22 //          indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
23 //   requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*>
24 //   constexpr range_value_t<R>
25 //     ranges::max(R&& r, Comp comp = {}, Proj proj = {});
26 
27 #include <algorithm>
28 #include <array>
29 #include <cassert>
30 #include <functional>
31 #include <ranges>
32 
33 #include "almost_satisfies_types.h"
34 #include "test_iterators.h"
35 #include "test_macros.h"
36 
37 template <class T>
38 concept HasMaxR = requires { std::ranges::max(std::declval<T>()); };
39 
40 struct NoLessThanOp {};
41 struct NotTotallyOrdered {
42   int i;
operator <NotTotallyOrdered43   bool operator<(const NotTotallyOrdered& o) const { return i < o.i; }
44 };
45 
46 struct Movable {
47   Movable& operator=(Movable&&) = default;
48   Movable(Movable&&) = default;
49   Movable(const Movable&) = delete;
50 };
51 
52 static_assert(!HasMaxR<int>);
53 
54 static_assert(HasMaxR<int(&)[10]>);
55 static_assert(HasMaxR<int(&&)[10]>);
56 static_assert(!HasMaxR<NoLessThanOp(&)[10]>);
57 static_assert(!HasMaxR<NotTotallyOrdered(&)[10]>);
58 static_assert(!HasMaxR<Movable(&)[10]>);
59 
60 static_assert(HasMaxR<std::initializer_list<int>>);
61 static_assert(!HasMaxR<std::initializer_list<NoLessThanOp>>);
62 static_assert(!HasMaxR<std::initializer_list<NotTotallyOrdered>>);
63 static_assert(!HasMaxR<std::initializer_list<Movable>>);
64 static_assert(!HasMaxR<InputRangeNotDerivedFrom>);
65 static_assert(!HasMaxR<InputRangeNotIndirectlyReadable>);
66 static_assert(!HasMaxR<InputRangeNotInputOrOutputIterator>);
67 static_assert(!HasMaxR<InputRangeNotSentinelSemiregular>);
68 static_assert(!HasMaxR<InputRangeNotSentinelEqualityComparableWith>);
69 
70 template <class T, class U = T>
71 concept HasMax2 = requires { std::ranges::max(std::declval<T>(), std::declval<U>()); };
72 
73 static_assert(HasMax2<int>);
74 static_assert(!HasMax2<int, long>);
75 
76 static_assert(std::is_same_v<decltype(std::ranges::max(1, 2)), const int&>);
77 
test_2_arguments()78 constexpr void test_2_arguments() {
79   assert(std::ranges::max(1, 2) == 2);
80   assert(std::ranges::max(2, 1) == 2);
81   // test comparator
82   assert(std::ranges::max(1, 2, std::ranges::greater{}) == 1);
83   // test projection
84   assert(std::ranges::max(1, 2, std::ranges::less{}, [](int i){ return i == 1 ? 10 : i; }) == 1);
85 
86   { // check that std::invoke is used
87     struct S { int i; };
88     S a[3] = { S{2}, S{1}, S{3} };
89     decltype(auto) ret = std::ranges::max(a[0], a[1], {}, &S::i);
90     ASSERT_SAME_TYPE(decltype(ret), const S&);
91     assert(&ret == &a[0]);
92     assert(ret.i == 2);
93   }
94 
95   { // check that pointers are compared and not a range
96     int i[1];
97     int* a[] = {i, i + 1};
98     auto ret = std::ranges::max(a[0], a[1]);
99     assert(ret == i + 1);
100   }
101 
102   { // test predicate and projection count
103     int compares = 0;
104     int projections = 0;
105     auto comparator = [&](int x, int y) {
106       ++compares;
107       return x < y;
108     };
109     auto projection = [&](int x) {
110       ++projections;
111       return x;
112     };
113     auto ret = std::ranges::max(1, 2, comparator, projection);
114     assert(ret == 2);
115     assert(compares == 1);
116     assert(projections == 2);
117   }
118 
119   { // check that the first argument is returned
120     struct S { int check; int other; };
121     auto ret = std::ranges::max(S {0, 1}, S {0, 2}, {}, &S::check);
122     assert(ret.other == 1);
123   }
124 }
125 
test_initializer_list()126 constexpr void test_initializer_list() {
127   { // test projection
128     auto proj = [](int i) { return i == 5 ? 100 : i; };
129     int ret = std::ranges::max({7, 6, 9, 3, 5, 1, 2, 4}, {}, proj);
130     assert(ret == 5);
131   }
132 
133   { // test comparator
134     int ret = std::ranges::max({7, 6, 9, 3, 5, 1, 2, 4}, std::ranges::greater{});
135     assert(ret == 1);
136   }
137 
138   { // check that complexity requirements are met
139     int compares = 0;
140     int projections = 0;
141     auto comparator = [&](int a, int b) {
142       ++compares;
143       return a < b;
144     };
145     auto projection = [&](int a) {
146       ++projections;
147       return a;
148     };
149     std::same_as<int> decltype(auto) ret = std::ranges::max({1, 2, 3}, comparator, projection);
150     assert(ret == 3);
151     assert(compares == 2);
152     assert(projections == 4);
153   }
154 
155   { // check that std::invoke is used
156     struct S { int i; };
157     std::same_as<S> decltype(auto) ret = std::ranges::max({ S{2}, S{1}, S{3} }, {}, &S::i);
158     assert(ret.i == 3);
159   }
160 
161   { // check that the first largest element is returned
162     { // where the first element is the largest
163       struct S { int check; int other; };
164       auto ret = std::ranges::max({ S{1, 1}, S{0, 2}, S{1, 3} }, {}, &S::check);
165       assert(ret.check == 1);
166       assert(ret.other == 1);
167     }
168     { // where the first element isn't the largest
169       struct S { int check; int other; };
170       auto ret = std::ranges::max({ S{0, 1}, S{1, 2}, S{1, 3} }, {}, &S::check);
171       assert(ret.check == 1);
172       assert(ret.other == 2);
173     }
174   }
175 }
176 
177 template <class It, class Sent = It>
test_range_types()178 constexpr void test_range_types() {
179   std::iter_value_t<It> a[] = {7, 6, 9, 3, 5, 1, 2, 4};
180   auto range = std::ranges::subrange(It(a), Sent(It(a + 8)));
181   auto ret = std::ranges::max(range);
182   assert(ret == 9);
183 }
184 
test_range()185 constexpr void test_range() {
186   // check that all range types work
187   {
188     struct NonTrivialInt {
189       int val_;
190       constexpr NonTrivialInt(int val) : val_(val) {}
191       constexpr NonTrivialInt(const NonTrivialInt& other) : val_(other.val_) {}
192       constexpr NonTrivialInt& operator=(const NonTrivialInt& other) {
193         val_ = other.val_;
194         return *this;
195       }
196 
197       constexpr ~NonTrivialInt() {}
198 
199       auto operator<=>(const NonTrivialInt&) const = default;
200     };
201 
202     auto call_with_sentinels = []<class Iter> {
203       if constexpr (std::forward_iterator<Iter>)
204         test_range_types<Iter, Iter>();
205       test_range_types<Iter, sentinel_wrapper<Iter>>();
206       test_range_types<Iter, sized_sentinel<Iter>>();
207     };
208 
209     types::for_each(types::cpp20_input_iterator_list<int*>{}, call_with_sentinels);
210     types::for_each(types::cpp20_input_iterator_list<NonTrivialInt*>{}, call_with_sentinels);
211   }
212 
213   int a[] = {7, 6, 9, 3, 5, 1, 2, 4};
214   { // test projection
215     auto proj = [](int& i) { return i == 5 ? 100 : i; };
216     int ret = std::ranges::max(a, std::ranges::less{}, proj);
217     assert(ret == 5);
218   }
219 
220   { // test comparator
221     int ret = std::ranges::max(a, std::ranges::greater{});
222     assert(ret == 1);
223   }
224 
225   { // check that predicate and projection call counts are correct
226     int compares = 0;
227     int projections = 0;
228     auto comparator = [&](int x, int y) {
229       ++compares;
230       return x < y;
231     };
232     auto projection = [&](int x) {
233       ++projections;
234       return x;
235     };
236     std::same_as<int> decltype(auto) ret = std::ranges::max(std::array{1, 2, 3}, comparator, projection);
237     assert(ret == 3);
238     assert(compares == 2);
239     assert(projections == 4);
240   }
241 
242   { // check that std::invoke is used
243     struct S { int i; };
244     S b[3] = { S{2}, S{1}, S{3} };
245     std::same_as<S> decltype(auto) ret = std::ranges::max(b, {}, &S::i);
246     assert(ret.i == 3);
247   }
248 
249   { // check that the first largest element is returned
250     { // where the first element is the largest
251       struct S { int check; int other; };
252       S b[] = { S{1, 1}, S{0, 2}, S{1, 3} };
253       auto ret = std::ranges::max(b, {}, &S::check);
254       assert(ret.check == 1);
255       assert(ret.other == 1);
256     }
257     { // where the first element isn't the largest
258       struct S { int check; int other; };
259       S b[] = { S{0, 1}, S{1, 2}, S{1, 3} };
260       auto ret = std::ranges::max(b, {}, &S::check);
261       assert(ret.check == 1);
262       assert(ret.other == 2);
263     }
264   }
265 }
266 
test()267 constexpr bool test() {
268   test_2_arguments();
269   test_initializer_list();
270   test_range();
271 
272   return true;
273 }
274 
main(int,char **)275 int main(int, char**) {
276   test();
277   static_assert(test());
278 
279   return 0;
280 }
281