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