xref: /llvm-project/libcxx/include/__algorithm/set_intersection.h (revision 09e3a360581dc36d0820d3fb6da9bd7cfed87b5d)
1134723edSLouis Dionne //===----------------------------------------------------------------------===//
2134723edSLouis Dionne //
3134723edSLouis Dionne // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4134723edSLouis Dionne // See https://llvm.org/LICENSE.txt for license information.
5134723edSLouis Dionne // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6134723edSLouis Dionne //
7134723edSLouis Dionne //===----------------------------------------------------------------------===//
8134723edSLouis Dionne 
9134723edSLouis Dionne #ifndef _LIBCPP___ALGORITHM_SET_INTERSECTION_H
10134723edSLouis Dionne #define _LIBCPP___ALGORITHM_SET_INTERSECTION_H
11134723edSLouis Dionne 
12134723edSLouis Dionne #include <__algorithm/comp.h>
13134723edSLouis Dionne #include <__algorithm/comp_ref_type.h>
1496b674f2SHui Xie #include <__algorithm/iterator_operations.h>
15a0662176SIuri Chaer #include <__algorithm/lower_bound.h>
164d81a46fSArthur O'Dwyer #include <__config>
17a0662176SIuri Chaer #include <__functional/identity.h>
18134723edSLouis Dionne #include <__iterator/iterator_traits.h>
19101d1e9bSNikolas Klauser #include <__iterator/next.h>
20a0662176SIuri Chaer #include <__type_traits/is_same.h>
21a0662176SIuri Chaer #include <__utility/exchange.h>
22*09e3a360SLouis Dionne #include <__utility/forward.h>
2396b674f2SHui Xie #include <__utility/move.h>
24a0662176SIuri Chaer #include <__utility/swap.h>
25134723edSLouis Dionne 
26134723edSLouis Dionne #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
27134723edSLouis Dionne #  pragma GCC system_header
28134723edSLouis Dionne #endif
29134723edSLouis Dionne 
307b462251SLouis Dionne _LIBCPP_PUSH_MACROS
317b462251SLouis Dionne #include <__undef_macros>
327b462251SLouis Dionne 
33134723edSLouis Dionne _LIBCPP_BEGIN_NAMESPACE_STD
34134723edSLouis Dionne 
3596b674f2SHui Xie template <class _InIter1, class _InIter2, class _OutIter>
3696b674f2SHui Xie struct __set_intersection_result {
37a5c0638dSHui Xie   _InIter1 __in1_;
38a5c0638dSHui Xie   _InIter2 __in2_;
39a5c0638dSHui Xie   _OutIter __out_;
4096b674f2SHui Xie 
4196b674f2SHui Xie   // need a constructor as C++03 aggregate init is hard
425146b57bSNikolas Klauser   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
43e90e7e70SHui Xie   __set_intersection_result(_InIter1&& __in_iter1, _InIter2&& __in_iter2, _OutIter&& __out_iter)
44a5c0638dSHui Xie       : __in1_(std::move(__in_iter1)), __in2_(std::move(__in_iter2)), __out_(std::move(__out_iter)) {}
4596b674f2SHui Xie };
4696b674f2SHui Xie 
47a0662176SIuri Chaer // Helper for __set_intersection() with one-sided binary search: populate result and advance input iterators if they
48a0662176SIuri Chaer // are found to potentially contain the same value in two consecutive calls. This function is very intimately related to
49a0662176SIuri Chaer // the way it is used and doesn't attempt to abstract that, it's not appropriate for general usage outside of its
50a0662176SIuri Chaer // context.
51a0662176SIuri Chaer template <class _InForwardIter1, class _InForwardIter2, class _OutIter>
52a0662176SIuri Chaer _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __set_intersection_add_output_if_equal(
53a0662176SIuri Chaer     bool __may_be_equal,
54a0662176SIuri Chaer     _InForwardIter1& __first1,
55a0662176SIuri Chaer     _InForwardIter2& __first2,
56a0662176SIuri Chaer     _OutIter& __result,
57a0662176SIuri Chaer     bool& __prev_may_be_equal) {
58a0662176SIuri Chaer   if (__may_be_equal && __prev_may_be_equal) {
59a0662176SIuri Chaer     *__result = *__first1;
60a0662176SIuri Chaer     ++__result;
61a0662176SIuri Chaer     ++__first1;
62a0662176SIuri Chaer     ++__first2;
63a0662176SIuri Chaer     __prev_may_be_equal = false;
64a0662176SIuri Chaer   } else {
65a0662176SIuri Chaer     __prev_may_be_equal = __may_be_equal;
66a0662176SIuri Chaer   }
67a0662176SIuri Chaer }
68a0662176SIuri Chaer 
69a0662176SIuri Chaer // With forward iterators we can make multiple passes over the data, allowing the use of one-sided binary search to
70a0662176SIuri Chaer // reduce best-case complexity to log(N). Understanding how we can use binary search and still respect complexity
71a0662176SIuri Chaer // guarantees is _not_ straightforward: the guarantee is "at most 2*(N+M)-1 comparisons", and one-sided binary search
72a0662176SIuri Chaer // will necessarily overshoot depending on the position of the needle in the haystack -- for instance, if we're
73a0662176SIuri Chaer // 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
74a0662176SIuri Chaer // comparisons, when linear search would have yielded 3. However, because we won't need to perform the intervening
75a0662176SIuri Chaer // reciprocal comparisons (ie 1<3, 2<3, 4<3), that extra comparison doesn't run afoul of the guarantee. Additionally,
76a0662176SIuri Chaer // this type of scenario can only happen for match distances of up to 5 elements, because 2*log2(8) is 6, and we'll
77a0662176SIuri Chaer // 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
78a0662176SIuri Chaer // 1 comparison worse-off compared to the classic linear-searching algorithm if matching position 3 of a set with 4
79a0662176SIuri Chaer // elements, or position 5 if the set has 7 or 8 elements, but we'll never exceed the complexity guarantees from the
80a0662176SIuri Chaer // standard.
81a0662176SIuri Chaer template <class _AlgPolicy,
82a0662176SIuri Chaer           class _Compare,
83a0662176SIuri Chaer           class _InForwardIter1,
84a0662176SIuri Chaer           class _Sent1,
85a0662176SIuri Chaer           class _InForwardIter2,
86a0662176SIuri Chaer           class _Sent2,
87a0662176SIuri Chaer           class _OutIter>
8817e0686aSNikolas Klauser [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI
89a0662176SIuri Chaer _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersection_result<_InForwardIter1, _InForwardIter2, _OutIter>
9096b674f2SHui Xie __set_intersection(
91a0662176SIuri Chaer     _InForwardIter1 __first1,
92a0662176SIuri Chaer     _Sent1 __last1,
93a0662176SIuri Chaer     _InForwardIter2 __first2,
94a0662176SIuri Chaer     _Sent2 __last2,
95a0662176SIuri Chaer     _OutIter __result,
96a0662176SIuri Chaer     _Compare&& __comp,
97a0662176SIuri Chaer     std::forward_iterator_tag,
98a0662176SIuri Chaer     std::forward_iterator_tag) {
99a0662176SIuri Chaer   _LIBCPP_CONSTEXPR std::__identity __proj;
100a0662176SIuri Chaer   bool __prev_may_be_equal = false;
101a0662176SIuri Chaer 
102a0662176SIuri Chaer   while (__first2 != __last2) {
103a0662176SIuri Chaer     _InForwardIter1 __first1_next =
104a0662176SIuri Chaer         std::__lower_bound_onesided<_AlgPolicy>(__first1, __last1, *__first2, __comp, __proj);
105a0662176SIuri Chaer     std::swap(__first1_next, __first1);
106a0662176SIuri Chaer     // keeping in mind that a==b iff !(a<b) && !(b<a):
107a0662176SIuri Chaer     // if we can't advance __first1, that means !(*__first1 < *_first2), therefore __may_be_equal==true
108a0662176SIuri Chaer     std::__set_intersection_add_output_if_equal(
109a0662176SIuri Chaer         __first1 == __first1_next, __first1, __first2, __result, __prev_may_be_equal);
110a0662176SIuri Chaer     if (__first1 == __last1)
111a0662176SIuri Chaer       break;
112a0662176SIuri Chaer 
113a0662176SIuri Chaer     _InForwardIter2 __first2_next =
114a0662176SIuri Chaer         std::__lower_bound_onesided<_AlgPolicy>(__first2, __last2, *__first1, __comp, __proj);
115a0662176SIuri Chaer     std::swap(__first2_next, __first2);
116a0662176SIuri Chaer     std::__set_intersection_add_output_if_equal(
117a0662176SIuri Chaer         __first2 == __first2_next, __first1, __first2, __result, __prev_may_be_equal);
118a0662176SIuri Chaer   }
119a0662176SIuri Chaer   return __set_intersection_result<_InForwardIter1, _InForwardIter2, _OutIter>(
120a0662176SIuri Chaer       _IterOps<_AlgPolicy>::next(std::move(__first1), std::move(__last1)),
121a0662176SIuri Chaer       _IterOps<_AlgPolicy>::next(std::move(__first2), std::move(__last2)),
122a0662176SIuri Chaer       std::move(__result));
123a0662176SIuri Chaer }
124a0662176SIuri Chaer 
125a0662176SIuri Chaer // input iterators are not suitable for multipass algorithms, so we stick to the classic single-pass version
126a0662176SIuri Chaer template <class _AlgPolicy,
127a0662176SIuri Chaer           class _Compare,
128a0662176SIuri Chaer           class _InInputIter1,
129a0662176SIuri Chaer           class _Sent1,
130a0662176SIuri Chaer           class _InInputIter2,
131a0662176SIuri Chaer           class _Sent2,
132a0662176SIuri Chaer           class _OutIter>
13317e0686aSNikolas Klauser [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI
134a0662176SIuri Chaer _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersection_result<_InInputIter1, _InInputIter2, _OutIter>
135a0662176SIuri Chaer __set_intersection(
136a0662176SIuri Chaer     _InInputIter1 __first1,
137a0662176SIuri Chaer     _Sent1 __last1,
138a0662176SIuri Chaer     _InInputIter2 __first2,
139a0662176SIuri Chaer     _Sent2 __last2,
140a0662176SIuri Chaer     _OutIter __result,
141a0662176SIuri Chaer     _Compare&& __comp,
142a0662176SIuri Chaer     std::input_iterator_tag,
143a0662176SIuri Chaer     std::input_iterator_tag) {
14496b674f2SHui Xie   while (__first1 != __last1 && __first2 != __last2) {
145134723edSLouis Dionne     if (__comp(*__first1, *__first2))
146134723edSLouis Dionne       ++__first1;
14796b674f2SHui Xie     else {
14896b674f2SHui Xie       if (!__comp(*__first2, *__first1)) {
149134723edSLouis Dionne         *__result = *__first1;
150134723edSLouis Dionne         ++__result;
151134723edSLouis Dionne         ++__first1;
152134723edSLouis Dionne       }
153134723edSLouis Dionne       ++__first2;
154134723edSLouis Dionne     }
155134723edSLouis Dionne   }
15696b674f2SHui Xie 
157a0662176SIuri Chaer   return __set_intersection_result<_InInputIter1, _InInputIter2, _OutIter>(
158295b951eSKonstantin Varlamov       _IterOps<_AlgPolicy>::next(std::move(__first1), std::move(__last1)),
159295b951eSKonstantin Varlamov       _IterOps<_AlgPolicy>::next(std::move(__first2), std::move(__last2)),
16096b674f2SHui Xie       std::move(__result));
161134723edSLouis Dionne }
162134723edSLouis Dionne 
163a0662176SIuri Chaer template <class _AlgPolicy, class _Compare, class _InIter1, class _Sent1, class _InIter2, class _Sent2, class _OutIter>
16417e0686aSNikolas Klauser [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI
165a0662176SIuri Chaer _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersection_result<_InIter1, _InIter2, _OutIter>
166a0662176SIuri Chaer __set_intersection(
167a0662176SIuri Chaer     _InIter1 __first1, _Sent1 __last1, _InIter2 __first2, _Sent2 __last2, _OutIter __result, _Compare&& __comp) {
168a0662176SIuri Chaer   return std::__set_intersection<_AlgPolicy>(
169a0662176SIuri Chaer       std::move(__first1),
170a0662176SIuri Chaer       std::move(__last1),
171a0662176SIuri Chaer       std::move(__first2),
172a0662176SIuri Chaer       std::move(__last2),
173a0662176SIuri Chaer       std::move(__result),
174a0662176SIuri Chaer       std::forward<_Compare>(__comp),
175a0662176SIuri Chaer       typename std::_IterOps<_AlgPolicy>::template __iterator_category<_InIter1>(),
176a0662176SIuri Chaer       typename std::_IterOps<_AlgPolicy>::template __iterator_category<_InIter2>());
177a0662176SIuri Chaer }
178a0662176SIuri Chaer 
179134723edSLouis Dionne template <class _InputIterator1, class _InputIterator2, class _OutputIterator, class _Compare>
1805146b57bSNikolas Klauser inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator set_intersection(
18196b674f2SHui Xie     _InputIterator1 __first1,
18296b674f2SHui Xie     _InputIterator1 __last1,
18396b674f2SHui Xie     _InputIterator2 __first2,
18496b674f2SHui Xie     _InputIterator2 __last2,
18596b674f2SHui Xie     _OutputIterator __result,
18696b674f2SHui Xie     _Compare __comp) {
187ed2d3644SNikolas Klauser   return std::__set_intersection<_ClassicAlgPolicy, __comp_ref_type<_Compare> >(
18896b674f2SHui Xie              std::move(__first1),
18996b674f2SHui Xie              std::move(__last1),
19096b674f2SHui Xie              std::move(__first2),
19196b674f2SHui Xie              std::move(__last2),
19296b674f2SHui Xie              std::move(__result),
19396b674f2SHui Xie              __comp)
194a5c0638dSHui Xie       .__out_;
195134723edSLouis Dionne }
196134723edSLouis Dionne 
197134723edSLouis Dionne template <class _InputIterator1, class _InputIterator2, class _OutputIterator>
1985146b57bSNikolas Klauser inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator set_intersection(
19996b674f2SHui Xie     _InputIterator1 __first1,
20096b674f2SHui Xie     _InputIterator1 __last1,
20196b674f2SHui Xie     _InputIterator2 __first2,
20296b674f2SHui Xie     _InputIterator2 __last2,
20396b674f2SHui Xie     _OutputIterator __result) {
204295b951eSKonstantin Varlamov   return std::__set_intersection<_ClassicAlgPolicy>(
20596b674f2SHui Xie              std::move(__first1),
20696b674f2SHui Xie              std::move(__last1),
20796b674f2SHui Xie              std::move(__first2),
20896b674f2SHui Xie              std::move(__last2),
20996b674f2SHui Xie              std::move(__result),
21088632e48SNikolas Klauser              __less<>())
211a5c0638dSHui Xie       .__out_;
212134723edSLouis Dionne }
213134723edSLouis Dionne 
214134723edSLouis Dionne _LIBCPP_END_NAMESPACE_STD
215134723edSLouis Dionne 
2167b462251SLouis Dionne _LIBCPP_POP_MACROS
2177b462251SLouis Dionne 
218134723edSLouis Dionne #endif // _LIBCPP___ALGORITHM_SET_INTERSECTION_H
219