xref: /llvm-project/libcxx/include/__algorithm/is_permutation.h (revision b905bcc5090cde734e8b7bbceae13bd5a606cc14)
1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #ifndef _LIBCPP___ALGORITHM_IS_PERMUTATION_H
11 #define _LIBCPP___ALGORITHM_IS_PERMUTATION_H
12 
13 #include <__algorithm/comp.h>
14 #include <__algorithm/iterator_operations.h>
15 #include <__config>
16 #include <__functional/identity.h>
17 #include <__iterator/concepts.h>
18 #include <__iterator/distance.h>
19 #include <__iterator/iterator_traits.h>
20 #include <__type_traits/enable_if.h>
21 #include <__type_traits/invoke.h>
22 #include <__type_traits/is_callable.h>
23 #include <__type_traits/is_same.h>
24 #include <__utility/move.h>
25 
26 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
27 #  pragma GCC system_header
28 #endif
29 
30 _LIBCPP_PUSH_MACROS
31 #include <__undef_macros>
32 
33 _LIBCPP_BEGIN_NAMESPACE_STD
34 
35 template <class _Iter1, class _Sent1, class _Iter2, class _Sent2, class = void>
36 struct _ConstTimeDistance : false_type {};
37 
38 #if _LIBCPP_STD_VER >= 20
39 
40 template <class _Iter1, class _Sent1, class _Iter2, class _Sent2>
41 struct _ConstTimeDistance<_Iter1,
42                           _Sent1,
43                           _Iter2,
44                           _Sent2,
45                           __enable_if_t< sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2> >>
46     : true_type {};
47 
48 #else
49 
50 template <class _Iter1, class _Iter2>
51 struct _ConstTimeDistance<
52     _Iter1,
53     _Iter1,
54     _Iter2,
55     _Iter2,
56     __enable_if_t< is_same<typename iterator_traits<_Iter1>::iterator_category, random_access_iterator_tag>::value &&
57                    is_same<typename iterator_traits<_Iter2>::iterator_category, random_access_iterator_tag>::value > >
58     : true_type {};
59 
60 #endif // _LIBCPP_STD_VER >= 20
61 
62 // Internal functions
63 
64 // For each element in [f1, l1) see if there are the same number of equal elements in [f2, l2)
65 template <class _AlgPolicy,
66           class _Iter1,
67           class _Sent1,
68           class _Iter2,
69           class _Sent2,
70           class _Proj1,
71           class _Proj2,
72           class _Pred>
73 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation_impl(
74     _Iter1 __first1,
75     _Sent1 __last1,
76     _Iter2 __first2,
77     _Sent2 __last2,
78     _Pred&& __pred,
79     _Proj1&& __proj1,
80     _Proj2&& __proj2) {
81   using _D1 = __iter_diff_t<_Iter1>;
82 
83   for (auto __i = __first1; __i != __last1; ++__i) {
84     //  Have we already counted the number of *__i in [f1, l1)?
85     auto __match = __first1;
86     for (; __match != __i; ++__match) {
87       if (std::__invoke(__pred, std::__invoke(__proj1, *__match), std::__invoke(__proj1, *__i)))
88         break;
89     }
90 
91     if (__match == __i) {
92       // Count number of *__i in [f2, l2)
93       _D1 __c2 = 0;
94       for (auto __j = __first2; __j != __last2; ++__j) {
95         if (std::__invoke(__pred, std::__invoke(__proj1, *__i), std::__invoke(__proj2, *__j)))
96           ++__c2;
97       }
98       if (__c2 == 0)
99         return false;
100 
101       // Count number of *__i in [__i, l1) (we can start with 1)
102       _D1 __c1 = 1;
103       for (auto __j = _IterOps<_AlgPolicy>::next(__i); __j != __last1; ++__j) {
104         if (std::__invoke(__pred, std::__invoke(__proj1, *__i), std::__invoke(__proj1, *__j)))
105           ++__c1;
106       }
107       if (__c1 != __c2)
108         return false;
109     }
110   }
111 
112   return true;
113 }
114 
115 // 2+1 iterators, predicate. Not used by range algorithms.
116 template <class _AlgPolicy, class _ForwardIterator1, class _Sentinel1, class _ForwardIterator2, class _BinaryPredicate>
117 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation(
118     _ForwardIterator1 __first1, _Sentinel1 __last1, _ForwardIterator2 __first2, _BinaryPredicate&& __pred) {
119   // Shorten sequences as much as possible by lopping of any equal prefix.
120   for (; __first1 != __last1; ++__first1, (void)++__first2) {
121     if (!__pred(*__first1, *__first2))
122       break;
123   }
124 
125   if (__first1 == __last1)
126     return true;
127 
128   //  __first1 != __last1 && *__first1 != *__first2
129   using _D1 = __iter_diff_t<_ForwardIterator1>;
130   _D1 __l1  = _IterOps<_AlgPolicy>::distance(__first1, __last1);
131   if (__l1 == _D1(1))
132     return false;
133   auto __last2 = _IterOps<_AlgPolicy>::next(__first2, __l1);
134 
135   return std::__is_permutation_impl<_AlgPolicy>(
136       std::move(__first1),
137       std::move(__last1),
138       std::move(__first2),
139       std::move(__last2),
140       __pred,
141       __identity(),
142       __identity());
143 }
144 
145 // 2+2 iterators, predicate, non-constant time `distance`.
146 template <class _AlgPolicy,
147           class _Iter1,
148           class _Sent1,
149           class _Iter2,
150           class _Sent2,
151           class _Proj1,
152           class _Proj2,
153           class _Pred>
154 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation(
155     _Iter1 __first1,
156     _Sent1 __last1,
157     _Iter2 __first2,
158     _Sent2 __last2,
159     _Pred&& __pred,
160     _Proj1&& __proj1,
161     _Proj2&& __proj2,
162     /*_ConstTimeDistance=*/false_type) {
163   // Shorten sequences as much as possible by lopping of any equal prefix.
164   while (__first1 != __last1 && __first2 != __last2) {
165     if (!std::__invoke(__pred, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
166       break;
167     ++__first1;
168     ++__first2;
169   }
170 
171   if (__first1 == __last1)
172     return __first2 == __last2;
173   if (__first2 == __last2) // Second range is shorter
174     return false;
175 
176   using _D1 = __iter_diff_t<_Iter1>;
177   _D1 __l1  = _IterOps<_AlgPolicy>::distance(__first1, __last1);
178 
179   using _D2 = __iter_diff_t<_Iter2>;
180   _D2 __l2  = _IterOps<_AlgPolicy>::distance(__first2, __last2);
181   if (__l1 != __l2)
182     return false;
183 
184   return std::__is_permutation_impl<_AlgPolicy>(
185       std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), __pred, __proj1, __proj2);
186 }
187 
188 // 2+2 iterators, predicate, specialization for constant-time `distance` call.
189 template <class _AlgPolicy,
190           class _Iter1,
191           class _Sent1,
192           class _Iter2,
193           class _Sent2,
194           class _Proj1,
195           class _Proj2,
196           class _Pred>
197 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation(
198     _Iter1 __first1,
199     _Sent1 __last1,
200     _Iter2 __first2,
201     _Sent2 __last2,
202     _Pred&& __pred,
203     _Proj1&& __proj1,
204     _Proj2&& __proj2,
205     /*_ConstTimeDistance=*/true_type) {
206   if (std::distance(__first1, __last1) != std::distance(__first2, __last2))
207     return false;
208   return std::__is_permutation<_AlgPolicy>(
209       std::move(__first1),
210       std::move(__last1),
211       std::move(__first2),
212       std::move(__last2),
213       __pred,
214       __proj1,
215       __proj2,
216       /*_ConstTimeDistance=*/false_type());
217 }
218 
219 // 2+2 iterators, predicate
220 template <class _AlgPolicy,
221           class _Iter1,
222           class _Sent1,
223           class _Iter2,
224           class _Sent2,
225           class _Proj1,
226           class _Proj2,
227           class _Pred>
228 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation(
229     _Iter1 __first1,
230     _Sent1 __last1,
231     _Iter2 __first2,
232     _Sent2 __last2,
233     _Pred&& __pred,
234     _Proj1&& __proj1,
235     _Proj2&& __proj2) {
236   return std::__is_permutation<_AlgPolicy>(
237       std::move(__first1),
238       std::move(__last1),
239       std::move(__first2),
240       std::move(__last2),
241       __pred,
242       __proj1,
243       __proj2,
244       _ConstTimeDistance<_Iter1, _Sent1, _Iter2, _Sent2>());
245 }
246 
247 // Public interface
248 
249 // 2+1 iterators, predicate
250 template <class _ForwardIterator1, class _ForwardIterator2, class _BinaryPredicate>
251 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool is_permutation(
252     _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2, _BinaryPredicate __pred) {
253   static_assert(__is_callable<_BinaryPredicate&, decltype(*__first1), decltype(*__first2)>::value,
254                 "The comparator has to be callable");
255 
256   return std::__is_permutation<_ClassicAlgPolicy>(std::move(__first1), std::move(__last1), std::move(__first2), __pred);
257 }
258 
259 // 2+1 iterators
260 template <class _ForwardIterator1, class _ForwardIterator2>
261 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
262 is_permutation(_ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
263   return std::is_permutation(__first1, __last1, __first2, __equal_to());
264 }
265 
266 #if _LIBCPP_STD_VER >= 14
267 
268 // 2+2 iterators
269 template <class _ForwardIterator1, class _ForwardIterator2>
270 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool is_permutation(
271     _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2, _ForwardIterator2 __last2) {
272   return std::__is_permutation<_ClassicAlgPolicy>(
273       std::move(__first1),
274       std::move(__last1),
275       std::move(__first2),
276       std::move(__last2),
277       __equal_to(),
278       __identity(),
279       __identity());
280 }
281 
282 // 2+2 iterators, predicate
283 template <class _ForwardIterator1, class _ForwardIterator2, class _BinaryPredicate>
284 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool is_permutation(
285     _ForwardIterator1 __first1,
286     _ForwardIterator1 __last1,
287     _ForwardIterator2 __first2,
288     _ForwardIterator2 __last2,
289     _BinaryPredicate __pred) {
290   static_assert(__is_callable<_BinaryPredicate&, decltype(*__first1), decltype(*__first2)>::value,
291                 "The comparator has to be callable");
292 
293   return std::__is_permutation<_ClassicAlgPolicy>(
294       std::move(__first1),
295       std::move(__last1),
296       std::move(__first2),
297       std::move(__last2),
298       __pred,
299       __identity(),
300       __identity());
301 }
302 
303 #endif // _LIBCPP_STD_VER >= 14
304 
305 _LIBCPP_END_NAMESPACE_STD
306 
307 _LIBCPP_POP_MACROS
308 
309 #endif // _LIBCPP___ALGORITHM_IS_PERMUTATION_H
310