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