зеркало из https://github.com/microsoft/STL.git
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:
Родитель
08f12aecf6
Коммит
254fca2427
|
@ -985,8 +985,6 @@ namespace ranges {
|
||||||
--_Final1;
|
--_Final1;
|
||||||
--_Final2;
|
--_Final2;
|
||||||
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Final1), _STD invoke(_Proj2, *_Final2))) { // mismatch
|
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Final1), _STD invoke(_Proj2, *_Final2))) { // mismatch
|
||||||
++_Final1;
|
|
||||||
++_Final2;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -996,6 +994,32 @@ namespace ranges {
|
||||||
}
|
}
|
||||||
// If we get here, _Count > 1, initial elements do not match, and final elements do not match.
|
// 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),
|
return _Match_counts(_STD move(_First1), _STD move(_Final1), _STD move(_First2), _STD move(_Final2),
|
||||||
_Pred, _Proj1, _Proj2);
|
_Pred, _Proj1, _Proj2);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1053,13 +1077,37 @@ namespace ranges {
|
||||||
--_Final2; // since ranges have equal lengths, _Final2 cannot equal _First2
|
--_Final2; // since ranges have equal lengths, _Final2 cannot equal _First2
|
||||||
|
|
||||||
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Final1), _STD invoke(_Proj2, *_Final2))) { // mismatch
|
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Final1), _STD invoke(_Proj2, *_Final2))) { // mismatch
|
||||||
++_Final1;
|
|
||||||
++_Final2;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If we get here, initial elements do not match, final elements do not match, and ranges have length
|
// If we get here, initial elements do not match, final elements do not match, and ranges have length
|
||||||
// at least 2.
|
// 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(
|
return _Match_counts(
|
||||||
|
|
110
stl/inc/xutility
110
stl/inc/xutility
|
@ -5348,9 +5348,106 @@ _NODISCARD constexpr _Iter_diff_t<_InIt> _Count_pr(_InIt _First, const _InIt _La
|
||||||
return _Count;
|
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>
|
template <class _FwdIt1, class _FwdIt2, class _Pr>
|
||||||
_NODISCARD _CONSTEXPR20 bool _Check_match_counts(
|
_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
|
// test if [_First1, _Last1) == permuted [_First2, _Last2), after matching prefix removal
|
||||||
_STL_INTERNAL_CHECK(!_Pred(*_First1, *_First2));
|
_STL_INTERNAL_CHECK(!_Pred(*_First1, *_First2));
|
||||||
_STL_INTERNAL_CHECK(_STD distance(_First1, _Last1) == _STD distance(_First2, _Last2));
|
_STL_INTERNAL_CHECK(_STD distance(_First1, _Last1) == _STD distance(_First2, _Last2));
|
||||||
|
@ -5359,6 +5456,17 @@ _NODISCARD _CONSTEXPR20 bool _Check_match_counts(
|
||||||
--_Last1;
|
--_Last1;
|
||||||
--_Last2;
|
--_Last2;
|
||||||
} while (_Pred(*_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;
|
++_Last1;
|
||||||
++_Last2;
|
++_Last2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -331,6 +331,32 @@ int main() {
|
||||||
assert(!is_permutation(a.begin(), a.end(), b.begin(), b.end(), equal_to<int>()));
|
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
|
{ // Test that _ITERATOR_DEBUG_ARRAY_OVERLOADS is not needed anymore
|
||||||
int arr[8] = {};
|
int arr[8] = {};
|
||||||
assert(mismatch(arr, arr, arr, arr) == make_pair(begin(arr), begin(arr)));
|
assert(mismatch(arr, arr, arr, arr) == make_pair(begin(arr), begin(arr)));
|
||||||
|
|
|
@ -500,6 +500,10 @@ constexpr void test_permutations() {
|
||||||
{40, 30, 20, 10},
|
{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;
|
size_t cursor = 0;
|
||||||
do {
|
do {
|
||||||
assert(equal(begin(buff), end(buff), begin(expected[cursor]), end(expected[cursor])));
|
assert(equal(begin(buff), end(buff), begin(expected[cursor]), end(expected[cursor])));
|
||||||
|
@ -518,6 +522,32 @@ constexpr void test_permutations() {
|
||||||
|
|
||||||
assert(cursor == 0);
|
assert(cursor == 0);
|
||||||
assert(equal(begin(buff), end(buff), begin(expected[23]), end(expected[23])));
|
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() {
|
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),
|
assert(!ranges::is_permutation(ranges::begin(r1), ranges::end(r1), ranges::begin(r2), ranges::end(r2),
|
||||||
ranges::equal_to{}, get_first, identity{}));
|
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; }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче