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
37template <class T>
38concept HasMaxR = requires { std::ranges::max(std::declval<T>()); };
39
40struct NoLessThanOp {};
41struct NotTotallyOrdered {
42 int i;
43 bool operator<(const NotTotallyOrdered& o) const { return i < o.i; }
44};
45
46struct Movable {
47 Movable& operator=(Movable&&) = default;
48 Movable(Movable&&) = default;
49 Movable(const Movable&) = delete;
50};
51
52static_assert(!HasMaxR<int>);
53
54static_assert(HasMaxR<int(&)[10]>);
55static_assert(HasMaxR<int(&&)[10]>);
56static_assert(!HasMaxR<NoLessThanOp(&)[10]>);
57static_assert(!HasMaxR<NotTotallyOrdered(&)[10]>);
58static_assert(!HasMaxR<Movable(&)[10]>);
59
60static_assert(HasMaxR<std::initializer_list<int>>);
61static_assert(!HasMaxR<std::initializer_list<NoLessThanOp>>);
62static_assert(!HasMaxR<std::initializer_list<NotTotallyOrdered>>);
63static_assert(!HasMaxR<std::initializer_list<Movable>>);
64static_assert(!HasMaxR<InputRangeNotDerivedFrom>);
65static_assert(!HasMaxR<InputRangeNotIndirectlyReadable>);
66static_assert(!HasMaxR<InputRangeNotInputOrOutputIterator>);
67static_assert(!HasMaxR<InputRangeNotSentinelSemiregular>);
68static_assert(!HasMaxR<InputRangeNotSentinelEqualityComparableWith>);
69
70template <class T, class U = T>
71concept HasMax2 = requires { std::ranges::max(std::declval<T>(), std::declval<U>()); };
72
73static_assert(HasMax2<int>);
74static_assert(!HasMax2<int, long>);
75
76static_assert(std::is_same_v<decltype(std::ranges::max(1, 2)), const int&>);
77
78constexpr 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{.i: 2}, S{.i: 1}, S{.i: 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
126constexpr 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
177template <class It, class Sent = It>
178constexpr 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
185constexpr 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{.i: 2}, S{.i: 1}, S{.i: 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{.check: 1, .other: 1}, S{.check: 0, .other: 2}, S{.check: 1, .other: 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{.check: 0, .other: 1}, S{.check: 1, .other: 2}, S{.check: 1, .other: 3} };
260 auto ret = std::ranges::max(b, {}, &S::check);
261 assert(ret.check == 1);
262 assert(ret.other == 2);
263 }
264 }
265}
266
267constexpr bool test() {
268 test_2_arguments();
269 test_initializer_list();
270 test_range();
271
272 return true;
273}
274
275int main(int, char**) {
276 test();
277 static_assert(test());
278
279 return 0;
280}
281

source code of libcxx/test/std/algorithms/alg.sorting/alg.min.max/ranges.max.pass.cpp