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