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