2727
2828_LIBCPP_BEGIN_NAMESPACE_STD
2929
30- template <class _AlgPolicy , class _Iter , class _Sent , class _Type , class _Proj , class _Comp >
31- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
32- _Iter __lower_bound (_Iter __first, _Sent __last, const _Type& __value, _Comp& __comp, _Proj& __proj) {
33- auto __len = _IterOps<_AlgPolicy>::distance (__first, __last);
34-
30+ template <class _AlgPolicy , class _Iter , class _Type , class _Proj , class _Comp >
31+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter __lower_bound_bisecting (
32+ _Iter __first,
33+ const _Type& __value,
34+ typename iterator_traits<_Iter>::difference_type __len,
35+ _Comp& __comp,
36+ _Proj& __proj) {
3537 while (__len != 0 ) {
3638 auto __l2 = std::__half_positive (__len);
3739 _Iter __m = __first;
@@ -46,13 +48,68 @@ _Iter __lower_bound(_Iter __first, _Sent __last, const _Type& __value, _Comp& __
4648 return __first;
4749}
4850
51+ // One-sided binary search, aka meta binary search, has been in the public domain for decades, and has the general
52+ // advantage of being Ω(1) rather than the classic algorithm's Ω(log(n)), with the downside of executing at most
53+ // 2*(log(n)-1) comparisons vs the classic algorithm's exact log(n). There are two scenarios in which it really shines:
54+ // the first one is when operating over non-random iterators, because the classic algorithm requires knowing the
55+ // container's size upfront, which adds Ω(n) iterator increments to the complexity. The second one is when you're
56+ // traversing the container in order, trying to fast-forward to the next value: in that case, the classic algorithm
57+ // would yield Ω(n*log(n)) comparisons and, for non-random iterators, Ω(n^2) iterator increments, whereas the one-sided
58+ // version will yield O(n) operations on both counts, with a Ω(log(n)) bound on the number of comparisons.
59+ template <class _AlgPolicy , class _Iter , class _Sent , class _Type , class _Proj , class _Comp >
60+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter
61+ __lower_bound_onesided (_Iter __first, _Sent __last, const _Type& __value, _Comp& __comp, _Proj& __proj) {
62+ // static_assert(std::is_base_of<std::forward_iterator_tag, typename _IterOps<_AlgPolicy>::template
63+ // __iterator_category<_Iter>>::value,
64+ // "lower_bound() is a multipass algorithm and requires forward iterator or better");
65+
66+ using _Distance = typename iterator_traits<_Iter>::difference_type;
67+ for (_Distance __step = 1 ; __first != __last; __step <<= 1 ) {
68+ auto __it = __first;
69+ auto __dist = __step - _IterOps<_AlgPolicy>::advance (__it, __step, __last);
70+ // once we reach the last range where needle can be we must start
71+ // looking inwards, bisecting that range
72+ if (__it == __last || !std::__invoke (__comp, std::__invoke (__proj, *__it), __value)) {
73+ return std::__lower_bound_bisecting<_AlgPolicy>(__first, __value, __dist, __comp, __proj);
74+ }
75+ // range not found, move forward!
76+ __first = std::move (__it);
77+ }
78+ return __first;
79+ }
80+
81+ template <class _AlgPolicy , class _InputIter , class _Sent , class _Type , class _Proj , class _Comp >
82+ _LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIter __lower_bound (
83+ _InputIter __first, _Sent __last, const _Type& __value, _Comp& __comp, _Proj& __proj, std::input_iterator_tag) {
84+ return std::__lower_bound_onesided<_AlgPolicy>(__first, __last, __value, __comp, __proj);
85+ }
86+
87+ template <class _AlgPolicy , class _RandIter , class _Sent , class _Type , class _Proj , class _Comp >
88+ _LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandIter __lower_bound (
89+ _RandIter __first,
90+ _Sent __last,
91+ const _Type& __value,
92+ _Comp& __comp,
93+ _Proj& __proj,
94+ std::random_access_iterator_tag) {
95+ const auto __dist = _IterOps<_AlgPolicy>::distance (__first, __last);
96+ return std::__lower_bound_bisecting<_AlgPolicy>(__first, __value, __dist, __comp, __proj);
97+ }
98+
99+ template <class _AlgPolicy , class _Iter , class _Sent , class _Type , class _Proj , class _Comp >
100+ _LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter
101+ __lower_bound (_Iter __first, _Sent __last, const _Type& __value, _Comp&& __comp, _Proj&& __proj) {
102+ return std::__lower_bound<_AlgPolicy>(
103+ __first, __last, __value, __comp, __proj, typename _IterOps<_AlgPolicy>::template __iterator_category<_Iter>());
104+ }
105+
49106template <class _ForwardIterator , class _Tp , class _Compare >
50107_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
51108_ForwardIterator lower_bound (_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value, _Compare __comp) {
52109 static_assert (__is_callable<_Compare, decltype (*__first), const _Tp&>::value,
53110 " The comparator has to be callable" );
54111 auto __proj = std::__identity ();
55- return std::__lower_bound<_ClassicAlgPolicy>(__first, __last, __value, __comp, __proj);
112+ return std::__lower_bound<_ClassicAlgPolicy>(__first, __last, __value, std::move ( __comp), std::move ( __proj) );
56113}
57114
58115template <class _ForwardIterator , class _Tp >
0 commit comments