Optimize `is_permutation` for reversed sequences (#2043)

Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
Co-authored-by: Miya Natsuhara <46756417+mnatsuhara@users.noreply.github.com>
This commit is contained in:
Adam Bucior 2021-11-13 04:21:36 +01:00 коммит произвёл GitHub
Родитель 08f12aecf6
Коммит 254fca2427
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 263 добавлений и 5 удалений

Просмотреть файл

@ -985,8 +985,6 @@ namespace ranges {
--_Final1;
--_Final2;
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Final1), _STD invoke(_Proj2, *_Final2))) { // mismatch
++_Final1;
++_Final2;
break;
}
@ -996,6 +994,32 @@ namespace ranges {
}
// If we get here, _Count > 1, initial elements do not match, and final elements do not match.
// We've trimmed matching prefixes and matching suffixes.
// Now we need to compare each range's prefix to the other range's suffix.
const auto _ProjectedPred = [&]<class _Ty1, class _Ty2>(_Ty1&& _Left, _Ty2&& _Right) {
return _STD invoke(_Pred, _STD invoke(_Proj1, _STD forward<_Ty1>(_Left)),
_STD invoke(_Proj2, _STD forward<_Ty2>(_Right)));
};
const _TrimResult _Res = _Trim_completely(_First1, _Final1, _First2, _Final2, _ProjectedPred);
if (_Res != _TrimResult::_HaveWorkAfterTrimming) {
return _Res == _TrimResult::_ReturnTrue;
}
++_Final1;
++_Final2;
// If we get here, initial elements do not match, final elements do not match, and ranges have length
// at least 2 and at most _Count.
// We've trimmed matching prefixes, matching suffixes,
// and each range's prefix matching the other range's suffix. That is, given:
// Range 1: [A, ..., B]
// Range 2: [X, ..., Y]
// we know that A != X, A != Y, B != X, and B != Y.
// (A == B and X == Y are possible but irrelevant.)
return _Match_counts(_STD move(_First1), _STD move(_Final1), _STD move(_First2), _STD move(_Final2),
_Pred, _Proj1, _Proj2);
} else {
@ -1053,13 +1077,37 @@ namespace ranges {
--_Final2; // since ranges have equal lengths, _Final2 cannot equal _First2
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Final1), _STD invoke(_Proj2, *_Final2))) { // mismatch
++_Final1;
++_Final2;
break;
}
}
// If we get here, initial elements do not match, final elements do not match, and ranges have length
// at least 2.
// We've trimmed matching prefixes and matching suffixes.
// Now we need to compare each range's prefix to the other range's suffix.
const auto _ProjectedPred = [&]<class _Ty1, class _Ty2>(_Ty1&& _Left, _Ty2&& _Right) {
return _STD invoke(_Pred, _STD invoke(_Proj1, _STD forward<_Ty1>(_Left)),
_STD invoke(_Proj2, _STD forward<_Ty2>(_Right)));
};
const _TrimResult _Res = _Trim_completely(_First1, _Final1, _First2, _Final2, _ProjectedPred);
if (_Res != _TrimResult::_HaveWorkAfterTrimming) {
return _Res == _TrimResult::_ReturnTrue;
}
++_Final1;
++_Final2;
// If we get here, initial elements do not match, final elements do not match, and ranges have length
// at least 2.
// We've trimmed matching prefixes, matching suffixes,
// and each range's prefix matching the other range's suffix. That is, given:
// Range 1: [A, ..., B]
// Range 2: [X, ..., Y]
// we know that A != X, A != Y, B != X, and B != Y.
// (A == B and X == Y are possible but irrelevant.)
}
return _Match_counts(

Просмотреть файл

@ -5348,9 +5348,106 @@ _NODISCARD constexpr _Iter_diff_t<_InIt> _Count_pr(_InIt _First, const _InIt _La
return _Count;
}
enum class _TrimResult : unsigned char { _KeepTrimming, _HaveWorkAfterTrimming, _ReturnFalse, _ReturnTrue };
template <class _BidIt1, class _BidIt2, class _Pr>
_NODISCARD _CONSTEXPR20 _TrimResult _Trim_equal(
_BidIt1& _First1, _BidIt1& _Back1, _BidIt2& _First2, _BidIt2& _Back2, _Pr _Pred) {
// advances the iterators, trimming matching prefixes then matching suffixes
// from [_First1, _Back1] and [_First2, _Back2]
_STL_INTERNAL_CHECK(_First1 != _Back1);
_STL_INTERNAL_CHECK(_STD distance(_First1, _Back1) == _STD distance(_First2, _Back2));
if (_Pred(*_First1, *_First2)) {
do {
++_First1;
++_First2;
if (_First1 == _Back1) {
// only one element is left
return _Pred(*_First1, *_First2) ? _TrimResult::_ReturnTrue : _TrimResult::_ReturnFalse;
}
} while (_Pred(*_First1, *_First2));
} else {
if (!_Pred(*_Back1, *_Back2)) {
// nothing to trim
return _TrimResult::_HaveWorkAfterTrimming;
}
--_Back1;
--_Back2;
}
for (;;) {
if (_First1 == _Back1) {
// only one element is left, it can't match because it wasn't trimmed by the first loop
return _TrimResult::_ReturnFalse;
}
if (!_Pred(*_Back1, *_Back2)) {
return _TrimResult::_KeepTrimming;
}
--_Back1;
--_Back2;
}
}
template <class _BidIt1, class _BidIt2, class _Pr>
_NODISCARD _CONSTEXPR20 _TrimResult _Trim_reversed(
_BidIt1& _First1, _BidIt1& _Back1, _BidIt2& _First2, _BidIt2& _Back2, _Pr _Pred) {
// advances the iterators, trimming each range's prefix that matches the other range's suffix
// from [_First1, _Back1] and [_First2, _Back2]
_STL_INTERNAL_CHECK(_First1 != _Back1);
_STL_INTERNAL_CHECK(_STD distance(_First1, _Back1) == _STD distance(_First2, _Back2));
if (_Pred(*_First1, *_Back2)) {
do {
++_First1;
--_Back2;
if (_First1 == _Back1) {
// only one element is left
return _Pred(*_First1, *_First2) ? _TrimResult::_ReturnTrue : _TrimResult::_ReturnFalse;
}
} while (_Pred(*_First1, *_Back2));
} else {
if (!_Pred(*_Back1, *_First2)) {
// nothing to trim
return _TrimResult::_HaveWorkAfterTrimming;
}
--_Back1;
++_First2;
}
for (;;) {
if (_First1 == _Back1) {
// only one element is left, it can't match because it wasn't trimmed by the first loop
return _TrimResult::_ReturnFalse;
}
if (!_Pred(*_Back1, *_First2)) {
return _TrimResult::_KeepTrimming;
}
--_Back1;
++_First2;
}
}
template <class _BidIt1, class _BidIt2, class _Pr>
_NODISCARD _CONSTEXPR20 _TrimResult _Trim_completely(
_BidIt1& _First1, _BidIt1& _Back1, _BidIt2& _First2, _BidIt2& _Back2, _Pr _Pred) {
// alternates between calling _Trim_reversed and _Trim_equal until no more trimming is possible
_TrimResult _Res = _TrimResult::_KeepTrimming;
for (bool _Check_reversed = true; _Res == _TrimResult::_KeepTrimming; _Check_reversed = !_Check_reversed) {
if (_Check_reversed) {
_Res = _Trim_reversed(_First1, _Back1, _First2, _Back2, _Pred);
} else {
_Res = _Trim_equal(_First1, _Back1, _First2, _Back2, _Pred);
}
}
return _Res;
}
template <class _FwdIt1, class _FwdIt2, class _Pr>
_NODISCARD _CONSTEXPR20 bool _Check_match_counts(
const _FwdIt1 _First1, _FwdIt1 _Last1, const _FwdIt2 _First2, _FwdIt2 _Last2, _Pr _Pred) {
_FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, _Pr _Pred) {
// test if [_First1, _Last1) == permuted [_First2, _Last2), after matching prefix removal
_STL_INTERNAL_CHECK(!_Pred(*_First1, *_First2));
_STL_INTERNAL_CHECK(_STD distance(_First1, _Last1) == _STD distance(_First2, _Last2));
@ -5359,6 +5456,17 @@ _NODISCARD _CONSTEXPR20 bool _Check_match_counts(
--_Last1;
--_Last2;
} while (_Pred(*_Last1, *_Last2));
if (_First1 == _Last1) {
return false;
}
const _TrimResult _Res = _Trim_completely(_First1, _Last1, _First2, _Last2, _Pred);
if (_Res != _TrimResult::_HaveWorkAfterTrimming) {
return _Res == _TrimResult::_ReturnTrue;
}
++_Last1;
++_Last2;
}

Просмотреть файл

@ -331,6 +331,32 @@ int main() {
assert(!is_permutation(a.begin(), a.end(), b.begin(), b.end(), equal_to<int>()));
}
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
assert(is_permutation(begin(arr1), end(arr1), begin(arr2), end(arr2)));
}
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 8, 7, 3, 4, 5, 6, 2, 1, 0};
assert(is_permutation(begin(arr1), end(arr1), begin(arr2), end(arr2)));
}
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 1, 7, 3, 5, 4, 6, 2, 8, 0};
assert(is_permutation(begin(arr1), end(arr1), begin(arr2), end(arr2)));
}
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 1, 7, 5, 6, 3, 4, 2, 8, 0};
assert(is_permutation(begin(arr1), end(arr1), begin(arr2), end(arr2)));
}
{
int arr1[] = {0, 1, 2, 3, 4, 10, 5, 6, 7, 8, 9};
int arr2[] = {9, 1, 7, 3, 5, 11, 4, 6, 2, 8, 0};
assert(!is_permutation(begin(arr1), end(arr1), begin(arr2), end(arr2)));
}
{ // Test that _ITERATOR_DEBUG_ARRAY_OVERLOADS is not needed anymore
int arr[8] = {};
assert(mismatch(arr, arr, arr, arr) == make_pair(begin(arr), begin(arr)));

Просмотреть файл

@ -500,6 +500,10 @@ constexpr void test_permutations() {
{40, 30, 20, 10},
};
for (size_t i = 0; i < size(expected); ++i) {
assert(is_permutation(begin(buff), end(buff), begin(expected[i]), end(expected[i])));
}
size_t cursor = 0;
do {
assert(equal(begin(buff), end(buff), begin(expected[cursor]), end(expected[cursor])));
@ -518,6 +522,32 @@ constexpr void test_permutations() {
assert(cursor == 0);
assert(equal(begin(buff), end(buff), begin(expected[23]), end(expected[23])));
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
assert(is_permutation(begin(arr1), end(arr1), begin(arr2), end(arr2)));
}
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 8, 7, 3, 4, 5, 6, 2, 1, 0};
assert(is_permutation(begin(arr1), end(arr1), begin(arr2), end(arr2)));
}
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 1, 7, 3, 5, 4, 6, 2, 8, 0};
assert(is_permutation(begin(arr1), end(arr1), begin(arr2), end(arr2)));
}
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 1, 7, 5, 6, 3, 4, 2, 8, 0};
assert(is_permutation(begin(arr1), end(arr1), begin(arr2), end(arr2)));
}
{
int arr1[] = {0, 1, 2, 3, 4, 10, 5, 6, 7, 8, 9};
int arr2[] = {9, 1, 7, 3, 5, 11, 4, 6, 2, 8, 0};
assert(!is_permutation(begin(arr1), end(arr1), begin(arr2), end(arr2)));
}
}
constexpr bool test() {

Просмотреть файл

@ -88,6 +88,52 @@ struct instantiator {
assert(!ranges::is_permutation(ranges::begin(r1), ranges::end(r1), ranges::begin(r2), ranges::end(r2),
ranges::equal_to{}, get_first, identity{}));
}
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
const Fwd2 r1{arr1};
const Fwd2 r2{arr2};
assert(ranges::is_permutation(r1, r2));
}
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 8, 7, 3, 4, 5, 6, 2, 1, 0};
const Fwd2 r1{arr1};
const Fwd2 r2{arr2};
assert(ranges::is_permutation(r1, r2));
}
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 1, 7, 3, 5, 4, 6, 2, 8, 0};
const Fwd2 r1{arr1};
const Fwd2 r2{arr2};
assert(ranges::is_permutation(r1, r2));
}
{
int arr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int arr2[] = {9, 1, 7, 5, 6, 3, 4, 2, 8, 0};
const Fwd2 r1{arr1};
const Fwd2 r2{arr2};
assert(ranges::is_permutation(r1, r2));
}
{
int arr1[] = {0, 1, 2, 3, 4, 10, 5, 6, 7, 8, 9};
int arr2[] = {9, 1, 7, 3, 5, 11, 4, 6, 2, 8, 0};
const Fwd2 r1{arr1};
const Fwd2 r2{arr2};
assert(!ranges::is_permutation(r1, r2));
}
{
int arr1[] = {-1, 0, 1, 2, 3, 4, 5, 6, 7, 8};
int arr2[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
const Fwd2 r1{arr1};
const Fwd2 r2{arr2};
assert(!ranges::is_permutation(r1, r2));
assert(!ranges::is_permutation(r1, r2, {}, {}, [](int n) { return n - 1; }));
assert(ranges::is_permutation(r1, r2, {}, {}, [](int n) { return n - 2; }));
assert(ranges::is_permutation(
r1, r2, {}, [](int n) { return n + 1; }, [](int n) { return n - 1; }));
}
}
};