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 I ranges::max_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 borrowed_iterator_t<R> ranges::max_element(R&& r, Comp comp = {}, Proj proj = {});
21
22 #include <algorithm>
23 #include <array>
24 #include <cassert>
25 #include <functional>
26 #include <random>
27 #include <ranges>
28
29 #include "test_macros.h"
30 #include "test_iterators.h"
31
32 template <class T>
33 concept HasMaxElement = requires (T t) { std::ranges::max_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(HasMaxElement<std::array<int, 0>>);
42 static_assert(!HasMaxElement<int>);
43 static_assert(!HasMaxElement<NoLessThanOp>);
44 static_assert(!HasMaxElement<NotTotallyOrdered>);
45
46 template <class Iter>
test_iterators(Iter first,Iter last)47 constexpr void test_iterators(Iter first, Iter last) {
48 std::same_as<Iter> auto it = std::ranges::max_element(first, last);
49 if (first != last) {
50 for (Iter j = first; j != last; ++j)
51 assert(!(*j > *it));
52 } else {
53 assert(it == first);
54 }
55 }
56
57 template <class Range, class Iter>
test_range(Range && rng,Iter begin,Iter end)58 constexpr void test_range(Range&& rng, Iter begin, Iter end) {
59 std::same_as<Iter> auto it = std::ranges::max_element(std::forward<Range>(rng));
60 if (begin != end) {
61 for (Iter j = begin; j != end; ++j)
62 assert(!(*j > *it));
63 } else {
64 assert(it == begin);
65 }
66 }
67
68 template <class It>
test(std::initializer_list<int> a,int expected)69 constexpr void test(std::initializer_list<int> a, int expected) {
70 const int* first = a.begin();
71 const int* last = a.end();
72 {
73 std::same_as<It> auto it = std::ranges::max_element(It(first), It(last));
74 assert(base(it) == first + expected);
75 }
76 {
77 using Sent = sentinel_wrapper<It>;
78 std::same_as<It> auto it = std::ranges::max_element(It(first), Sent(It(last)));
79 assert(base(it) == first + expected);
80 }
81 {
82 auto range = std::ranges::subrange(It(first), It(last));
83 std::same_as<It> auto it = std::ranges::max_element(range);
84 assert(base(it) == first + expected);
85 }
86 {
87 using Sent = sentinel_wrapper<It>;
88 auto range = std::ranges::subrange(It(first), Sent(It(last)));
89 std::same_as<It> auto it = std::ranges::max_element(range);
90 assert(base(it) == first + expected);
91 }
92 }
93
94 template <class It>
test()95 constexpr bool test() {
96 test<It>({}, 0);
97 test<It>({1}, 0);
98 test<It>({1, 2}, 1);
99 test<It>({2, 1}, 0);
100 test<It>({2, 1, 2}, 0);
101 test<It>({2, 1, 1}, 0);
102
103 return true;
104 }
105
test_borrowed_range_and_sentinel()106 constexpr void test_borrowed_range_and_sentinel() {
107 int a[] = {7, 6, 1, 3, 5, 1, 2, 4};
108
109 int* ret = std::ranges::max_element(std::views::all(a));
110 assert(ret == a + 0);
111 assert(*ret == 7);
112 }
113
test_comparator()114 constexpr void test_comparator() {
115 int a[] = {7, 6, 9, 3, 5, 1, 2, 4};
116 int* ret = std::ranges::max_element(a, std::ranges::greater{});
117 assert(ret == a + 5);
118 assert(*ret == 1);
119 }
120
test_projection()121 constexpr void test_projection() {
122 int a[] = {7, 6, 9, 3, 5, 1, 2, 4};
123 {
124 int* ret = std::ranges::max_element(a, std::ranges::less{}, [](int i) { return i == 5 ? 100 : i; });
125 assert(ret == a + 4);
126 assert(*ret == 5);
127 }
128 {
129 int* ret = std::ranges::max_element(a, std::less<int*>{}, [](int& i) { return &i; });
130 assert(ret == a + 7);
131 assert(*ret == 4);
132 }
133 }
134
135 struct Immobile {
136 int i;
137
ImmobileImmobile138 constexpr Immobile(int i_) : i(i_) {}
139 Immobile(const Immobile&) = delete;
140 Immobile(Immobile&&) = delete;
141
142 auto operator<=>(const Immobile&) const = default;
143 };
144
test_immobile()145 constexpr void test_immobile() {
146
147 Immobile arr[] {1, 2, 3};
148 assert(std::ranges::max_element(arr) == arr);
149 assert(std::ranges::max_element(arr, arr + 3) == arr);
150 }
151
test_dangling()152 constexpr void test_dangling() {
153 int compares = 0;
154 int projections = 0;
155 auto comparator = [&](int a, int b) {
156 ++compares;
157 return a < b;
158 };
159 auto projection = [&](int a) {
160 ++projections;
161 return a;
162 };
163 [[maybe_unused]] std::same_as<std::ranges::dangling> auto ret =
164 std::ranges::max_element(std::array{1, 2, 3}, comparator, projection);
165 assert(compares == 2);
166 assert(projections == 4);
167 }
168
test()169 constexpr bool test() {
170
171 test<forward_iterator<const int*>>();
172 test<bidirectional_iterator<const int*>>();
173 test<random_access_iterator<const int*>>();
174 test<const int*>();
175
176 int a[] = {7, 6, 5, 3, 4, 2, 1, 8};
177 test_iterators(a, a + 8);
178 int a2[] = {7, 6, 5, 3, 4, 2, 1, 8};
179 test_range(a2, a2, a2 + 8);
180
181 test_borrowed_range_and_sentinel();
182 test_comparator();
183 test_projection();
184 test_dangling();
185
186 return true;
187 }
188
main(int,char **)189 int main(int, char**) {
190 test();
191 static_assert(test());
192
193 return 0;
194 }
195