diff --git a/libcxx/benchmarks/CMakeLists.txt b/libcxx/benchmarks/CMakeLists.txt --- a/libcxx/benchmarks/CMakeLists.txt +++ b/libcxx/benchmarks/CMakeLists.txt @@ -160,6 +160,7 @@ # Register Benchmark tests #============================================================================== set(BENCHMARK_TESTS + algorithms/lower_bound.bench.cpp algorithms/make_heap.bench.cpp algorithms/make_heap_then_sort_heap.bench.cpp algorithms/min_max_element.bench.cpp diff --git a/libcxx/benchmarks/algorithms/lower_bound.bench.cpp b/libcxx/benchmarks/algorithms/lower_bound.bench.cpp new file mode 100644 --- /dev/null +++ b/libcxx/benchmarks/algorithms/lower_bound.bench.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include + +#include "common.h" + +namespace { +template +struct LowerBound { + size_t Quantity; + + mutable std::mt19937_64 rng { std::random_device{}() }; + + void run(benchmark::State& state) const { + runOpOnCopies(state, Quantity, Order::Ascending, BatchSize::CountBatch, [&](auto& Copy) { + auto result = std::lower_bound(Copy.begin(), Copy.end(), Copy[rng() % Copy.size()]); + benchmark::DoNotOptimize(result); + }); + } + + std::string name() const { + return "BM_LowerBound" + ValueType::name() + "_" + std::to_string(Quantity); + } +}; +} // namespace + +int main(int argc, char** argv) { + benchmark::Initialize(&argc, argv); + if (benchmark::ReportUnrecognizedArguments(argc, argv)) + return 1; + makeCartesianProductBenchmark(Quantities); + benchmark::RunSpecifiedBenchmarks(); +} diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -11,10 +11,10 @@ Search,equal,Nikolas Klauser,`D123681 `,✅ Search,lexicographical_compare,Not assigned,n/a,Not started Search,partition_point,Christopher Di Bella,`D105794 `_,Under review -Search,lower_bound,Christopher Di Bella,`D105795 `_,Under review -Search,upper_bound,Christopher Di Bella,`D105795 `_,Under review +Search,lower_bound,Nikolas Klauser,`D121964 `_,✅ +Search,upper_bound,Nikolas Klauser,`D121964 `_,✅ Search,equal_range,Christopher Di Bella,n/a,Not started -Search,binary_search,Christopher Di Bella,n/a,Not started +Search,binary_search,Nikolas Klauser,`D121964 `_,✅ Search,min,Nikolas Klauser,`D119589 `_,✅ Search,max,Nikolas Klauser,`D122002 `_,✅ Search,minmax,Nikolas Klauser,`D120637 `_,✅ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -68,6 +68,7 @@ __algorithm/push_heap.h __algorithm/ranges_all_of.h __algorithm/ranges_any_of.h + __algorithm/ranges_binary_search.h __algorithm/ranges_copy.h __algorithm/ranges_copy_backward.h __algorithm/ranges_copy_if.h @@ -85,6 +86,7 @@ __algorithm/ranges_is_partitioned.h __algorithm/ranges_is_sorted.h __algorithm/ranges_is_sorted_until.h + __algorithm/ranges_lower_bound.h __algorithm/ranges_max.h __algorithm/ranges_max_element.h __algorithm/ranges_min.h @@ -96,6 +98,7 @@ __algorithm/ranges_reverse.h __algorithm/ranges_swap_ranges.h __algorithm/ranges_transform.h + __algorithm/ranges_upper_bound.h __algorithm/remove.h __algorithm/remove_copy.h __algorithm/remove_copy_if.h diff --git a/libcxx/include/__algorithm/binary_search.h b/libcxx/include/__algorithm/binary_search.h --- a/libcxx/include/__algorithm/binary_search.h +++ b/libcxx/include/__algorithm/binary_search.h @@ -21,23 +21,15 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 -bool -__binary_search(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_, _Compare __comp) -{ - __first = _VSTD::__lower_bound<_Compare>(__first, __last, __value_, __comp); - return __first != __last && !__comp(__value_, *__first); -} - template _LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 bool binary_search(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_, _Compare __comp) { - typedef typename __comp_ref_type<_Compare>::type _Comp_ref; - return _VSTD::__binary_search<_Comp_ref>(__first, __last, __value_, __comp); + using _Comp_ref = typename __comp_ref_type<_Compare>::type; + __first = std::lower_bound<_ForwardIterator, _Tp, _Comp_ref>(__first, __last, __value_, __comp); + return __first != __last && !__comp(__value_, *__first); } template @@ -46,8 +38,8 @@ bool binary_search(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_) { - return _VSTD::binary_search(__first, __last, __value_, - __less::value_type, _Tp>()); + return std::binary_search(__first, __last, __value_, + __less::value_type, _Tp>()); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/equal_range.h b/libcxx/include/__algorithm/equal_range.h --- a/libcxx/include/__algorithm/equal_range.h +++ b/libcxx/include/__algorithm/equal_range.h @@ -15,6 +15,7 @@ #include <__algorithm/lower_bound.h> #include <__algorithm/upper_bound.h> #include <__config> +#include <__functional/identity.h> #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -46,10 +47,11 @@ } else { + auto __proj = std::__identity(); _ForwardIterator __mp1 = __m; return pair<_ForwardIterator, _ForwardIterator> ( - _VSTD::__lower_bound<_Compare>(__first, __m, __value_, __comp), + _VSTD::__lower_bound_impl(__first, __m, __value_, __comp, __proj), _VSTD::__upper_bound<_Compare>(++__mp1, __last, __value_, __comp) ); } diff --git a/libcxx/include/__algorithm/inplace_merge.h b/libcxx/include/__algorithm/inplace_merge.h --- a/libcxx/include/__algorithm/inplace_merge.h +++ b/libcxx/include/__algorithm/inplace_merge.h @@ -166,7 +166,7 @@ __len11 = __len1 / 2; __m1 = __first; _VSTD::advance(__m1, __len11); - __m2 = _VSTD::__lower_bound<_Compare>(__middle, __last, *__m1, __comp); + __m2 = std::lower_bound(__middle, __last, *__m1, __comp); __len21 = _VSTD::distance(__middle, __m2); } difference_type __len12 = __len1 - __len11; // distance(__m1, __middle) diff --git a/libcxx/include/__algorithm/lower_bound.h b/libcxx/include/__algorithm/lower_bound.h --- a/libcxx/include/__algorithm/lower_bound.h +++ b/libcxx/include/__algorithm/lower_bound.h @@ -12,6 +12,8 @@ #include <__algorithm/comp.h> #include <__algorithm/half_positive.h> #include <__config> +#include <__functional/identity.h> +#include <__type_traits/is_callable.h> #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -20,45 +22,39 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template -_LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator -__lower_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_, _Compare __comp) -{ - typedef typename iterator_traits<_ForwardIterator>::difference_type difference_type; - difference_type __len = _VSTD::distance(__first, __last); - while (__len != 0) - { - difference_type __l2 = _VSTD::__half_positive(__len); - _ForwardIterator __m = __first; - _VSTD::advance(__m, __l2); - if (__comp(*__m, __value_)) - { - __first = ++__m; - __len -= __l2 + 1; - } - else - __len = __l2; +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 +_Iter __lower_bound_impl(_Iter __first, _Sent __last, const _Type& __value, _Comp& __comp, _Proj& __proj) { + auto __len = std::__ranges_distance(__first, __last); + + while (__len != 0) { + auto __l2 = std::__half_positive(__len); + _Iter __m = __first; + std::__ranges_advance(__m, __l2); + if (std::__invoke(__comp, std::__invoke(__proj, *__m), __value)) { + __first = ++__m; + __len -= __l2 + 1; + } else { + __len = __l2; } - return __first; + } + return __first; } template -_LIBCPP_NODISCARD_EXT inline -_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 -_ForwardIterator -lower_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_, _Compare __comp) -{ - return _VSTD::__lower_bound<_Compare&>(__first, __last, __value_, __comp); +_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 +_ForwardIterator lower_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_, _Compare __comp) { + static_assert(__is_callable<_Compare, decltype(*__first), const _Tp&>::value, + "The comparator has to be callable"); + auto __proj = std::__identity(); + return std::__lower_bound_impl(__first, __last, __value_, __comp, __proj); } template -_LIBCPP_NODISCARD_EXT inline -_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 -_ForwardIterator -lower_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_) -{ - return _VSTD::lower_bound(__first, __last, __value_, - __less::value_type, _Tp>()); +_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 +_ForwardIterator lower_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value_) { + return std::lower_bound(__first, __last, __value_, + __less::value_type, _Tp>()); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/ranges_binary_search.h b/libcxx/include/__algorithm/ranges_binary_search.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_binary_search.h @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// 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_BINARY_SEARCH_H +#define _LIBCPP___ALGORITHM_RANGES_BINARY_SEARCH_H + +#include <__algorithm/lower_bound.h> +#include <__config> +#include <__functional/identity.h> +#include <__functional/invoke.h> +#include <__functional/ranges_operations.h> +#include <__iterator/concepts.h> +#include <__iterator/projected.h> +#include <__ranges/access.h> +#include <__ranges/concepts.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 __binary_search { +struct __fn { + template _Sent, class _Type, class _Proj = identity, + indirect_strict_weak_order> _Comp = ranges::less> + _LIBCPP_HIDE_FROM_ABI constexpr + bool operator()(_Iter __first, _Sent __last, const _Type& __value, _Comp __comp = {}, _Proj __proj = {}) const { + auto __ret = std::__lower_bound_impl(__first, __last, __value, __comp, __proj); + return __ret != __last && !std::invoke(__comp, __value, std::invoke(__proj, *__first)); + } + + template , _Proj>> _Comp = ranges::less> + _LIBCPP_HIDE_FROM_ABI constexpr + bool operator()(_Range&& __r, const _Type& __value, _Comp __comp = {}, _Proj __proj = {}) const { + auto __first = ranges::begin(__r); + auto __last = ranges::end(__r); + auto __ret = std::__lower_bound_impl(__first, __last, __value, __comp, __proj); + return __ret != __last && !std::invoke(__comp, __value, std::invoke(__proj, *__first)); + } +}; +} // namespace __binary_search + +inline namespace __cpo { + inline constexpr auto binary_search = __binary_search::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +#endif // _LIBCPP___ALGORITHM_RANGES_BINARY_SEARCH_H diff --git a/libcxx/include/__algorithm/ranges_lower_bound.h b/libcxx/include/__algorithm/ranges_lower_bound.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_lower_bound.h @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// 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_LOWER_BOUND_H +#define _LIBCPP___ALGORITHM_RANGES_LOWER_BOUND_H + +#include <__algorithm/lower_bound.h> +#include <__config> +#include <__functional/identity.h> +#include <__functional/invoke.h> +#include <__functional/ranges_operations.h> +#include <__iterator/advance.h> +#include <__iterator/concepts.h> +#include <__iterator/iterator_traits.h> +#include <__iterator/projected.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/dangling.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 __lower_bound { +struct __fn { + template _Sent, class _Type, class _Proj = identity, + indirect_strict_weak_order> _Comp = ranges::less> + _LIBCPP_HIDE_FROM_ABI constexpr + _Iter operator()(_Iter __first, _Sent __last, const _Type& __value, _Comp __comp = {}, _Proj __proj = {}) const { + return std::__lower_bound_impl(__first, __last, __value, __comp, __proj); + } + + template , _Proj>> _Comp = ranges::less> + _LIBCPP_HIDE_FROM_ABI constexpr + borrowed_iterator_t<_Range> operator()(_Range&& __r, + const _Type& __value, + _Comp __comp = {}, + _Proj __proj = {}) const { + return std::__lower_bound_impl(ranges::begin(__r), ranges::end(__r), __value, __comp, __proj); + } +}; +} // namespace __lower_bound + +inline namespace __cpo { + inline constexpr auto lower_bound = __lower_bound::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +#endif // _LIBCPP___ALGORITHM_RANGES_LOWER_BOUND_H diff --git a/libcxx/include/__algorithm/ranges_upper_bound.h b/libcxx/include/__algorithm/ranges_upper_bound.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_upper_bound.h @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// 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_UPPER_BOUND_H +#define _LIBCPP___ALGORITHM_RANGES_UPPER_BOUND_H + +#include <__algorithm/lower_bound.h> +#include <__config> +#include <__functional/identity.h> +#include <__functional/invoke.h> +#include <__functional/ranges_operations.h> +#include <__iterator/concepts.h> +#include <__iterator/projected.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/dangling.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 __upper_bound { +struct __fn { + template _Sent, class _Type, class _Proj = identity, + indirect_strict_weak_order> _Comp = ranges::less> + _LIBCPP_HIDE_FROM_ABI constexpr + _Iter operator()(_Iter __first, _Sent __last, const _Type& __value, _Comp __comp = {}, _Proj __proj = {}) const { + auto __comp_lhs_rhs_swapped = [&](const auto& __lhs, const auto& __rhs) { + return !std::invoke(__comp, __rhs, __lhs); + }; + + return std::__lower_bound_impl(__first, __last, __value, __comp_lhs_rhs_swapped, __proj); + } + + template , _Proj>> _Comp = ranges::less> + _LIBCPP_HIDE_FROM_ABI constexpr + borrowed_iterator_t<_Range> operator()(_Range&& __r, + const _Type& __value, + _Comp __comp = {}, + _Proj __proj = {}) const { + auto __comp_lhs_rhs_swapped = [&](const auto& __lhs, const auto& __rhs) { + return !std::invoke(__comp, __rhs, __lhs); + }; + + return std::__lower_bound_impl(ranges::begin(__r), ranges::end(__r), __value, __comp_lhs_rhs_swapped, __proj); + } +}; +} // namespace __upper_bound + +inline namespace __cpo { + inline constexpr auto upper_bound = __upper_bound::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +#endif // _LIBCPP___ALGORITHM_RANGES_UPPER_BOUND_H diff --git a/libcxx/include/__iterator/advance.h b/libcxx/include/__iterator/advance.h --- a/libcxx/include/__iterator/advance.h +++ b/libcxx/include/__iterator/advance.h @@ -194,6 +194,15 @@ #endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) +template +_LIBCPP_CONSTEXPR_AFTER_CXX11 void __ranges_advance(_Iter& __first, _Sent& __last) { +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + return ranges::advance(__first, __last); +#else + return std::advance(__first, __last); +#endif +} + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___ITERATOR_ADVANCE_H diff --git a/libcxx/include/__iterator/distance.h b/libcxx/include/__iterator/distance.h --- a/libcxx/include/__iterator/distance.h +++ b/libcxx/include/__iterator/distance.h @@ -102,6 +102,16 @@ #endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) +template +_LIBCPP_CONSTEXPR_AFTER_CXX11 +typename iterator_traits<_Iter>::difference_type __ranges_distance(_Iter __first, _Sent __second) { +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + return ranges::distance(__first, __second); +#else + return std::distance(__first, __second); +#endif +} + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___ITERATOR_DISTANCE_H diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -345,6 +345,37 @@ indirect_strict_weak_order, Proj>> Comp = ranges::less> constexpr borrowed_iterator_t ranges::is_sorted_until(R&& r, Comp comp = {}, Proj proj = {}); // since C++20 + + template S, class T, class Proj = identity, + indirect_strict_weak_order> Comp = ranges::less> + constexpr I upper_bound(I first, S last, const T& value, Comp comp = {}, Proj proj = {}); // since C++20 + + template, Proj>> Comp = + ranges::less> + constexpr borrowed_iterator_t + upper_bound(R&& r, const T& value, Comp comp = {}, Proj proj = {}); // since C++20 + + template S, class T, class Proj = identity, + indirect_strict_weak_order> Comp = ranges::less> + constexpr I lower_bound(I first, S last, const T& value, Comp comp = {}, + Proj proj = {}); // since C++20 + template, Proj>> Comp = + ranges::less> + constexpr borrowed_iterator_t + lower_bound(R&& r, const T& value, Comp comp = {}, Proj proj = {}); // since C++20 + + template S, class T, class Proj = identity, + indirect_strict_weak_order> Comp = ranges::less> + constexpr bool binary_search(I first, S last, const T& value, Comp comp = {}, + Proj proj = {}); // since C++20 + template, Proj>> Comp = + ranges::less> + constexpr bool binary_search(R&& r, const T& value, Comp comp = {}, + Proj proj = {}); // since C++20 + } constexpr bool // constexpr in C++20 @@ -1063,6 +1094,7 @@ #include <__algorithm/push_heap.h> #include <__algorithm/ranges_all_of.h> #include <__algorithm/ranges_any_of.h> +#include <__algorithm/ranges_binary_search.h> #include <__algorithm/ranges_copy.h> #include <__algorithm/ranges_copy_backward.h> #include <__algorithm/ranges_copy_if.h> @@ -1080,6 +1112,7 @@ #include <__algorithm/ranges_is_partitioned.h> #include <__algorithm/ranges_is_sorted.h> #include <__algorithm/ranges_is_sorted_until.h> +#include <__algorithm/ranges_lower_bound.h> #include <__algorithm/ranges_max.h> #include <__algorithm/ranges_max_element.h> #include <__algorithm/ranges_min.h> @@ -1091,6 +1124,7 @@ #include <__algorithm/ranges_reverse.h> #include <__algorithm/ranges_swap_ranges.h> #include <__algorithm/ranges_transform.h> +#include <__algorithm/ranges_upper_bound.h> #include <__algorithm/remove.h> #include <__algorithm/remove_copy.h> #include <__algorithm/remove_copy_if.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -300,6 +300,7 @@ module push_heap { private header "__algorithm/push_heap.h" } module ranges_all_of { private header "__algorithm/ranges_all_of.h" } module ranges_any_of { private header "__algorithm/ranges_any_of.h" } + module ranges_binary_search { private header "__algorithm/ranges_binary_search.h" } module ranges_copy { private header "__algorithm/ranges_copy.h" } module ranges_copy_backward { private header "__algorithm/ranges_copy_backward.h" } module ranges_copy_if { private header "__algorithm/ranges_copy_if.h" } @@ -317,6 +318,7 @@ module ranges_is_partitioned { private header "__algorithm/ranges_is_partitioned.h" } module ranges_is_sorted { private header "__algorithm/ranges_is_sorted.h" } module ranges_is_sorted_until { private header "__algorithm/ranges_is_sorted_until.h" } + module ranges_lower_bound { private header "__algorithm/ranges_lower_bound.h" } module ranges_max { private header "__algorithm/ranges_max.h" } module ranges_max_element { private header "__algorithm/ranges_max_element.h" } module ranges_min { private header "__algorithm/ranges_min.h" } @@ -328,6 +330,7 @@ module ranges_reverse { private header "__algorithm/ranges_reverse.h" } module ranges_swap_ranges { private header "__algorithm/ranges_swap_ranges.h" } module ranges_transform { private header "__algorithm/ranges_transform.h" } + module ranges_upper_bound { private header "__algorithm/ranges_upper_bound.h" } module remove { private header "__algorithm/remove.h" } module remove_copy { private header "__algorithm/remove_copy.h" } module remove_copy_if { private header "__algorithm/remove_copy_if.h" } diff --git a/libcxx/test/libcxx/algorithms/callable.verify.cpp b/libcxx/test/libcxx/algorithms/callable.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/algorithms/callable.verify.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03 + +// + +// check that the classical algorithms with non-callable comparators fail + +#include + +int main() { + struct S { + int i; + + S(int i_) : i(i_) {} + + bool compare(const S&) const; + }; + + S a[] = {1, 2, 3, 4}; + std::lower_bound(a, a + 4, 0, &S::compare); // expected-error@*:* {{The comparator has to be callable}} + std::minmax({S{1}}, &S::compare); // expected-error@*:* {{The comparator has to be callable}} + std::minmax_element(a, a + 4, &S::compare); // expected-error@*:* {{The comparator has to be callable}} +} 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 --- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp +++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp @@ -96,8 +96,8 @@ (void)std::ranges::all_of(a, UnaryTrue(&copies)); assert(copies == 0); (void)std::ranges::any_of(first, last, UnaryTrue(&copies)); assert(copies == 0); (void)std::ranges::any_of(a, UnaryTrue(&copies)); assert(copies == 0); - //(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::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); @@ -142,8 +142,8 @@ //if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(a, mid, Less(&copies)); assert(copies == 0); } //(void)std::ranges::lexicographical_compare(first, last, first2, last2, Less(&copies)); assert(copies == 0); //(void)std::ranges::lexicographical_compare(a, b, Less(&copies)); assert(copies == 0); - //(void)std::ranges::lower_bound(first, last, value, Less(&copies)); assert(copies == 0); - //(void)std::ranges::lower_bound(a, value, Less(&copies)); assert(copies == 0); + (void)std::ranges::lower_bound(first, last, value, Less(&copies)); assert(copies == 0); + (void)std::ranges::lower_bound(a, value, Less(&copies)); assert(copies == 0); //(void)std::ranges::make_heap(first, last, Less(&copies)); assert(copies == 0); //(void)std::ranges::make_heap(a, Less(&copies)); assert(copies == 0); (void)std::ranges::max(value, value, Less(&copies)); assert(copies == 0); @@ -226,8 +226,8 @@ //(void)std::ranges::unique(a, Equal(&copies)); assert(copies == 0); //(void)std::ranges::unique_copy(first, last, first2, Equal(&copies)); assert(copies == 0); //(void)std::ranges::unique_copy(a, first2, Equal(&copies)); assert(copies == 0); - //(void)std::ranges::upper_bound(first, last, value, Less(&copies)); assert(copies == 0); - //(void)std::ranges::upper_bound(a, value, Less(&copies)); assert(copies == 0); + (void)std::ranges::upper_bound(first, last, value, Less(&copies)); assert(copies == 0); + (void)std::ranges::upper_bound(a, value, Less(&copies)); assert(copies == 0); return true; } diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -105,6 +105,7 @@ #include <__algorithm/push_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/push_heap.h'}} #include <__algorithm/ranges_all_of.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_all_of.h'}} #include <__algorithm/ranges_any_of.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_any_of.h'}} +#include <__algorithm/ranges_binary_search.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_binary_search.h'}} #include <__algorithm/ranges_copy.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy.h'}} #include <__algorithm/ranges_copy_backward.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_backward.h'}} #include <__algorithm/ranges_copy_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_if.h'}} @@ -122,6 +123,7 @@ #include <__algorithm/ranges_is_partitioned.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_is_partitioned.h'}} #include <__algorithm/ranges_is_sorted.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_is_sorted.h'}} #include <__algorithm/ranges_is_sorted_until.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_is_sorted_until.h'}} +#include <__algorithm/ranges_lower_bound.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_lower_bound.h'}} #include <__algorithm/ranges_max.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_max.h'}} #include <__algorithm/ranges_max_element.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_max_element.h'}} #include <__algorithm/ranges_min.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_min.h'}} @@ -133,6 +135,7 @@ #include <__algorithm/ranges_reverse.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_reverse.h'}} #include <__algorithm/ranges_swap_ranges.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_swap_ranges.h'}} #include <__algorithm/ranges_transform.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_transform.h'}} +#include <__algorithm/ranges_upper_bound.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_upper_bound.h'}} #include <__algorithm/remove.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/remove.h'}} #include <__algorithm/remove_copy.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/remove_copy.h'}} #include <__algorithm/remove_copy_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/remove_copy_if.h'}} diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/binary.search/ranges.binary_search.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/binary.search/ranges.binary_search.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/binary.search/ranges.binary_search.pass.cpp @@ -0,0 +1,176 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template S, class T, class Proj = identity, +// indirect_strict_weak_order> Comp = ranges::less> +// constexpr bool ranges::binary_search(I first, S last, const T& value, Comp comp = {}, +// Proj proj = {}); +// template, Proj>> Comp = +// ranges::less> +// constexpr bool ranges::binary_search(R&& r, const T& value, Comp comp = {}, +// Proj proj = {}); + +#include +#include +#include +#include +#include + +#include "almost_satisfies_types.h" +#include "test_iterators.h" + +struct NotLessThanComparable {}; + +template +concept HasLowerBoundIt = requires(It it, Sent sent) { std::ranges::binary_search(it, sent, 1); }; + +static_assert(HasLowerBoundIt); +static_assert(!HasLowerBoundIt, sentinel_wrapper>>); +static_assert(!HasLowerBoundIt); +static_assert(!HasLowerBoundIt); +static_assert(!HasLowerBoundIt); + +template +concept HasLowerBoundR = requires(Range range) { std::ranges::binary_search(range, 1); }; + +static_assert(HasLowerBoundR>); +static_assert(!HasLowerBoundR); +static_assert(!HasLowerBoundR); +static_assert(!HasLowerBoundR>); + +template +concept HasLowerBoundPred = requires(int* it, Pred pred) {std::ranges::binary_search(it, it, 1, pred); }; + +static_assert(HasLowerBoundPred); +static_assert(!HasLowerBoundPred); +static_assert(!HasLowerBoundPred); + +template +constexpr void test_iterators() { + { // simple test + { + int a[] = {1, 2, 3, 4, 5, 6}; + std::same_as auto ret = std::ranges::binary_search(It(a), Sent(It(a + 6)), 3); + assert(ret); + } + { + int a[] = {1, 2, 3, 4, 5, 6}; + auto range = std::ranges::subrange(It(a), Sent(It(a + 6))); + std::same_as auto ret = std::ranges::binary_search(range, 3); + assert(ret); + } + } + + { // check that the predicate is used + int a[] = {6, 5, 4, 3, 2, 1}; + assert(std::ranges::binary_search(It(a), Sent(It(a + 6)), 2, std::ranges::greater{})); + auto range = std::ranges::subrange(It(a), Sent(It(a + 6))); + assert(std::ranges::binary_search(range, 2, std::ranges::greater{})); + } + + { // check that the projection is used + int a[] = {1, 2, 3, 4, 5, 6}; + assert(std::ranges::binary_search(It(a), Sent(It(a + 6)), 0, {}, [](int i) { return i - 3; })); + auto range = std::ranges::subrange(It(a), Sent(It(a + 6))); + assert(std::ranges::binary_search(range, 0, {}, [](int i) { return i - 3; })); + } + + { // check that true is returned with multiple matches + int a[] = {1, 2, 2, 2, 3}; + assert(std::ranges::binary_search(It(a), Sent(It(a + 5)), 2)); + auto range = std::ranges::subrange(It(a), Sent(It(a + 5))); + assert(std::ranges::binary_search(range, 2)); + } + + { // check that false is returned if all elements compare less than + int a[] = {1, 2, 3, 4}; + assert(!std::ranges::binary_search(It(a), Sent(It(a + 4)), 5)); + auto range = std::ranges::subrange(It(a), Sent(It(a + 4))); + assert(!std::ranges::binary_search(range, 5)); + } + + { // check that false is returned if no element compares less than + int a[] = {1, 2, 3, 4}; + assert(!std::ranges::binary_search(It(a), Sent(It(a + 4)), 0)); + auto range = std::ranges::subrange(It(a), Sent(It(a + 4))); + assert(!std::ranges::binary_search(range, 0)); + } + + { // check that a single element works + int a[] = {1}; + assert(std::ranges::binary_search(It(a), Sent(It(a + 1)), 1)); + auto range = std::ranges::subrange(It(a), Sent(It(a + 1))); + assert(std::ranges::binary_search(range, 1)); + } + + { // check that an even number of elements works and that searching for the first element works + int a[] = {1, 2, 7, 8, 10, 11}; + assert(std::ranges::binary_search(It(a), Sent(It(a + 6)), 1)); + auto range = std::ranges::subrange(It(a), Sent(It(a + 6))); + assert(std::ranges::binary_search(range, 1)); + } + + { // check that an odd number of elements works and that searching for the last element works + int a[] = {1, 2, 7, 10, 11}; + assert(std::ranges::binary_search(It(a), Sent(It(a + 5)), 11)); + auto range = std::ranges::subrange(It(a), Sent(It(a + 5))); + assert(std::ranges::binary_search(range, 11)); + } + + { // check that it works when all but the searched for elements are equal + int a[] = {1, 2, 2, 2, 2}; + assert(std::ranges::binary_search(It(a), Sent(It(a + 5)), 1)); + auto range = std::ranges::subrange(It(a), Sent(It(a + 5))); + assert(std::ranges::binary_search(range, 1)); + } +} + +constexpr bool test() { + test_iterators(); + test_iterators>(); + test_iterators, sentinel_wrapper>>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators, sentinel_wrapper>>(); + + { // check that std::invoke is used + struct S { int check; int other; }; + S a[] = {{1, 6}, {2, 5}, {3, 4}, {4, 3}, {5, 2}, {6, 1}}; + assert(std::ranges::binary_search(a, a + 6, 4, {}, &S::check)); + assert(std::ranges::binary_search(a, 4, {}, &S::check)); + } + + { // check that an empty range works + std::array a; + assert(!std::ranges::binary_search(a.begin(), a.end(), 1)); + assert(!std::ranges::binary_search(a, 1)); + } + + { // check that a non-const operator() works + struct Func { + constexpr bool operator()(const int& i, const int& j) { return i < j; } + }; + int a[] = {1, 6, 9, 10, 23}; + assert(std::ranges::binary_search(a, 6, Func{})); + assert(std::ranges::binary_search(a, a + 5, 6, Func{})); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/lower.bound/ranges.lower_bound.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/lower.bound/ranges.lower_bound.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/lower.bound/ranges.lower_bound.pass.cpp @@ -0,0 +1,274 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template S, class T, class Proj = identity, +// indirect_strict_weak_order> Comp = ranges::less> +// constexpr I ranges::lower_bound(I first, S last, const T& value, Comp comp = {}, +// Proj proj = {}); +// template, Proj>> Comp = +// ranges::less> +// constexpr borrowed_iterator_t +// ranges::lower_bound(R&& r, const T& value, Comp comp = {}, Proj proj = {}); + +#include +#include +#include +#include +#include + +#include "almost_satisfies_types.h" +#include "test_iterators.h" + +struct NotLessThanComparable {}; + +template +concept HasLowerBoundIt = requires(It it, Sent sent) { std::ranges::lower_bound(it, sent, 1); }; + +static_assert(HasLowerBoundIt); +static_assert(!HasLowerBoundIt, sentinel_wrapper>>); +static_assert(!HasLowerBoundIt); +static_assert(!HasLowerBoundIt); +static_assert(!HasLowerBoundIt); + +template +concept HasLowerBoundR = requires(Range range) { std::ranges::lower_bound(range, 1); }; + +static_assert(HasLowerBoundR>); +static_assert(!HasLowerBoundR); +static_assert(!HasLowerBoundR); +static_assert(!HasLowerBoundR>); + +template +concept HasLowerBoundPred = requires(int* it, Pred pred) {std::ranges::lower_bound(it, it, 1, pred); }; + +static_assert(HasLowerBoundPred); +static_assert(!HasLowerBoundPred); +static_assert(!HasLowerBoundPred); + +template +constexpr void test_iterators() { + { // simple test + { + int a[] = {1, 2, 3, 4, 5, 6}; + std::same_as auto ret = std::ranges::lower_bound(It(a), It(a + 6), 3); + assert(base(ret) == a + 2); + } + { + int a[] = {1, 2, 3, 4, 5, 6}; + auto range = std::ranges::subrange(It(a), It(a + 6)); + std::same_as auto ret = std::ranges::lower_bound(range, 3); + assert(base(ret) == a + 2); + } + } + + { // check that the predicate is used + { + int a[] = {6, 5, 4, 3, 2, 1}; + auto ret = std::ranges::lower_bound(It(a), It(a + 6), 2, std::ranges::greater{}); + assert(base(ret) == a + 4); + } + { + int a[] = {6, 5, 4, 3, 2, 1}; + auto range = std::ranges::subrange(It(a), It(a + 6)); + auto ret = std::ranges::lower_bound(range, 2, std::ranges::greater{}); + assert(base(ret) == a + 4); + } + } + + { // check that the projection is used + { + int a[] = {1, 2, 3, 4, 5, 6}; + auto ret = std::ranges::lower_bound(It(a), It(a + 6), 0, {}, [](int i) { return i - 3; }); + assert(base(ret) == a + 2); + } + { + int a[] = {1, 2, 3, 4, 5, 6}; + auto range = std::ranges::subrange(It(a), It(a + 6)); + auto ret = std::ranges::lower_bound(range, 0, {}, [](int i) { return i - 3; }); + assert(base(ret) == a + 2); + } + } + + { // check that the last lower bound is returned + { + int a[] = {1, 2, 2, 2, 3}; + auto ret = std::ranges::lower_bound(It(a), It(a + 5), 2); + assert(base(ret) == a + 1); + } + { + int a[] = {1, 2, 2, 2, 3}; + auto range = std::ranges::subrange(It(a), It(a + 5)); + auto ret = std::ranges::lower_bound(range, 2); + assert(base(ret) == a + 1); + } + } + + { // check that end is returned if all elements compare less than + { + int a[] = {1, 2, 3, 4}; + auto ret = std::ranges::lower_bound(It(a), It(a + 4), 5); + assert(base(ret) == a + 4); + } + { + int a[] = {1, 2, 3, 4}; + auto range = std::ranges::subrange(It(a), It(a + 4)); + auto ret = std::ranges::lower_bound(range, 5); + assert(base(ret) == a + 4); + } + } + + { // check that the first element is returned if no element compares less than + { + int a[] = {1, 2, 3, 4}; + auto ret = std::ranges::lower_bound(It(a), It(a + 4), 0); + assert(base(ret) == a); + } + { + int a[] = {1, 2, 3, 4}; + auto range = std::ranges::subrange(It(a), It(a + 4)); + auto ret = std::ranges::lower_bound(range, 0); + assert(base(ret) == a); + } + } + + { // check that a single element works + { + int a[] = {1}; + auto ret = std::ranges::lower_bound(It(a), It(a + 1), 1); + assert(base(ret) == a); + } + { + int a[] = {1}; + auto range = std::ranges::subrange(It(a), It(a + 1)); + auto ret = std::ranges::lower_bound(range, 1); + assert(base(ret) == a); + } + } + + { // check that an even number of elements works + { + int a[] = {1, 3, 6, 6, 7, 8}; + auto ret = std::ranges::lower_bound(It(a), It(a + 6), 6); + assert(base(ret) == a + 2); + } + { + int a[] = {1, 3, 6, 6, 7, 8}; + auto range = std::ranges::subrange(It(a), It(a + 6)); + auto ret = std::ranges::lower_bound(range, 6); + assert(base(ret) == a + 2); + } + } + + { // check that an odd number of elements works + { + int a[] = {1, 3, 6, 6, 7}; + auto ret = std::ranges::lower_bound(It(a), It(a + 5), 6); + assert(base(ret) == a + 2); + } + { + int a[] = {1, 3, 6, 6, 7}; + auto range = std::ranges::subrange(It(a), It(a + 5)); + auto ret = std::ranges::lower_bound(range, 6); + assert(base(ret) == a + 2); + } + } + + { // check that it works when all but the searched for element are equal + { + int a[] = {1, 6, 6, 6, 6, 6}; + auto ret = std::ranges::lower_bound(It(a), It(a + 6), 1); + assert(base(ret) == a); + } + { + int a[] = {1, 6, 6, 6, 6, 6}; + auto range = std::ranges::subrange(It(a), It(a + 6)); + auto ret = std::ranges::lower_bound(range, 1); + assert(base(ret) == a); + } + } +} + +constexpr bool test() { + test_iterators(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + + { // check that std::invoke is used for the projections + { + struct S { int check; int other; }; + S a[] = {{1, 6}, {2, 5}, {3, 4}, {4, 3}, {5, 2}, {6, 1}}; + auto ret = std::ranges::lower_bound(a, a + 6, 4, {}, &S::check); + assert(ret == a + 3); + } + { + struct S { int check; int other; }; + S a[] = {{1, 6}, {2, 5}, {3, 4}, {4, 3}, {5, 2}, {6, 1}}; + auto ret = std::ranges::lower_bound(a, 4, {}, &S::check); + assert(ret == a + 3); + } + } + + { // check that std::invoke is used for the predicate + struct S { + int check; + int other; + + constexpr bool compare(const S& s) const { + return check < s.check; + } + }; + { + S a[] = {{1, 6}, {2, 5}, {3, 4}, {4, 3}, {5, 2}, {6, 1}}; + auto ret = std::ranges::lower_bound(a, a + 6, S{4, 0}, &S::compare); + assert(ret == a + 3); + } + { + S a[] = {{1, 6}, {2, 5}, {3, 4}, {4, 3}, {5, 2}, {6, 1}}; + auto ret = std::ranges::lower_bound(a, S{4, 0}, &S::compare); + assert(ret == a + 3); + } + } + + { // check that an empty range works + { + std::array a; + auto ret = std::ranges::lower_bound(a.begin(), a.end(), 1); + assert(ret == a.end()); + } + { + std::array a; + auto ret = std::ranges::lower_bound(a, 1); + assert(ret == a.end()); + } + } + + { // check that ranges::dangling is returned + [[maybe_unused]] std::same_as auto ret = std::ranges::lower_bound(std::array{1, 2}, 1); + } + + { // check that an iterator is returned with a borrowing range + int a[] = {1, 2, 3}; + std::same_as auto ret = std::ranges::lower_bound(std::views::all(a), 1); + assert(ret == a); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/upper.bound/ranges.upper_bound.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/upper.bound/ranges.upper_bound.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/upper.bound/ranges.upper_bound.pass.cpp @@ -0,0 +1,273 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template S, class T, class Proj = identity, +// indirect_strict_weak_order> Comp = ranges::less> +// constexpr I ranges::upper_bound(I first, S last, const T& value, Comp comp = {}, Proj proj = {}); +// template, Proj>> Comp = +// ranges::less> +// constexpr borrowed_iterator_t +// ranges::upper_bound(R&& r, const T& value, Comp comp = {}, Proj proj = {}); + +#include +#include +#include +#include +#include + +#include "almost_satisfies_types.h" +#include "test_iterators.h" + +struct NotLessThanComparable {}; + +template +concept HasUpperBoundIt = requires(It it, Sent sent) { std::ranges::upper_bound(it, sent, 1); }; + +static_assert(HasUpperBoundIt); +static_assert(!HasUpperBoundIt, sentinel_wrapper>>); +static_assert(!HasUpperBoundIt); +static_assert(!HasUpperBoundIt); +static_assert(!HasUpperBoundIt); + +template +concept HasUpperBoundR = requires(Range range) { std::ranges::upper_bound(range, 1); }; + +static_assert(HasUpperBoundR>); +static_assert(!HasUpperBoundR); +static_assert(!HasUpperBoundR); +static_assert(!HasUpperBoundR>); + +template +concept HasUpperBoundPred = requires(int* it, Pred pred) {std::ranges::upper_bound(it, it, 1, pred); }; + +static_assert(HasUpperBoundPred); +static_assert(!HasUpperBoundPred); +static_assert(!HasUpperBoundPred); + +template +constexpr void test_iterators() { + { // simple test + { + int a[] = {1, 2, 3, 4, 5, 6}; + std::same_as auto ret = std::ranges::upper_bound(It(a), It(a + 6), 3); + assert(base(ret) == a + 3); + } + { + int a[] = {1, 2, 3, 4, 5, 6}; + auto range = std::ranges::subrange(It(a), It(a + 6)); + std::same_as auto ret = std::ranges::upper_bound(range, 3); + assert(base(ret) == a + 3); + } + } + + { // check that the predicate is used + { + int a[] = {6, 5, 4, 3, 2, 1}; + auto ret = std::ranges::upper_bound(It(a), It(a + 6), 2, std::ranges::greater{}); + assert(base(ret) == a + 5); + } + { + int a[] = {6, 5, 4, 3, 2, 1}; + auto range = std::ranges::subrange(It(a), It(a + 6)); + auto ret = std::ranges::upper_bound(range, 2, std::ranges::greater{}); + assert(base(ret) == a + 5); + } + } + + { // check that the projection is used + { + int a[] = {1, 2, 3, 4, 5, 6}; + auto ret = std::ranges::upper_bound(It(a), It(a + 6), 0, {}, [](int i) { return i - 3; }); + assert(base(ret) == a + 3); + } + { + int a[] = {1, 2, 3, 4, 5, 6}; + auto range = std::ranges::subrange(It(a), It(a + 6)); + auto ret = std::ranges::upper_bound(range, 0, {}, [](int i) { return i - 3; }); + assert(base(ret) == a + 3); + } + } + + { // check that the last upper bound is returned + { + int a[] = {1, 2, 2, 2, 3}; + auto ret = std::ranges::upper_bound(It(a), It(a + 5), 2); + assert(base(ret) == a + 4); + } + { + int a[] = {1, 2, 2, 2, 3}; + auto range = std::ranges::subrange(It(a), It(a + 5)); + auto ret = std::ranges::upper_bound(range, 2); + assert(base(ret) == a + 4); + } + } + + { // check that end is returned if all elements compare less than + { + int a[] = {1, 2, 3, 4}; + auto ret = std::ranges::upper_bound(It(a), It(a + 4), 5); + assert(base(ret) == a + 4); + } + { + int a[] = {1, 2, 3, 4}; + auto range = std::ranges::subrange(It(a), It(a + 4)); + auto ret = std::ranges::upper_bound(range, 5); + assert(base(ret) == a + 4); + } + } + + { // check that the first element is returned if no element compares less than + { + int a[] = {1, 2, 3, 4}; + auto ret = std::ranges::upper_bound(It(a), It(a + 4), 0); + assert(base(ret) == a); + } + { + int a[] = {1, 2, 3, 4}; + auto range = std::ranges::subrange(It(a), It(a + 4)); + auto ret = std::ranges::upper_bound(range, 0); + assert(base(ret) == a); + } + } + + { // check that a single element works + { + int a[] = {1}; + auto ret = std::ranges::upper_bound(It(a), It(a + 1), 1); + assert(base(ret) == a + 1); + } + { + int a[] = {1}; + auto range = std::ranges::subrange(It(a), It(a + 1)); + auto ret = std::ranges::upper_bound(range, 1); + assert(base(ret) == a + 1); + } + } + + { // check that an even number of elements works + { + int a[] = {1, 3, 6, 6, 7, 8}; + auto ret = std::ranges::upper_bound(It(a), It(a + 6), 6); + assert(base(ret) == a + 4); + } + { + int a[] = {1, 3, 6, 6, 7, 8}; + auto range = std::ranges::subrange(It(a), It(a + 6)); + auto ret = std::ranges::upper_bound(range, 6); + assert(base(ret) == a + 4); + } + } + + { // check that an odd number of elements works + { + int a[] = {1, 3, 6, 6, 7}; + auto ret = std::ranges::upper_bound(It(a), It(a + 5), 6); + assert(base(ret) == a + 4); + } + { + int a[] = {1, 3, 6, 6, 7}; + auto range = std::ranges::subrange(It(a), It(a + 5)); + auto ret = std::ranges::upper_bound(range, 6); + assert(base(ret) == a + 4); + } + } + + { // check that it works when all but the searched for element are equal + { + int a[] = {1, 6, 6, 6, 6, 6}; + auto ret = std::ranges::upper_bound(It(a), It(a + 6), 1); + assert(base(ret) == a + 1); + } + { + int a[] = {1, 6, 6, 6, 6, 6}; + auto range = std::ranges::subrange(It(a), It(a + 6)); + auto ret = std::ranges::upper_bound(range, 1); + assert(base(ret) == a + 1); + } + } +} + +constexpr bool test() { + test_iterators(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + test_iterators>(); + + { // check that std::invoke is used for the projections + { + struct S { int check; int other; }; + S a[] = {{1, 6}, {2, 5}, {3, 4}, {4, 3}, {5, 2}, {6, 1}}; + auto ret = std::ranges::upper_bound(a, a + 6, 4, {}, &S::check); + assert(ret == a + 4); + } + { + struct S { int check; int other; }; + S a[] = {{1, 6}, {2, 5}, {3, 4}, {4, 3}, {5, 2}, {6, 1}}; + auto ret = std::ranges::upper_bound(a, 4, {}, &S::check); + assert(ret == a + 4); + } + } + + { // check that std::invoke is used for the predicate + struct S { + int check; + int other; + + constexpr bool compare(const S& s) const { + return check < s.check; + } + }; + { + S a[] = {{1, 6}, {2, 5}, {3, 4}, {4, 3}, {5, 2}, {6, 1}}; + auto ret = std::ranges::upper_bound(a, a + 6, S{4, 0}, &S::compare); + assert(ret == a + 4); + } + { + S a[] = {{1, 6}, {2, 5}, {3, 4}, {4, 3}, {5, 2}, {6, 1}}; + auto ret = std::ranges::upper_bound(a, S{4, 0}, &S::compare); + assert(ret == a + 4); + } + } + + { // check that an empty range works + { + std::array a; + auto ret = std::ranges::upper_bound(a.begin(), a.end(), 1); + assert(ret == a.end()); + } + { + std::array a; + auto ret = std::ranges::upper_bound(a, 1); + assert(ret == a.end()); + } + } + + { // check that ranges::dangling is returned + [[maybe_unused]] std::same_as auto ret = std::ranges::upper_bound(std::array{1, 2}, 1); + } + + { // check that an iterator is returned with a borrowing range + int a[] = {1, 2, 3}; + std::same_as auto ret = std::ranges::upper_bound(std::views::all(a), 1); + assert(ret == a + 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 --- 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 @@ -63,7 +63,7 @@ //static_assert(test(std::ranges::adjacent_find, a)); static_assert(test(std::ranges::all_of, a, odd)); static_assert(test(std::ranges::any_of, a, odd)); -//static_assert(test(std::ranges::binary_search, a, 42)); +static_assert(test(std::ranges::binary_search, a, 42)); //static_assert(test(std::ranges::clamp, 42, 42, 42)); static_assert(test(std::ranges::copy, a, a)); static_assert(test(std::ranges::copy_backward, a, a)); @@ -94,7 +94,7 @@ static_assert(test(std::ranges::is_sorted, a)); static_assert(test(std::ranges::is_sorted_until, a)); //static_assert(test(std::ranges::lexicographical_compare, a, a)); -//static_assert(test(std::ranges::lower_bound, a, 42)); +static_assert(test(std::ranges::lower_bound, a, 42)); //static_assert(test(std::ranges::make_heap, a)); static_assert(test(std::ranges::max, a)); static_assert(test(std::ranges::max_element, a)); @@ -146,7 +146,7 @@ static_assert(test(std::ranges::transform, a, a, triple)); //static_assert(test(std::ranges::unique, a)); //static_assert(test(std::ranges::unique_copy, a, a)); -//static_assert(test(std::ranges::upper_bound, a, 42)); +static_assert(test(std::ranges::upper_bound, a, 42)); // [memory.syn]