1212#include < __algorithm/comp.h>
1313#include < __algorithm/comp_ref_type.h>
1414#include < __algorithm/iterator_operations.h>
15+ #include < __algorithm/lower_bound.h>
1516#include < __config>
17+ #include < __functional/identity.h>
1618#include < __iterator/iterator_traits.h>
1719#include < __iterator/next.h>
20+ #include < __type_traits/is_same.h>
21+ #include < __utility/exchange.h>
1822#include < __utility/move.h>
1923
2024#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -36,9 +40,122 @@ struct __set_intersection_result {
3640};
3741
3842template <class _AlgPolicy , class _Compare , class _InIter1 , class _Sent1 , class _InIter2 , class _Sent2 , class _OutIter >
39- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersection_result<_InIter1, _InIter2, _OutIter>
40- __set_intersection (
41- _InIter1 __first1, _Sent1 __last1, _InIter2 __first2, _Sent2 __last2, _OutIter __result, _Compare&& __comp) {
43+ struct _LIBCPP_NODISCARD_EXT __set_intersector {
44+ _InIter1& __first1_;
45+ const _Sent1& __last1_;
46+ _InIter2& __first2_;
47+ const _Sent2& __last2_;
48+ _OutIter& __result_;
49+ _Compare& __comp_;
50+ static constexpr auto __proj_ = std::__identity();
51+ bool __prev_advanced_ = true ;
52+
53+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersector (
54+ _InIter1& __first1, _Sent1& __last1, _InIter2& __first2, _Sent2& __last2, _OutIter& __result, _Compare& __comp)
55+ : __first1_(__first1),
56+ __last1_(__last1),
57+ __first2_(__first2),
58+ __last2_(__last2),
59+ __result_(__result),
60+ __comp_(__comp) {}
61+
62+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI
63+ _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersection_result<_InIter1, _InIter2, _OutIter>
64+ operator ()() && {
65+ while (__first2_ != __last2_) {
66+ __advance1_and_maybe_add_result ();
67+ if (__first1_ == __last1_)
68+ break ;
69+ __advance2_and_maybe_add_result ();
70+ }
71+ return __set_intersection_result<_InIter1, _InIter2, _OutIter>(
72+ _IterOps<_AlgPolicy>::next (std::move (__first1_), std::move (__last1_)),
73+ _IterOps<_AlgPolicy>::next (std::move (__first2_), std::move (__last2_)),
74+ std::move (__result_));
75+ }
76+
77+ private:
78+ // advance __iter to the first element in the range where !__comp_(__iter, __value)
79+ // add result if this is the second consecutive call without advancing
80+ // this method only works if you alternate calls between __advance1_and_maybe_add_result() and
81+ // __advance2_and_maybe_add_result()
82+ template <class _Iter , class _Sent , class _Value >
83+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
84+ __advance_and_maybe_add_result (_Iter& __iter, const _Sent& __sentinel, const _Value& __value) {
85+ // use one-sided lower bound for improved algorithmic complexity bounds
86+ const auto __tmp =
87+ std::exchange (__iter, std::__lower_bound_onesided<_AlgPolicy>(__iter, __sentinel, __value, __comp_, __proj_));
88+ __add_output_unless (__tmp != __iter);
89+ }
90+
91+ // advance __first1_ to the first element in the range where !__comp_(*__first1_, *__first2_)
92+ // add result if neither __first1_ nor __first2_ advanced in the last attempt (meaning they are equal)
93+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __advance1_and_maybe_add_result () {
94+ __advance_and_maybe_add_result (__first1_, __last1_, *__first2_);
95+ }
96+
97+ // advance __first2_ to the first element in the range where !__comp_(*__first2_, *__first1_)
98+ // add result if neither __first1_ nor __first2_ advanced in the last attempt (meaning they are equal)
99+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __advance2_and_maybe_add_result () {
100+ __advance_and_maybe_add_result (__first2_, __last2_, *__first1_);
101+ }
102+
103+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __add_output_unless (bool __advanced) {
104+ if (__advanced | __prev_advanced_) {
105+ __prev_advanced_ = __advanced;
106+ } else {
107+ *__result_ = *__first1_;
108+ ++__result_;
109+ ++__first1_;
110+ ++__first2_;
111+ __prev_advanced_ = true ;
112+ }
113+ }
114+ };
115+
116+ // with forward iterators we can use binary search to skip over entries
117+ template <class _AlgPolicy ,
118+ class _Compare ,
119+ class _InForwardIter1 ,
120+ class _Sent1 ,
121+ class _InForwardIter2 ,
122+ class _Sent2 ,
123+ class _OutIter >
124+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI
125+ _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersection_result<_InForwardIter1, _InForwardIter2, _OutIter>
126+ __set_intersection (
127+ _InForwardIter1 __first1,
128+ _Sent1 __last1,
129+ _InForwardIter2 __first2,
130+ _Sent2 __last2,
131+ _OutIter __result,
132+ _Compare&& __comp,
133+ std::forward_iterator_tag,
134+ std::forward_iterator_tag) {
135+ std::__set_intersector<_AlgPolicy, _Compare, _InForwardIter1, _Sent1, _InForwardIter2, _Sent2, _OutIter>
136+ __intersector (__first1, __last1, __first2, __last2, __result, __comp);
137+ return std::move (__intersector)();
138+ }
139+
140+ // input iterators are not suitable for multipass algorithms, so we stick to the classic single-pass version
141+ template <class _AlgPolicy ,
142+ class _Compare ,
143+ class _InInputIter1 ,
144+ class _Sent1 ,
145+ class _InInputIter2 ,
146+ class _Sent2 ,
147+ class _OutIter >
148+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI
149+ _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersection_result<_InInputIter1, _InInputIter2, _OutIter>
150+ __set_intersection (
151+ _InInputIter1 __first1,
152+ _Sent1 __last1,
153+ _InInputIter2 __first2,
154+ _Sent2 __last2,
155+ _OutIter __result,
156+ _Compare&& __comp,
157+ std::input_iterator_tag,
158+ std::input_iterator_tag) {
42159 while (__first1 != __last1 && __first2 != __last2) {
43160 if (__comp (*__first1, *__first2))
44161 ++__first1;
@@ -52,12 +169,41 @@ __set_intersection(
52169 }
53170 }
54171
55- return __set_intersection_result<_InIter1, _InIter2 , _OutIter>(
172+ return std:: __set_intersection_result<_InInputIter1, _InInputIter2 , _OutIter>(
56173 _IterOps<_AlgPolicy>::next (std::move (__first1), std::move (__last1)),
57174 _IterOps<_AlgPolicy>::next (std::move (__first2), std::move (__last2)),
58175 std::move (__result));
59176}
60177
178+ template <class _AlgPolicy , class _Iter >
179+ class __set_intersection_iter_category {
180+ template <class _It >
181+ using __cat = typename std::_IterOps<_AlgPolicy>::template __iterator_category<_It>;
182+ template <class _It >
183+ static auto test (__cat<_It>*) -> __cat<_It>;
184+ template <class >
185+ static std::input_iterator_tag test (...);
186+
187+ public:
188+ using __type = decltype (test<_Iter>(nullptr ));
189+ };
190+
191+ template <class _AlgPolicy , class _Compare , class _InIter1 , class _Sent1 , class _InIter2 , class _Sent2 , class _OutIter >
192+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI
193+ _LIBCPP_CONSTEXPR_SINCE_CXX20 __set_intersection_result<_InIter1, _InIter2, _OutIter>
194+ __set_intersection (
195+ _InIter1 __first1, _Sent1 __last1, _InIter2 __first2, _Sent2 __last2, _OutIter __result, _Compare&& __comp) {
196+ return std::__set_intersection<_AlgPolicy>(
197+ std::move (__first1),
198+ std::move (__last1),
199+ std::move (__first2),
200+ std::move (__last2),
201+ std::move (__result),
202+ std::forward<_Compare>(__comp),
203+ typename std::__set_intersection_iter_category<_AlgPolicy, _InIter1>::__type (),
204+ typename std::__set_intersection_iter_category<_AlgPolicy, _InIter2>::__type ());
205+ }
206+
61207template <class _InputIterator1 , class _InputIterator2 , class _OutputIterator , class _Compare >
62208inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator set_intersection (
63209 _InputIterator1 __first1,
0 commit comments