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