[libcxx-commits] [libcxx] 1306b10 - [libc++][ranges] Implement ranges::count{, _if}

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Thu Apr 7 06:18:32 PDT 2022


Author: Nikolas Klauser
Date: 2022-04-07T15:18:14+02:00
New Revision: 1306b1025c507ec8c406a975558007e3e94e5cbd

URL: https://github.com/llvm/llvm-project/commit/1306b1025c507ec8c406a975558007e3e94e5cbd
DIFF: https://github.com/llvm/llvm-project/commit/1306b1025c507ec8c406a975558007e3e94e5cbd.diff

LOG: [libc++][ranges] Implement ranges::count{, _if}

Reviewed By: var-const, Mordante, ldionne, #libc

Spies: tcanens, libcxx-commits, mgorny

Differential Revision: https://reviews.llvm.org/D121523

Added: 
    libcxx/include/__algorithm/ranges_count.h
    libcxx/include/__algorithm/ranges_count_if.h
    libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count.pass.cpp
    libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count_if.pass.cpp

Modified: 
    libcxx/docs/Status/RangesAlgorithms.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/algorithm
    libcxx/include/module.modulemap
    libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
    libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
    libcxx/test/libcxx/private_headers.verify.cpp
    libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
index 33401e8f41819..68cd1a0f261bd 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -21,8 +21,8 @@ Search,minmax,Not assigned,n/a,Not started
 Search,min_element,Nikolas Klauser,`D117025 <https://llvm.org/D117025>`_,✅
 Search,max_element,Nikolas Klauser,`D117523 <https://llvm.org/D117523>`_,✅
 Search,minmax_element,Not assigned,n/a,Not started
