Skip to content

[libc++] Optimizing is_permutation #129565

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 28 additions & 28 deletions libcxx/include/__algorithm/is_permutation.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@
#define _LIBCPP___ALGORITHM_IS_PERMUTATION_H

#include <__algorithm/comp.h>
#include <__algorithm/count_if.h>
#include <__algorithm/find_if.h>
#include <__algorithm/iterator_operations.h>
#include <__algorithm/mismatch.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/reference_wrapper.h>
#include <__iterator/concepts.h>
#include <__iterator/distance.h>
#include <__iterator/iterator_traits.h>
Expand Down Expand Up @@ -79,31 +83,30 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation_impl(
_Proj1&& __proj1,
_Proj2&& __proj2) {
using _D1 = __iter_diff_t<_Iter1>;
using _Ref1 = typename iterator_traits<_Iter1>::reference;
using _Ref2 = typename iterator_traits<_Iter2>::reference;
__identity __ident;

for (auto __i = __first1; __i != __last1; ++__i) {
// Have we already counted the number of *__i in [f1, l1)?
auto __match = __first1;
for (; __match != __i; ++__match) {
if (std::__invoke(__pred, std::__invoke(__proj1, *__match), std::__invoke(__proj1, *__i)))
break;
}
auto __match = std::find_if(__first1, __i, [&](_Ref1 __x) -> bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably hold on to the result of *__i. Something like _Ref1 __va = *__i. This simplifies the code a bit and might be a bit faster depending on the kind of iterator.

return std::__invoke(__pred, std::__invoke(__proj1, __x), std::__invoke(__proj1, *__i));
});

if (__match == __i) {
// Count number of *__i in [f2, l2)
_D1 __c2 = 0;
for (auto __j = __first2; __j != __last2; ++__j) {
if (std::__invoke(__pred, std::__invoke(__proj1, *__i), std::__invoke(__proj2, *__j)))
++__c2;
}
auto __predicate2 = [&](_Ref2 __x) -> bool {
return std::__invoke(__pred, std::__invoke(__proj1, *__i), std::__invoke(__proj2, __x));
};
_D1 __c2 = std::__count_if<_AlgPolicy>(__first2, __last2, __predicate2, __ident);
if (__c2 == 0)
return false;

// Count number of *__i in [__i, l1) (we can start with 1)
_D1 __c1 = 1;
for (auto __j = _IterOps<_AlgPolicy>::next(__i); __j != __last1; ++__j) {
if (std::__invoke(__pred, std::__invoke(__proj1, *__i), std::__invoke(__proj1, *__j)))
++__c1;
}
// Count number of *__i in [__i, l1)
auto __predicate1 = [&](_Ref1 __x) -> bool {
return std::__invoke(__pred, std::__invoke(__proj1, *__i), std::__invoke(__proj1, __x));
};
_D1 __c1 = std::__count_if<_AlgPolicy>(__i, __last1, __predicate1, __ident);
if (__c1 != __c2)
return false;
}
Expand All @@ -116,11 +119,10 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation_impl(
template <class _AlgPolicy, class _ForwardIterator1, class _Sentinel1, class _ForwardIterator2, class _BinaryPredicate>
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation(
_ForwardIterator1 __first1, _Sentinel1 __last1, _ForwardIterator2 __first2, _BinaryPredicate&& __pred) {
// Shorten sequences as much as possible by lopping of any equal prefix.
for (; __first1 != __last1; ++__first1, (void)++__first2) {
if (!__pred(*__first1, *__first2))
break;
}
// Shorten sequences as much as possible by lopping off any equal prefix.
auto __result = std::mismatch(__first1, __last1, __first2, std::ref(__pred));
__first1 = __result.first;
__first2 = __result.second;

if (__first1 == __last1)
return true;
Expand Down Expand Up @@ -160,13 +162,11 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_permutation(
_Proj1&& __proj1,
_Proj2&& __proj2,
/*_ConstTimeDistance=*/false_type) {
// Shorten sequences as much as possible by lopping of any equal prefix.
while (__first1 != __last1 && __first2 != __last2) {
if (!std::__invoke(__pred, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
break;
++__first1;
++__first2;
}
// Shorten sequences as much as possible by lopping off any equal prefix.
auto __result = std::__mismatch(__first1, __last1, __first2, __last2, __pred, __proj1, __proj2);

__first1 = __result.first;
__first2 = __result.second;

if (__first1 == __last1)
return __first2 == __last2;
Expand Down
Loading