diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 12077672a..1c269f864 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -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 = [&](_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 = [&](_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( diff --git a/stl/inc/xutility b/stl/inc/xutility index f9e058b16..f03e03fdd 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -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 +_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 +_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 +_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 _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; } diff --git a/tests/std/tests/Dev11_0000000_dual_range_algorithms/test.cpp b/tests/std/tests/Dev11_0000000_dual_range_algorithms/test.cpp index 8dbef86df..6f02556e5 100644 --- a/tests/std/tests/Dev11_0000000_dual_range_algorithms/test.cpp +++ b/tests/std/tests/Dev11_0000000_dual_range_algorithms/test.cpp @@ -331,6 +331,32 @@ int main() { assert(!is_permutation(a.begin(), a.end(), b.begin(), b.end(), equal_to())); } + { + 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))); diff --git a/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp b/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp index c05948020..d7615f245 100644 --- a/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp +++ b/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp @@ -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() { diff --git a/tests/std/tests/P0896R4_ranges_alg_is_permutation/test.cpp b/tests/std/tests/P0896R4_ranges_alg_is_permutation/test.cpp index 308e50f45..ea364cdca 100644 --- a/tests/std/tests/P0896R4_ranges_alg_is_permutation/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_is_permutation/test.cpp @@ -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; })); + } } };