1 //===----------------------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef _LIBCPP___ALGORITHM_SET_INTERSECTION_H
10 #define _LIBCPP___ALGORITHM_SET_INTERSECTION_H
12 #include <__algorithm/comp.h>
13 #include <__algorithm/comp_ref_type.h>
14 #include <__algorithm/iterator_operations.h>
15 #include <__algorithm/lower_bound.h>
17 #include <__functional/identity.h>
18 #include <__iterator/iterator_traits.h>
19 #include <__iterator/next.h>
20 #include <__type_traits/is_same.h>
21 #include <__utility/exchange.h>
22 #include <__utility/forward.h>
23 #include <__utility/move.h>
24 #include <__utility/swap.h>
26 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
27 # pragma GCC system_header
31 #include <__undef_macros>
33 _LIBCPP_BEGIN_NAMESPACE_STD
35 template <class _InIter1
, class _InIter2
, class _OutIter
>
36 struct __set_intersection_result
{
41 // need a constructor as C++03 aggregate init is hard
42 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
43 __set_intersection_result(_InIter1
&& __in_iter1
, _InIter2
&& __in_iter2
, _OutIter
&& __out_iter
)
44 : __in1_(std::move(__in_iter1
)), __in2_(std::move(__in_iter2
)), __out_(std::move(__out_iter
)) {}
47 // Helper for __set_intersection() with one-sided binary search: populate result and advance input iterators if they
48 // are found to potentially contain the same value in two consecutive calls. This function is very intimately related to
49 // the way it is used and doesn't attempt to abstract that, it's not appropriate for general usage outside of its
51 template <class _InForwardIter1
, class _InForwardIter2
, class _OutIter
>
52 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
void __set_intersection_add_output_if_equal(
54 _InForwardIter1
& __first1
,
55 _InForwardIter2
& __first2
,
57 bool& __prev_may_be_equal
) {
58 if (__may_be_equal
&& __prev_may_be_equal
) {
59 *__result
= *__first1
;
63 __prev_may_be_equal
= false;
65 __prev_may_be_equal
= __may_be_equal
;
69 // With forward iterators we can make multiple passes over the data, allowing the use of one-sided binary search to
70 // reduce best-case complexity to log(N). Understanding how we can use binary search and still respect complexity
71 // guarantees is _not_ straightforward: the guarantee is "at most 2*(N+M)-1 comparisons", and one-sided binary search
72 // will necessarily overshoot depending on the position of the needle in the haystack -- for instance, if we're
73 // searching for 3 in (1, 2, 3, 4), we'll check if 3<1, then 3<2, then 3<4, and, finally, 3<3, for a total of 4
74 // comparisons, when linear search would have yielded 3. However, because we won't need to perform the intervening
75 // reciprocal comparisons (ie 1<3, 2<3, 4<3), that extra comparison doesn't run afoul of the guarantee. Additionally,
76 // this type of scenario can only happen for match distances of up to 5 elements, because 2*log2(8) is 6, and we'll
77 // still be worse-off at position 5 of an 8-element set. From then onwards these scenarios can't happen. TL;DR: we'll be
78 // 1 comparison worse-off compared to the classic linear-searching algorithm if matching position 3 of a set with 4
79 // elements, or position 5 if the set has 7 or 8 elements, but we'll never exceed the complexity guarantees from the
81 template <class _AlgPolicy
,
83 class _InForwardIter1
,
85 class _InForwardIter2
,
88 [[__nodiscard__
]] _LIBCPP_HIDE_FROM_ABI
89 _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersection_result
<_InForwardIter1
, _InForwardIter2
, _OutIter
>
91 _InForwardIter1 __first1
,
93 _InForwardIter2 __first2
,
97 std::forward_iterator_tag
,
98 std::forward_iterator_tag
) {
99 _LIBCPP_CONSTEXPR
std::__identity __proj
;
100 bool __prev_may_be_equal
= false;
102 while (__first2
!= __last2
) {
103 _InForwardIter1 __first1_next
=
104 std::__lower_bound_onesided
<_AlgPolicy
>(__first1
, __last1
, *__first2
, __comp
, __proj
);
105 std::swap(__first1_next
, __first1
);
106 // keeping in mind that a==b iff !(a<b) && !(b<a):
107 // if we can't advance __first1, that means !(*__first1 < *_first2), therefore __may_be_equal==true
108 std::__set_intersection_add_output_if_equal(
109 __first1
== __first1_next
, __first1
, __first2
, __result
, __prev_may_be_equal
);
110 if (__first1
== __last1
)
113 _InForwardIter2 __first2_next
=
114 std::__lower_bound_onesided
<_AlgPolicy
>(__first2
, __last2
, *__first1
, __comp
, __proj
);
115 std::swap(__first2_next
, __first2
);
116 std::__set_intersection_add_output_if_equal(
117 __first2
== __first2_next
, __first1
, __first2
, __result
, __prev_may_be_equal
);
119 return __set_intersection_result
<_InForwardIter1
, _InForwardIter2
, _OutIter
>(
120 _IterOps
<_AlgPolicy
>::next(std::move(__first1
), std::move(__last1
)),
121 _IterOps
<_AlgPolicy
>::next(std::move(__first2
), std::move(__last2
)),
122 std::move(__result
));
125 // input iterators are not suitable for multipass algorithms, so we stick to the classic single-pass version
126 template <class _AlgPolicy
,
133 [[__nodiscard__
]] _LIBCPP_HIDE_FROM_ABI
134 _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersection_result
<_InInputIter1
, _InInputIter2
, _OutIter
>
136 _InInputIter1 __first1
,
138 _InInputIter2 __first2
,
142 std::input_iterator_tag
,
143 std::input_iterator_tag
) {
144 while (__first1
!= __last1
&& __first2
!= __last2
) {
145 if (__comp(*__first1
, *__first2
))
148 if (!__comp(*__first2
, *__first1
)) {
149 *__result
= *__first1
;
157 return __set_intersection_result
<_InInputIter1
, _InInputIter2
, _OutIter
>(
158 _IterOps
<_AlgPolicy
>::next(std::move(__first1
), std::move(__last1
)),
159 _IterOps
<_AlgPolicy
>::next(std::move(__first2
), std::move(__last2
)),
160 std::move(__result
));
163 template <class _AlgPolicy
, class _Compare
, class _InIter1
, class _Sent1
, class _InIter2
, class _Sent2
, class _OutIter
>
164 [[__nodiscard__
]] _LIBCPP_HIDE_FROM_ABI
165 _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersection_result
<_InIter1
, _InIter2
, _OutIter
>
167 _InIter1 __first1
, _Sent1 __last1
, _InIter2 __first2
, _Sent2 __last2
, _OutIter __result
, _Compare
&& __comp
) {
168 return std::__set_intersection
<_AlgPolicy
>(
174 std::forward
<_Compare
>(__comp
),
175 typename
std::_IterOps
<_AlgPolicy
>::template __iterator_category
<_InIter1
>(),
176 typename
std::_IterOps
<_AlgPolicy
>::template __iterator_category
<_InIter2
>());
179 template <class _InputIterator1
, class _InputIterator2
, class _OutputIterator
, class _Compare
>
180 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
set_intersection(
181 _InputIterator1 __first1
,
182 _InputIterator1 __last1
,
183 _InputIterator2 __first2
,
184 _InputIterator2 __last2
,
185 _OutputIterator __result
,
187 return std::__set_intersection
<_ClassicAlgPolicy
, __comp_ref_type
<_Compare
> >(
197 template <class _InputIterator1
, class _InputIterator2
, class _OutputIterator
>
198 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
set_intersection(
199 _InputIterator1 __first1
,
200 _InputIterator1 __last1
,
201 _InputIterator2 __first2
,
202 _InputIterator2 __last2
,
203 _OutputIterator __result
) {
204 return std::__set_intersection
<_ClassicAlgPolicy
>(
214 _LIBCPP_END_NAMESPACE_STD
218 #endif // _LIBCPP___ALGORITHM_SET_INTERSECTION_H