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