-Search,count,Not assigned,n/a,Not started
-Search,count_if,Not assigned,n/a,Not started
+Search,count,Nikolas Klauser, `D121523 <https://llvm.org/D121523>`_,✅
+Search,count_if,Nikolas Klauser, `D121523 <https://llvm.org/D121523>`_,✅
 Search,search,Not assigned,n/a,Not started
 Search,search_n,Not assigned,n/a,Not started
 Search,find_end,Not assigned,n/a,Not started

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 6d37f91be4cd9..be544cfd288e6 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -66,6 +66,8 @@ set(files
   __algorithm/pop_heap.h
   __algorithm/prev_permutation.h
   __algorithm/push_heap.h
+  __algorithm/ranges_count.h
+  __algorithm/ranges_count_if.h
   __algorithm/ranges_find.h
   __algorithm/ranges_find_if.h
   __algorithm/ranges_find_if_not.h

diff  --git a/libcxx/include/__algorithm/ranges_count.h b/libcxx/include/__algorithm/ranges_count.h
new file mode 100644
index 0000000000000..3cbcdc959d27c
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_count.h
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ALGORITHM_RANGES_COUNT_H
+#define _LIBCPP___ALGORITHM_RANGES_COUNT_H
+
+#include <__algorithm/ranges_count_if.h>
+#include <__config>
+#include <__functional/identity.h>
+#include <__functional/ranges_operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/projected.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace ranges {
+namespace __count {
+struct __fn {
+  template <input_iterator _Iter, sentinel_for<_Iter> _Sent, class _Type, class _Proj = identity>
+    requires indirect_binary_predicate<ranges::equal_to, projected<_Iter, _Proj>, const _Type*>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  iter_
diff erence_t<_Iter> operator()(_Iter __first, _Sent __last, const _Type& __value, _Proj __proj = {}) const {
+    auto __pred = [&](auto&& __e) { return __e == __value; };
+    return ranges::__count_if_impl(std::move(__first), std::move(__last), __pred, __proj);
+  }
+
+  template <input_range _Range, class _Type, class _Proj = identity>
+    requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Range>, _Proj>, const _Type*>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  range_
diff erence_t<_Range> operator()(_Range&& __r, const _Type& __value, _Proj __proj = {}) const {
+    auto __pred = [&](auto&& __e) { return __e == __value; };
+    return ranges::__count_if_impl(ranges::begin(__r), ranges::end(__r), __pred, __proj);
+  }
+};
+} // namespace __count
+
+inline namespace __cpo {
+  inline constexpr auto count = __count::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+#endif // _LIBCPP___ALGORITHM_RANGES_COUNT_H

diff  --git a/libcxx/include/__algorithm/ranges_count_if.h b/libcxx/include/__algorithm/ranges_count_if.h
new file mode 100644
index 0000000000000..9080631f629f0
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_count_if.h
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ALGORITHM_RANGES_COUNT_IF_H
+#define _LIBCPP___ALGORITHM_RANGES_COUNT_IF_H
+
+#include <__config>
+#include <__functional/identity.h>
+#include <__functional/invoke.h>
+#include <__functional/ranges_operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/projected.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace ranges {
+template <class _Iter, class _Sent, class _Proj, class _Pred>
+_LIBCPP_HIDE_FROM_ABI constexpr
+iter_
diff erence_t<_Iter> __count_if_impl(_Iter __first, _Sent __last,
+                                             _Pred& __pred, _Proj& __proj) {
+  iter_
diff erence_t<_Iter> __counter(0);
+  for (; __first != __last; ++__first) {
+    if (std::invoke(__pred, std::invoke(__proj, *__first)))
+      ++__counter;
+  }
+  return __counter;
+}
+
+namespace __count_if {
+struct __fn {
+  template <input_iterator _Iter, sentinel_for<_Iter> _Sent, class _Proj = identity,
+            indirect_unary_predicate<projected<_Iter, _Proj>> _Predicate>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  iter_
diff erence_t<_Iter> operator()(_Iter __first, _Sent __last, _Predicate __pred, _Proj __proj = {}) const {
+    return ranges::__count_if_impl(std::move(__first), std::move(__last), __pred, __proj);
+  }
+
+  template <input_range _Range, class _Proj = identity,
+            indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Predicate>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  range_
diff erence_t<_Range> operator()(_Range&& __r, _Predicate __pred, _Proj __proj = {}) const {
+    return ranges::__count_if_impl(ranges::begin(__r), ranges::end(__r), __pred, __proj);
+  }
+};
+} // namespace __count_if
+
+inline namespace __cpo {
+  inline constexpr auto count_if = __count_if::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+#endif // _LIBCPP___ALGORITHM_RANGES_COUNT_IF_H

diff  --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index d86cfe74449b4..401e91841bdc1 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -145,6 +145,26 @@ namespace ranges {
     constexpr ranges::binary_transform_result<borrowed_iterator_t<R1>, borrowed_iterator_t<R2>, O>
       transform(R1&& r1, R2&& r2, O result,
                         F binary_op, Proj1 proj1 = {}, Proj2 proj2 = {});           // since C++20
+
+  template<input_iterator I, sentinel_for<I> S, class T, class Proj = identity>
+    requires indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>
+    constexpr iter_
diff erence_t<I>
+      count(I first, S last, const T& value, Proj proj = {});                       // since C++20
+
+  template<input_range R, class T, class Proj = identity>
+    requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*>
+    constexpr range_
diff erence_t<R>
+      count(R&& r, const T& value, Proj proj = {});                                 // since C++20
+
+  template<input_iterator I, sentinel_for<I> S, class Proj = identity,
+           indirect_unary_predicate<projected<I, Proj>> Pred>
+    constexpr iter_
diff erence_t<I>
+      count_if(I first, S last, Pred pred, Proj proj = {});                         // since C++20
+
+  template<input_range R, class Proj = identity,
+           indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
+    constexpr range_
diff erence_t<R>
+      count_if(R&& r, Pred pred, Proj proj = {});                                   // since C++20
 }
 
     constexpr bool     // constexpr in C++20
@@ -862,6 +882,8 @@ template <class BidirectionalIterator, class Compare>
 #include <__algorithm/pop_heap.h>
 #include <__algorithm/prev_permutation.h>
 #include <__algorithm/push_heap.h>
+#include <__algorithm/ranges_count.h>
+#include <__algorithm/ranges_count_if.h>
 #include <__algorithm/ranges_find.h>
 #include <__algorithm/ranges_find_if.h>
 #include <__algorithm/ranges_find_if_not.h>

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index ef373874aff41..cf74a07e4be7c 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -294,6 +294,8 @@ module std [system] {
       module pop_heap                 { private header "__algorithm/pop_heap.h" }
       module prev_permutation         { private header "__algorithm/prev_permutation.h" }
       module push_heap                { private header "__algorithm/push_heap.h" }
+      module ranges_count             { private header "__algorithm/ranges_count.h" }
+      module ranges_count_if          { private header "__algorithm/ranges_count_if.h" }
       module ranges_find              { private header "__algorithm/ranges_find.h" }
       module ranges_find_if           { private header "__algorithm/ranges_find_if.h" }
       module ranges_find_if_not       { private header "__algorithm/ranges_find_if_not.h" }

diff  --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
index 2ce0a7793d0aa..35f7c36835a1b 100644
--- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
@@ -99,8 +99,8 @@ constexpr bool all_the_algorithms()
     //(void)std::ranges::binary_search(first, last, value, Less(&copies)); assert(copies == 0);
     //(void)std::ranges::binary_search(a, value, Less(&copies)); assert(copies == 0);
     //(void)std::ranges::clamp(value, value, value, Less(&copies)); assert(copies == 0);
-    //(void)std::ranges::count_if(first, last, UnaryTrue(&copies)); assert(copies == 0);
-    //(void)std::ranges::count_if(a, UnaryTrue(&copies)); assert(copies == 0);
+    (void)std::ranges::count_if(first, last, UnaryTrue(&copies)); assert(copies == 0);
+    (void)std::ranges::count_if(a, UnaryTrue(&copies)); assert(copies == 0);
     //(void)std::ranges::copy_if(first, last, first2, UnaryTrue(&copies)); assert(copies == 0);
     //(void)std::ranges::copy_if(a, first2, UnaryTrue(&copies)); assert(copies == 0);
 #if TEST_STD_VER > 20

diff  --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
index e0b27644bade4..e593216c0d2b7 100644
--- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
@@ -81,10 +81,10 @@ constexpr bool all_the_algorithms()
     //(void)std::ranges::binary_search(first, last, value, Less(), Proj(&copies)); assert(copies == 0);
     //(void)std::ranges::binary_search(a, value, Less(), Proj(&copies)); assert(copies == 0);
     //(void)std::ranges::clamp(T(), T(), T(), Less(), Proj(&copies)); assert(copies == 0);
-    //(void)std::ranges::count(first, last, value, Proj(&copies)); assert(copies == 0);
-    //(void)std::ranges::count(a, value, Proj(&copies)); assert(copies == 0);
-    //(void)std::ranges::count_if(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0);
-    //(void)std::ranges::count_if(a, UnaryTrue(), Proj(&copies)); assert(copies == 0);
+    (void)std::ranges::count(first, last, value, Proj(&copies)); assert(copies == 0);
+    (void)std::ranges::count(a, value, Proj(&copies)); assert(copies == 0);
+    (void)std::ranges::count_if(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0);
+    (void)std::ranges::count_if(a, UnaryTrue(), Proj(&copies)); assert(copies == 0);
     //(void)std::ranges::copy_if(first, last, first2, UnaryTrue(), Proj(&copies)); assert(copies == 0);
     //(void)std::ranges::copy_if(a, first2, UnaryTrue(), Proj(&copies)); assert(copies == 0);
 #if TEST_STD_VER > 20

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 96c8014b3e1b2..f7117dda10b23 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -103,6 +103,8 @@ END-SCRIPT
 #include <__algorithm/pop_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pop_heap.h'}}
 #include <__algorithm/prev_permutation.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/prev_permutation.h'}}
 #include <__algorithm/push_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/push_heap.h'}}
+#include <__algorithm/ranges_count.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_count.h'}}
+#include <__algorithm/ranges_count_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_count_if.h'}}
 #include <__algorithm/ranges_find.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_find.h'}}
 #include <__algorithm/ranges_find_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_find_if.h'}}
 #include <__algorithm/ranges_find_if_not.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_find_if_not.h'}}

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count.pass.cpp
new file mode 100644
index 0000000000000..ae03c289be718
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count.pass.cpp
@@ -0,0 +1,265 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template<input_iterator I, sentinel_for<I> S, class T, class Proj = identity>
+//   requires indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>
+//   constexpr iter_
diff erence_t<I>
+//     ranges::count(I first, S last, const T& value, Proj proj = {});
+// template<input_range R, class T, class Proj = identity>
+//   requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*>
+//   constexpr range_
diff erence_t<R>
+//     ranges::count(R&& r, const T& value, Proj proj = {});
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+
+struct NotEqualityComparable {
+  bool operator==(NotEqualityComparable&&) const;
+  bool operator==(NotEqualityComparable&) const;
+  bool operator==(const NotEqualityComparable&&) const;
+};
+
+template <class It, class Sent = It>
+concept HasCountIt = requires(It it, Sent sent) { std::ranges::count(it, sent, *it); };
+static_assert(HasCountIt<int*>);
+static_assert(!HasCountIt<NotEqualityComparable*>);
+static_assert(!HasCountIt<InputIteratorNotDerivedFrom>);
+static_assert(!HasCountIt<InputIteratorNotIndirectlyReadable>);
+static_assert(!HasCountIt<InputIteratorNotInputOrOutputIterator>);
+static_assert(!HasCountIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);
+static_assert(!HasCountIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);
+
+static_assert(!HasCountIt<int*, int>);
+static_assert(!HasCountIt<int, int*>);
+
+template <class Range, class ValT>
+concept HasCountR = requires(Range r) { std::ranges::count(r, ValT{}); };
+static_assert(HasCountR<std::array<int, 1>, int>);
+static_assert(!HasCountR<int, int>);
+static_assert(!HasCountR<std::array<NotEqualityComparable, 1>, NotEqualityComparable>);
+static_assert(!HasCountR<InputRangeNotDerivedFrom, int>);
+static_assert(!HasCountR<InputRangeNotIndirectlyReadable, int>);
+static_assert(!HasCountR<InputRangeNotInputOrOutputIterator, int>);
+static_assert(!HasCountR<InputRangeNotSentinelSemiregular, int>);
+static_assert(!HasCountR<InputRangeNotSentinelEqualityComparableWith, int>);
+
+template <class It, class Sent = It>
+constexpr void test_iterators() {
+  {
+    // simple test
+    {
+      int a[] = {1, 2, 3, 4};
+      std::same_as<std::ptr
diff _t> auto ret = std::ranges::count(It(a), Sent(It(a + 4)), 3);
+      assert(ret == 1);
+    }
+    {
+      int a[] = {1, 2, 3, 4};
+      auto range = std::ranges::subrange(It(a), Sent(It(a + 4)));
+      std::same_as<std::ptr
diff _t> auto ret = std::ranges::count(range, 3);
+      assert(ret == 1);
+    }
+  }
+
+  {
+    // check that an empty range works
+    {
+      std::array<int, 0> a = {};
+      auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 1);
+      assert(ret == 0);
+    }
+    {
+      std::array<int, 0> a = {};
+      auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
+      auto ret = std::ranges::count(range, 1);
+      assert(ret == 0);
+    }
+  }
+
+  {
+    // check that a range with a single element works
+    {
+      std::array a = {2};
+      auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 2);
+      assert(ret == 1);
+    }
+    {
+      std::array a = {2};
+      auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
+      auto ret = std::ranges::count(range, 2);
+      assert(ret == 1);
+    }
+  }
+
+  {
+    // check that 0 is returned with no match
+    {
+      std::array a = {1, 1, 1};
+      auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 0);
+      assert(ret == 0);
+    }
+    {
+      std::array a = {1, 1, 1};
+      auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
+      auto ret = std::ranges::count(range, 0);
+      assert(ret == 0);
+    }
+  }
+
+  {
+    // check that more than one element is counted
+    {
+      std::array a = {3, 3, 4, 3, 3};
+      auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 3);
+      assert(ret == 4);
+    }
+    {
+      std::array a = {3, 3, 4, 3, 3};
+      auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
+      auto ret = std::ranges::count(range, 3);
+      assert(ret == 4);
+    }
+  }
+
+  {
+    // check that all elements are counted
+    {
+      std::array a = {5, 5, 5, 5};
+      auto ret = std::ranges::count(It(a.data()), Sent(It(a.data() + a.size())), 5);
+      assert(ret == 4);
+    }
+    {
+      std::array a = {5, 5, 5, 5};
+      auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
+      auto ret = std::ranges::count(range, 5);
+      assert(ret == 4);
+    }
+  }
+}
+
+constexpr bool test() {
+  test_iterators<int*>();
+  test_iterators<const int*>();
+  test_iterators<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>();
+  test_iterators<bidirectional_iterator<int*>>();
+  test_iterators<forward_iterator<int*>>();
+  test_iterators<random_access_iterator<int*>>();
+  test_iterators<contiguous_iterator<int*>>();
+
+  {
+    // check that projections are used properly and that they are called with the iterator directly
+    {
+      int a[] = {1, 2, 3, 4};
+      auto ret = std::ranges::count(a, a + 4, a + 3, [](int& i) { return &i; });
+      assert(ret == 1);
+    }
+    {
+      int a[] = {1, 2, 3, 4};
+      auto ret = std::ranges::count(a, a + 3, [](int& i) { return &i; });
+      assert(ret == 1);
+    }
+  }
+
+  {
+    // check that std::invoke is used
+    struct S { int i; };
+    S a[] = { S{1}, S{3}, S{2} };
+    std::same_as<std::ptr
diff _t> auto ret = std::ranges::count(a, 4, &S::i);
+    assert(ret == 0);
+  }
+
+  {
+    // count invocations of the projection
+    {
+      int a[] = {1, 2, 3, 4};
+      int projection_count = 0;
+      auto ret = std::ranges::count(a, a + 4, 2, [&](int i) { ++projection_count; return i; });
+      assert(ret == 1);
+      assert(projection_count == 4);
+    }
+    {
+      int a[] = {1, 2, 3, 4};
+      int projection_count = 0;
+      auto ret = std::ranges::count(a, 2, [&](int i) { ++projection_count; return i; });
+      assert(ret == 1);
+      assert(projection_count == 4);
+    }
+  }
+
+  {
+    // check that an immobile type works
+    struct NonMovable {
+      NonMovable(const NonMovable&) = delete;
+      NonMovable(NonMovable&&) = delete;
+      constexpr NonMovable(int i_) : i(i_) {}
+      int i;
+
+      bool operator==(const NonMovable&) const = default;
+    };
+    {
+      NonMovable a[] = {9, 8, 4, 3};
+      auto ret = std::ranges::count(a, a + 4, NonMovable(8));
+      assert(ret == 1);
+    }
+    {
+      NonMovable a[] = {9, 8, 4, 3};
+      auto ret = std::ranges::count(a, NonMovable(8));
+      assert(ret == 1);
+    }
+  }
+
+  {
+    // check that 
diff erence_type is used
+    struct DiffTypeIterator {
+      using 
diff erence_type = signed char;
+      using value_type = int;
+
+      int* it = nullptr;
+
+      constexpr DiffTypeIterator() = default;
+      constexpr DiffTypeIterator(int* i) : it(i) {}
+
+      constexpr int& operator*() const { return *it; }
+      constexpr DiffTypeIterator& operator++() { ++it; return *this; }
+      constexpr void operator++(int) { ++it; }
+
+      bool operator==(const DiffTypeIterator&) const = default;
+    };
+
+    {
+      int a[] = {5, 5, 4, 3, 2, 1};
+      std::same_as<signed char> decltype(auto) ret =
+          std::ranges::count(DiffTypeIterator(a), DiffTypeIterator(a + 6), 4);
+      assert(ret == 1);
+    }
+    {
+      int a[] = {5, 5, 4, 3, 2, 1};
+      auto range = std::ranges::subrange(DiffTypeIterator(a), DiffTypeIterator(a + 6));
+      std::same_as<signed char> decltype(auto) ret = std::ranges::count(range, 4);
+      assert(ret == 1);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count_if.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count_if.pass.cpp
new file mode 100644
index 0000000000000..5e762458dae69
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/ranges.count_if.pass.cpp
@@ -0,0 +1,323 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template<input_iterator I, sentinel_for<I> S, class Proj = identity,
+//          indirect_unary_predicate<projected<I, Proj>> Pred>
+//   constexpr iter_
diff erence_t<I>
+//     ranges::count_if(I first, S last, Pred pred, Proj proj = {});
+// template<input_range R, class Proj = identity,
+//          indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
+//   constexpr range_
diff erence_t<R>
+//     ranges::count_if(R&& r, Pred pred, Proj proj = {});
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+
+struct Predicate {
+  bool operator()(int);
+};
+
+template <class It, class Sent = It>
+concept HasCountIfIt = requires(It it, Sent sent) { std::ranges::count_if(it, sent, Predicate{}); };
+static_assert(HasCountIfIt<int*>);
+static_assert(!HasCountIfIt<InputIteratorNotDerivedFrom>);
+static_assert(!HasCountIfIt<InputIteratorNotIndirectlyReadable>);
+static_assert(!HasCountIfIt<InputIteratorNotInputOrOutputIterator>);
+static_assert(!HasCountIfIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);
+static_assert(!HasCountIfIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);
+
+static_assert(!HasCountIfIt<int*, int>);
+static_assert(!HasCountIfIt<int, int*>);
+
+template <class Pred>
+concept HasCountIfPred = requires(int* it, Pred pred) {std::ranges::count_if(it, it, pred); };
+
+static_assert(!HasCountIfPred<IndirectUnaryPredicateNotCopyConstructible>);
+static_assert(!HasCountIfPred<IndirectUnaryPredicateNotPredicate>);
+
+template <class R>
+concept HasCountIfR = requires(R r) { std::ranges::count_if(r, Predicate{}); };
+static_assert(HasCountIfR<std::array<int, 0>>);
+static_assert(!HasCountIfR<int>);
+static_assert(!HasCountIfR<InputRangeNotDerivedFrom>);
+static_assert(!HasCountIfR<InputRangeNotIndirectlyReadable>);
+static_assert(!HasCountIfR<InputRangeNotInputOrOutputIterator>);
+static_assert(!HasCountIfR<InputRangeNotSentinelSemiregular>);
+static_assert(!HasCountIfR<InputRangeNotSentinelEqualityComparableWith>);
+
+template <class It, class Sent = It>
+constexpr void test_iterators() {
+  {
+    // simple test
+    {
+      int a[] = {1, 2, 3, 4};
+      std::same_as<std::ptr
diff _t> auto ret =
+        std::ranges::count_if(It(a), Sent(It(a + 4)), [](int x) { return x == 4; });
+      assert(ret == 1);
+    }
+    {
+      int a[] = {1, 2, 3, 4};
+      auto range = std::ranges::subrange(It(a), Sent(It(a + 4)));
+      std::same_as<std::ptr
diff _t> auto ret =
+        std::ranges::count_if(range, [](int x) { return x == 4; });
+      assert(ret == 1);
+    }
+  }
+
+  {
+    // check that an empty range works
+    {
+      std::array<int, 0> a = {};
+      auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int) { return true; });
+      assert(ret == 0);
+    }
+    {
+      std::array<int, 0> a = {};
+      auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
+      auto ret = std::ranges::count_if(range, [](int) { return true; });
+      assert(ret == 0);
+    }
+  }
+
+  {
+    // check that a range with a single element works
+    {
+      std::array a = {2};
+      auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int i) { return i == 2; });
+      assert(ret == 1);
+    }
+    {
+      std::array a = {2};
+      auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
+      auto ret = std::ranges::count_if(range, [](int i) { return i == 2; });
+      assert(ret == 1);
+    }
+  }
+
+  {
+    // check that 0 is returned with no match
+    {
+      int a[] = {1, 1, 1};
+      auto ret = std::ranges::count_if(It(a), Sent(It(a + 3)), [](int) { return false; });
+      assert(ret == 0);
+    }
+    {
+      int a[] = {1, 1, 1};
+      auto range = std::ranges::subrange(It(a), Sent(It(a + 3)));
+      auto ret = std::ranges::count_if(range, [](int){ return false; });
+      assert(ret == 0);
+    }
+  }
+
+  {
+    // check that more than one element is counted
+    {
+      std::array a = {3, 3, 4, 3, 3};
+      auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int i) { return i == 3; });
+      assert(ret == 4);
+    }
+    {
+      std::array a = {3, 3, 4, 3, 3};
+      auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
+      auto ret = std::ranges::count_if(range, [](int i) { return i == 3; });
+      assert(ret == 4);
+    }
+  }
+
+  {
+    // check that all elements are counted
+    {
+      std::array a = {5, 5, 5, 5};
+      auto ret = std::ranges::count_if(It(a.data()), Sent(It(a.data() + a.size())), [](int) { return true; });
+      assert(ret == 4);
+    }
+    {
+      std::array a = {5, 5, 5, 5};
+      auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data() + a.size())));
+      auto ret = std::ranges::count_if(range, [](int) { return true; });
+      assert(ret == 4);
+    }
+  }
+}
+
+constexpr bool test() {
+  test_iterators<int*>();
+  test_iterators<const int*>();
+  test_iterators<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>();
+  test_iterators<bidirectional_iterator<int*>>();
+  test_iterators<forward_iterator<int*>>();
+  test_iterators<random_access_iterator<int*>>();
+  test_iterators<contiguous_iterator<int*>>();
+
+  {
+    // check that projections are used properly and that they are called with the iterator directly
+    {
+      int a[] = {1, 2, 3, 4};
+      auto ret = std::ranges::count_if(a, a + 4, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; });
+      assert(ret == 1);
+    }
+    {
+      int a[] = {1, 2, 3, 4};
+      auto ret = std::ranges::count_if(a, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; });
+      assert(ret == 1);
+    }
+  }
+
+  {
+    // check that std::invoke is used
+    {
+      struct S {
+        int comp;
+        int other;
+      };
+      S a[] = { {0, 0}, {0, 2}, {0, 1} };
+      auto ret = std::ranges::count_if(a, [](int i){ return i == 0; }, &S::comp);
+      assert(ret == 3);
+    }
+    {
+      struct S {
+        int comp;
+        int other;
+      };
+      S a[] = { {0, 0}, {0, 2}, {0, 1} };
+      auto ret = std::ranges::count_if(a, a + 3, [](int i) { return i == 0; }, &S::comp);
+      assert(ret == 3);
+    }
+  }
+
+  {
+    // check projection and predicate invocation count
+    {
+      int a[] = {1, 2, 3, 4};
+      int predicate_count = 0;
+      int projection_count = 0;
+      auto ret = std::ranges::count_if(a, a + 4,
+                                       [&](int i) { ++predicate_count; return i == 2; },
+                                       [&](int i) { ++projection_count; return i; });
+      assert(ret == 1);
+      assert(predicate_count == 4);
+      assert(projection_count == 4);
+    }
+    {
+      int a[] = {1, 2, 3, 4};
+      int predicate_count = 0;
+      int projection_count = 0;
+      auto ret = std::ranges::count_if(a,
+                                       [&](int i) { ++predicate_count; return i == 2; },
+                                       [&](int i) { ++projection_count; return i; });
+      assert(ret == 1);
+      assert(predicate_count == 4);
+      assert(projection_count == 4);
+    }
+  }
+
+  {
+    // check that an immobile type works
+    struct NonMovable {
+      NonMovable(const NonMovable&) = delete;
+      NonMovable(NonMovable&&) = delete;
+      constexpr NonMovable(int i_) : i(i_) {}
+      int i;
+
+      bool operator==(const NonMovable&) const = default;
+    };
+    {
+      NonMovable a[] = {9, 8, 4, 3};
+      auto ret = std::ranges::count_if(a, a + 4, [](const NonMovable& i) { return i == NonMovable(8); });
+      assert(ret == 1);
+    }
+    {
+      NonMovable a[] = {9, 8, 4, 3};
+      auto ret = std::ranges::count_if(a, [](const NonMovable& i) { return i == NonMovable(8); });
+      assert(ret == 1);
+    }
+  }
+
+  {
+    // check that 
diff erence_type is used
+    struct DiffTypeIterator {
+      using 
diff erence_type = signed char;
+      using value_type = int;
+
+      int* it = nullptr;
+
+      constexpr DiffTypeIterator() = default;
+      constexpr DiffTypeIterator(int* i) : it(i) {}
+
+      constexpr int& operator*() const { return *it; }
+      constexpr DiffTypeIterator& operator++() { ++it; return *this; }
+      constexpr void operator++(int) { ++it; }
+
+      bool operator==(const DiffTypeIterator&) const = default;
+    };
+
+    {
+      int a[] = {5, 5, 4, 3, 2, 1};
+      std::same_as<signed char> auto ret =
+          std::ranges::count_if(DiffTypeIterator(a), DiffTypeIterator(a + 6), [](int& i) { return i == 4; });
+      assert(ret == 1);
+    }
+    {
+      int a[] = {5, 5, 4, 3, 2, 1};
+      auto range = std::ranges::subrange(DiffTypeIterator(a), DiffTypeIterator(a + 6));
+      std::same_as<signed char> auto ret = std::ranges::count_if(range, [](int& i) { return i == 4; });
+      assert(ret == 1);
+    }
+  }
+
+  {
+    // check that the predicate can take the argument by lvalue ref
+    {
+      int a[] = {9, 8, 4, 3};
+      auto ret = std::ranges::count_if(a, a + 4, [](int& i) { return i == 8; });
+      assert(ret == 1);
+    }
+    {
+      int a[] = {9, 8, 4, 3};
+      auto ret = std::ranges::count_if(a, [](int& i) { return i == 8; });
+      assert(ret == 1);
+    }
+  }
+
+  {
+    // check that the predicate isn't made const
+    struct MutablePredicate {
+      constexpr bool operator()(int i) & { return i == 8; }
+      constexpr bool operator()(int i) && { return i == 8; }
+    };
+    {
+      int a[] = {9, 8, 4, 3};
+      auto ret = std::ranges::count_if(a, a + 4, MutablePredicate{});
+      assert(ret == 1);
+    }
+    {
+      int a[] = {9, 8, 4, 3};
+      auto ret = std::ranges::count_if(a, MutablePredicate{});
+      assert(ret == 1);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
index 71b43b272c23c..09cce4c205c2f 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
@@ -69,8 +69,8 @@ auto odd = [](int x) { return x % 2 != 0; };
 //static_assert(test(std::ranges::copy_backward, a, a));
 //static_assert(test(std::ranges::copy_if, a, a, odd));
 //static_assert(test(std::ranges::copy_n, a, 10, a));
-//static_assert(test(std::ranges::count, a, 42));
-//static_assert(test(std::ranges::count_if, a, odd));
+static_assert(test(std::ranges::count, a, 42));
+static_assert(test(std::ranges::count_if, a, odd));
 //static_assert(test(std::ranges::ends_with, a, a));
 //static_assert(test(std::ranges::equal, a, a));
 //static_assert(test(std::ranges::equal_range, a, 42));


        


More information about the libcxx-commits mailing list