xref: /llvm-project/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count.pass.cpp (revision b8cb1dc9ea87faa8e8e9ab7a31710a8c0bb8b084)
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<input_iterator I, sentinel_for<I> S, class T, class Proj = identity>
14 //   requires indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>
15 //   constexpr iter_difference_t<I>
16 //     ranges::count(I first, S last, const T& value, Proj proj = {});
17 // template<input_range R, class T, class Proj = identity>
18 //   requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*>
19 //   constexpr range_difference_t<R>
20 //     ranges::count(R&& r, const T& value, Proj proj = {});
21 
22 #include <algorithm>
23 #include <array>
24 #include <cassert>
25 #include <ranges>
26 
27 #include "almost_satisfies_types.h"
28 #include "test_iterators.h"
29 
30 struct NotEqualityComparable {
31   bool operator==(NotEqualityComparable&&) const;
32   bool operator==(NotEqualityComparable&) const;
33   bool operator==(const NotEqualityComparable&&) const;
34 };
35 
36 template <class It, class Sent = It>
37 concept HasCountIt = requires(It it, Sent sent) { std::ranges::count(it, sent, *it); };
38 static_assert(HasCountIt<int*>);
39 static_assert(!HasCountIt<NotEqualityComparable*>);
40 static_assert(!HasCountIt<InputIteratorNotDerivedFrom>);
41 static_assert(!HasCountIt<InputIteratorNotIndirectlyReadable>);
42 static_assert(!HasCountIt<InputIteratorNotInputOrOutputIterator>);
43 static_assert(!HasCountIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);
44 static_assert(!HasCountIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);
45 
46 static_assert(!HasCountIt<int*, int>);
47 static_assert(!HasCountIt<int, int*>);
48 
49 template <class Range, class ValT>
50 concept HasCountR = requires(Range r) { std::ranges::count(r, ValT{}); };
51 static_assert(HasCountR<std::array<int, 1>, int>);
52 static_assert(!HasCountR<int, int>);
53 static_assert(!HasCountR<std::array<NotEqualityComparable, 1>, NotEqualityComparable>);
54 static_assert(!HasCountR<InputRangeNotDerivedFrom, int>);
55 static_assert(!HasCountR<InputRangeNotIndirectlyReadable, int>);
56 static_assert(!HasCountR<InputRangeNotInputOrOutputIterator, int>);
57 static_assert(!HasCountR<InputRangeNotSentinelSemiregular, int>);
58 static_assert(!HasCountR<InputRangeNotSentinelEqualityComparableWith, int>);
59 
60 template <class It, class Sent = It>
61 constexpr void test_iterators() {
62   {
63     // simple test
64     {
65       int a[] = {1, 2, 3, 4};
66       std::same_as<std::ptrdiff_t> auto ret = std::ranges::count(It(a), Sent(It(a + 4)), 3);
67       assert(ret == 1);
68     }
69     {
70       int a[] = {1, 2, 3, 4};
71       auto range = std::ranges::subrange(It(a), Sent(It(a + 4)));
72       std::same_as<std::ptrdiff_t> auto ret = std::ranges::count(range, 3);
73       assert(ret == 1);
74     }
75   }
76 
77   {
78     // check that an empty range works
79     {
80       std::array<int, 0> a = {};
81       auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 1);
82       assert(ret == 0);
83     }
84     {
85       std::array<int, 0> a = {};
86       auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
87       auto ret = std::ranges::count(range, 1);
88       assert(ret == 0);
89     }
90   }
91 
92   {
93     // check that a range with a single element works
94     {
95       std::array a = {2};
96       auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 2);
97       assert(ret == 1);
98     }
99     {
100       std::array a = {2};
101       auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
102       auto ret = std::ranges::count(range, 2);
103       assert(ret == 1);
104     }
105   }
106 
107   {
108     // check that 0 is returned with no match
109     {
110       std::array a = {1, 1, 1};
111       auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 0);
112       assert(ret == 0);
113     }
114     {
115       std::array a = {1, 1, 1};
116       auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
117       auto ret = std::ranges::count(range, 0);
118       assert(ret == 0);
119     }
120   }
121 
122   {
123     // check that more than one element is counted
124     {
125       std::array a = {3, 3, 4, 3, 3};
126       auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 3);
127       assert(ret == 4);
128     }
129     {
130       std::array a = {3, 3, 4, 3, 3};
131       auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
132       auto ret = std::ranges::count(range, 3);
133       assert(ret == 4);
134     }
135   }
136 
137   {
138     // check that all elements are counted
139     {
140       std::array a = {5, 5, 5, 5};
141       auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 5);
142       assert(ret == 4);
143     }
144     {
145       std::array a = {5, 5, 5, 5};
146       auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
147       auto ret = std::ranges::count(range, 5);
148       assert(ret == 4);
149     }
150   }
151 }
152 
153 constexpr bool test() {
154   test_iterators<int*>();
155   test_iterators<const int*>();
156   test_iterators<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>();
157   test_iterators<bidirectional_iterator<int*>>();
158   test_iterators<forward_iterator<int*>>();
159   test_iterators<random_access_iterator<int*>>();
160   test_iterators<contiguous_iterator<int*>>();
161 
162   {
163     // check that projections are used properly and that they are called with the iterator directly
164     {
165       int a[] = {1, 2, 3, 4};
166       auto ret = std::ranges::count(a, a + 4, a + 3, [](int& i) { return &i; });
167       assert(ret == 1);
168     }
169     {
170       int a[] = {1, 2, 3, 4};
171       auto ret = std::ranges::count(a, a + 3, [](int& i) { return &i; });
172       assert(ret == 1);
173     }
174   }
175 
176   {
177     // check that std::invoke is used
178     struct S { int i; };
179     S a[] = { S{1}, S{3}, S{2} };
180     std::same_as<std::ptrdiff_t> auto ret = std::ranges::count(a, 4, &S::i);
181     assert(ret == 0);
182   }
183 
184   {
185     // count invocations of the projection
186     {
187       int a[] = {1, 2, 3, 4};
188       int projection_count = 0;
189       auto ret = std::ranges::count(a, a + 4, 2, [&](int i) { ++projection_count; return i; });
190       assert(ret == 1);
191       assert(projection_count == 4);
192     }
193     {
194       int a[] = {1, 2, 3, 4};
195       int projection_count = 0;
196       auto ret = std::ranges::count(a, 2, [&](int i) { ++projection_count; return i; });
197       assert(ret == 1);
198       assert(projection_count == 4);
199     }
200   }
201 
202   {
203     // check that an immobile type works
204     struct NonMovable {
205       NonMovable(const NonMovable&) = delete;
206       NonMovable(NonMovable&&) = delete;
207       constexpr NonMovable(int i_) : i(i_) {}
208       int i;
209 
210       bool operator==(const NonMovable&) const = default;
211     };
212     {
213       NonMovable a[] = {9, 8, 4, 3};
214       auto ret = std::ranges::count(a, a + 4, NonMovable(8));
215       assert(ret == 1);
216     }
217     {
218       NonMovable a[] = {9, 8, 4, 3};
219       auto ret = std::ranges::count(a, NonMovable(8));
220       assert(ret == 1);
221     }
222   }
223 
224   {
225     // check that difference_type is used
226     struct DiffTypeIterator {
227       using difference_type = signed char;
228       using value_type = int;
229 
230       int* it = nullptr;
231 
232       constexpr DiffTypeIterator() = default;
233       constexpr DiffTypeIterator(int* i) : it(i) {}
234 
235       constexpr int& operator*() const { return *it; }
236       constexpr DiffTypeIterator& operator++() { ++it; return *this; }
237       constexpr void operator++(int) { ++it; }
238 
239       bool operator==(const DiffTypeIterator&) const = default;
240     };
241 
242     {
243       int a[] = {5, 5, 4, 3, 2, 1};
244       std::same_as<signed char> decltype(auto) ret =
245           std::ranges::count(DiffTypeIterator(a), DiffTypeIterator(a + 6), 4);
246       assert(ret == 1);
247     }
248     {
249       int a[] = {5, 5, 4, 3, 2, 1};
250       auto range = std::ranges::subrange(DiffTypeIterator(a), DiffTypeIterator(a + 6));
251       std::same_as<signed char> decltype(auto) ret = std::ranges::count(range, 4);
252       assert(ret == 1);
253     }
254   }
255 
256   return true;
257 }
258 
259 int main(int, char**) {
260   test();
261   static_assert(test());
262 
263   return 0;
264 }
265