diff --git a/stl/inc/algorithm b/stl/inc/algorithm index fb1965765..148989d21 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -9471,6 +9471,79 @@ _CONSTEXPR20 bool next_permutation(_BidIt _First, _BidIt _Last) { return _STD next_permutation(_First, _Last, less<>{}); } +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE next_permutation_result + template + using next_permutation_result = in_found_result<_In>; + + // VARIABLE ranges::next_permutation + class _Next_permutation_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pr = ranges::less, class _Pj = identity> + requires sortable<_It, _Pr, _Pj> + constexpr next_permutation_result<_It> operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + const bool _Found = + _Next_permutation_common(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return {_STD move(_First), _Found}; + } + + // clang-format off + template + requires sortable, _Pr, _Pj> + constexpr next_permutation_result> operator()( + _Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + auto _ULast = _Get_final_iterator_unwrapped(_Range); + const bool _Found = _Next_permutation_common(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return {_Rewrap_iterator(_Range, _STD move(_ULast)), _Found}; + } + + private: + template + _NODISCARD static constexpr bool _Next_permutation_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + auto _Next = _Last; + if (_First == _Last || _First == --_Next) { + return false; + } + + for (;;) { // find rightmost element smaller than successor + auto _Next1 = _Next; + if (_STD invoke(_Pred, _STD invoke(_Proj, *--_Next), _STD invoke(_Proj, *_Next1))) { + // swap with rightmost element that's smaller, flip suffix + auto _Mid = _Last; + do { + --_Mid; + } while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_Mid))); + + _RANGES iter_swap(_Next, _Mid); + _Reverse_common(_STD move(_Next1), _STD move(_Last)); + return true; + } + + if (_Next == _First) { // pure descending, flip all + _Reverse_common(_STD move(_First), _STD move(_Last)); + return false; + } + } + } + }; + + inline constexpr _Next_permutation_fn next_permutation{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE prev_permutation template _CONSTEXPR20 bool prev_permutation(_BidIt _First, _BidIt _Last, _Pr _Pred) { @@ -9509,6 +9582,79 @@ _CONSTEXPR20 bool prev_permutation(_BidIt _First, _BidIt _Last) { return _STD prev_permutation(_First, _Last, less<>{}); } +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE prev_permutation_result + template + using prev_permutation_result = in_found_result<_In>; + + // VARIABLE ranges::prev_permutation + class _Prev_permutation_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pr = ranges::less, class _Pj = identity> + requires sortable<_It, _Pr, _Pj> + constexpr prev_permutation_result<_It> operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + const bool _Found = + _Prev_permutation_common(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return {_STD move(_First), _Found}; + } + + // clang-format off + template + requires sortable, _Pr, _Pj> + constexpr prev_permutation_result> operator()( + _Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + auto _ULast = _Get_final_iterator_unwrapped(_Range); + const bool _Found = _Prev_permutation_common(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return {_Rewrap_iterator(_Range, _STD move(_ULast)), _Found}; + } + + private: + template + _NODISCARD static constexpr bool _Prev_permutation_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + auto _Next = _Last; + if (_First == _Last || _First == --_Next) { + return false; + } + + for (;;) { // find rightmost element not smaller than successor + auto _Next1 = _Next; + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next1), _STD invoke(_Proj, *--_Next))) { + // swap with rightmost element that's not smaller, flip suffix + auto _Mid = _Last; + do { + --_Mid; + } while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_Next))); + + _RANGES iter_swap(_Next, _Mid); + _Reverse_common(_STD move(_Next1), _STD move(_Last)); + return true; + } + + if (_Next == _First) { // pure ascending, flip all + _Reverse_common(_STD move(_First), _STD move(_Last)); + return false; + } + } + } + }; + + inline constexpr _Prev_permutation_fn prev_permutation{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATES is_sorted AND is_sorted_until template _NODISCARD _CONSTEXPR20 _FwdIt is_sorted_until(const _FwdIt _First, _FwdIt _Last, _Pr _Pred) { diff --git a/tests/std/test.lst b/tests/std/test.lst index 35d27c471..07d790a1f 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -274,6 +274,7 @@ tests\P0896R4_ranges_alg_nth_element tests\P0896R4_ranges_alg_partition tests\P0896R4_ranges_alg_partition_copy tests\P0896R4_ranges_alg_partition_point +tests\P0896R4_ranges_alg_permutations tests\P0896R4_ranges_alg_remove tests\P0896R4_ranges_alg_remove_copy tests\P0896R4_ranges_alg_remove_copy_if diff --git a/tests/std/tests/P0896R4_ranges_alg_permutations/env.lst b/tests/std/tests/P0896R4_ranges_alg_permutations/env.lst new file mode 100644 index 000000000..f3ccc8613 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_permutations/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_permutations/test.cpp b/tests/std/tests/P0896R4_ranges_alg_permutations/test.cpp new file mode 100644 index 000000000..6b8cd0037 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_permutations/test.cpp @@ -0,0 +1,264 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +#pragma warning(disable : 6294) // Ill-defined for-loop: initial condition does not satisfy test. Loop body not executed + +// Validate that (next|prev)_permutation_result alias in_found_result +STATIC_ASSERT(same_as, ranges::in_found_result>); +STATIC_ASSERT(same_as, ranges::in_found_result>); + +// Validate dangling story +STATIC_ASSERT( + same_as{})), ranges::next_permutation_result>); +STATIC_ASSERT(same_as{})), ranges::next_permutation_result>); +STATIC_ASSERT( + same_as{})), ranges::prev_permutation_result>); +STATIC_ASSERT(same_as{})), ranges::prev_permutation_result>); + +constexpr int perm1[][1] = {{0}}; +constexpr int perm4[][4] = {{0, 1, 2, 3}, {0, 1, 3, 2}, {0, 2, 1, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {0, 3, 2, 1}, + {1, 0, 2, 3}, {1, 0, 3, 2}, {1, 2, 0, 3}, {1, 2, 3, 0}, {1, 3, 0, 2}, {1, 3, 2, 0}, {2, 0, 1, 3}, {2, 0, 3, 1}, + {2, 1, 0, 3}, {2, 1, 3, 0}, {2, 3, 0, 1}, {2, 3, 1, 0}, {3, 0, 1, 2}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 1, 2, 0}, + {3, 2, 0, 1}, {3, 2, 1, 0}}; +constexpr int perm5[][5] = {{0, 1, 2, 3, 4}, {0, 1, 2, 4, 3}, {0, 1, 3, 2, 4}, {0, 1, 3, 4, 2}, {0, 1, 4, 2, 3}, + {0, 1, 4, 3, 2}, {0, 2, 1, 3, 4}, {0, 2, 1, 4, 3}, {0, 2, 3, 1, 4}, {0, 2, 3, 4, 1}, {0, 2, 4, 1, 3}, + {0, 2, 4, 3, 1}, {0, 3, 1, 2, 4}, {0, 3, 1, 4, 2}, {0, 3, 2, 1, 4}, {0, 3, 2, 4, 1}, {0, 3, 4, 1, 2}, + {0, 3, 4, 2, 1}, {0, 4, 1, 2, 3}, {0, 4, 1, 3, 2}, {0, 4, 2, 1, 3}, {0, 4, 2, 3, 1}, {0, 4, 3, 1, 2}, + {0, 4, 3, 2, 1}, {1, 0, 2, 3, 4}, {1, 0, 2, 4, 3}, {1, 0, 3, 2, 4}, {1, 0, 3, 4, 2}, {1, 0, 4, 2, 3}, + {1, 0, 4, 3, 2}, {1, 2, 0, 3, 4}, {1, 2, 0, 4, 3}, {1, 2, 3, 0, 4}, {1, 2, 3, 4, 0}, {1, 2, 4, 0, 3}, + {1, 2, 4, 3, 0}, {1, 3, 0, 2, 4}, {1, 3, 0, 4, 2}, {1, 3, 2, 0, 4}, {1, 3, 2, 4, 0}, {1, 3, 4, 0, 2}, + {1, 3, 4, 2, 0}, {1, 4, 0, 2, 3}, {1, 4, 0, 3, 2}, {1, 4, 2, 0, 3}, {1, 4, 2, 3, 0}, {1, 4, 3, 0, 2}, + {1, 4, 3, 2, 0}, {2, 0, 1, 3, 4}, {2, 0, 1, 4, 3}, {2, 0, 3, 1, 4}, {2, 0, 3, 4, 1}, {2, 0, 4, 1, 3}, + {2, 0, 4, 3, 1}, {2, 1, 0, 3, 4}, {2, 1, 0, 4, 3}, {2, 1, 3, 0, 4}, {2, 1, 3, 4, 0}, {2, 1, 4, 0, 3}, + {2, 1, 4, 3, 0}, {2, 3, 0, 1, 4}, {2, 3, 0, 4, 1}, {2, 3, 1, 0, 4}, {2, 3, 1, 4, 0}, {2, 3, 4, 0, 1}, + {2, 3, 4, 1, 0}, {2, 4, 0, 1, 3}, {2, 4, 0, 3, 1}, {2, 4, 1, 0, 3}, {2, 4, 1, 3, 0}, {2, 4, 3, 0, 1}, + {2, 4, 3, 1, 0}, {3, 0, 1, 2, 4}, {3, 0, 1, 4, 2}, {3, 0, 2, 1, 4}, {3, 0, 2, 4, 1}, {3, 0, 4, 1, 2}, + {3, 0, 4, 2, 1}, {3, 1, 0, 2, 4}, {3, 1, 0, 4, 2}, {3, 1, 2, 0, 4}, {3, 1, 2, 4, 0}, {3, 1, 4, 0, 2}, + {3, 1, 4, 2, 0}, {3, 2, 0, 1, 4}, {3, 2, 0, 4, 1}, {3, 2, 1, 0, 4}, {3, 2, 1, 4, 0}, {3, 2, 4, 0, 1}, + {3, 2, 4, 1, 0}, {3, 4, 0, 1, 2}, {3, 4, 0, 2, 1}, {3, 4, 1, 0, 2}, {3, 4, 1, 2, 0}, {3, 4, 2, 0, 1}, + {3, 4, 2, 1, 0}, {4, 0, 1, 2, 3}, {4, 0, 1, 3, 2}, {4, 0, 2, 1, 3}, {4, 0, 2, 3, 1}, {4, 0, 3, 1, 2}, + {4, 0, 3, 2, 1}, {4, 1, 0, 2, 3}, {4, 1, 0, 3, 2}, {4, 1, 2, 0, 3}, {4, 1, 2, 3, 0}, {4, 1, 3, 0, 2}, + {4, 1, 3, 2, 0}, {4, 2, 0, 1, 3}, {4, 2, 0, 3, 1}, {4, 2, 1, 0, 3}, {4, 2, 1, 3, 0}, {4, 2, 3, 0, 1}, + {4, 2, 3, 1, 0}, {4, 3, 0, 1, 2}, {4, 3, 0, 2, 1}, {4, 3, 1, 0, 2}, {4, 3, 1, 2, 0}, {4, 3, 2, 0, 1}, + {4, 3, 2, 1, 0}}; +constexpr int perm6[][6] = {{0, 0, 1, 1, 2, 2}, {0, 0, 1, 2, 1, 2}, {0, 0, 1, 2, 2, 1}, {0, 0, 2, 1, 1, 2}, + {0, 0, 2, 1, 2, 1}, {0, 0, 2, 2, 1, 1}, {0, 1, 0, 1, 2, 2}, {0, 1, 0, 2, 1, 2}, {0, 1, 0, 2, 2, 1}, + {0, 1, 1, 0, 2, 2}, {0, 1, 1, 2, 0, 2}, {0, 1, 1, 2, 2, 0}, {0, 1, 2, 0, 1, 2}, {0, 1, 2, 0, 2, 1}, + {0, 1, 2, 1, 0, 2}, {0, 1, 2, 1, 2, 0}, {0, 1, 2, 2, 0, 1}, {0, 1, 2, 2, 1, 0}, {0, 2, 0, 1, 1, 2}, + {0, 2, 0, 1, 2, 1}, {0, 2, 0, 2, 1, 1}, {0, 2, 1, 0, 1, 2}, {0, 2, 1, 0, 2, 1}, {0, 2, 1, 1, 0, 2}, + {0, 2, 1, 1, 2, 0}, {0, 2, 1, 2, 0, 1}, {0, 2, 1, 2, 1, 0}, {0, 2, 2, 0, 1, 1}, {0, 2, 2, 1, 0, 1}, + {0, 2, 2, 1, 1, 0}, {1, 0, 0, 1, 2, 2}, {1, 0, 0, 2, 1, 2}, {1, 0, 0, 2, 2, 1}, {1, 0, 1, 0, 2, 2}, + {1, 0, 1, 2, 0, 2}, {1, 0, 1, 2, 2, 0}, {1, 0, 2, 0, 1, 2}, {1, 0, 2, 0, 2, 1}, {1, 0, 2, 1, 0, 2}, + {1, 0, 2, 1, 2, 0}, {1, 0, 2, 2, 0, 1}, {1, 0, 2, 2, 1, 0}, {1, 1, 0, 0, 2, 2}, {1, 1, 0, 2, 0, 2}, + {1, 1, 0, 2, 2, 0}, {1, 1, 2, 0, 0, 2}, {1, 1, 2, 0, 2, 0}, {1, 1, 2, 2, 0, 0}, {1, 2, 0, 0, 1, 2}, + {1, 2, 0, 0, 2, 1}, {1, 2, 0, 1, 0, 2}, {1, 2, 0, 1, 2, 0}, {1, 2, 0, 2, 0, 1}, {1, 2, 0, 2, 1, 0}, + {1, 2, 1, 0, 0, 2}, {1, 2, 1, 0, 2, 0}, {1, 2, 1, 2, 0, 0}, {1, 2, 2, 0, 0, 1}, {1, 2, 2, 0, 1, 0}, + {1, 2, 2, 1, 0, 0}, {2, 0, 0, 1, 1, 2}, {2, 0, 0, 1, 2, 1}, {2, 0, 0, 2, 1, 1}, {2, 0, 1, 0, 1, 2}, + {2, 0, 1, 0, 2, 1}, {2, 0, 1, 1, 0, 2}, {2, 0, 1, 1, 2, 0}, {2, 0, 1, 2, 0, 1}, {2, 0, 1, 2, 1, 0}, + {2, 0, 2, 0, 1, 1}, {2, 0, 2, 1, 0, 1}, {2, 0, 2, 1, 1, 0}, {2, 1, 0, 0, 1, 2}, {2, 1, 0, 0, 2, 1}, + {2, 1, 0, 1, 0, 2}, {2, 1, 0, 1, 2, 0}, {2, 1, 0, 2, 0, 1}, {2, 1, 0, 2, 1, 0}, {2, 1, 1, 0, 0, 2}, + {2, 1, 1, 0, 2, 0}, {2, 1, 1, 2, 0, 0}, {2, 1, 2, 0, 0, 1}, {2, 1, 2, 0, 1, 0}, {2, 1, 2, 1, 0, 0}, + {2, 2, 0, 0, 1, 1}, {2, 2, 0, 1, 0, 1}, {2, 2, 0, 1, 1, 0}, {2, 2, 1, 0, 0, 1}, {2, 2, 1, 0, 1, 0}, + {2, 2, 1, 1, 0, 0}}; +constexpr int perm8[][8] = {{0, 0, 0, 0, 0, 0, 0, 0}}; + +struct int_wrapper { + int val = 10; + + constexpr int_wrapper() = default; + constexpr int_wrapper(int x) : val{x} {} + constexpr int_wrapper(const int_wrapper&) = default; + constexpr int_wrapper(int_wrapper&& that) : val{exchange(that.val, -1)} {} + constexpr int_wrapper& operator=(const int_wrapper&) = default; + + constexpr int_wrapper& operator=(int_wrapper&& that) { + val = exchange(that.val, -1); + return *this; + } + auto operator<=>(const int_wrapper&) const = default; +}; + +constexpr auto get_val = [](auto&& x) { return static_cast(x).val; }; + +template +struct next_perm_instantiator { + template + static constexpr void call() { + using ranges::next_permutation, ranges::next_permutation_result, ranges::equal, ranges::iterator_t; + + constexpr auto number_of_permutations = static_cast(ranges::size(Expected)); + + { // Validate range overload + int_wrapper input[ranges::size(Expected[0])]; + ranges::copy(Expected[0], input); + + for (int i = 1; i < number_of_permutations; ++i) { + R range{input}; + const same_as>> auto result = + next_permutation(range, ranges::less{}, get_val); + assert(result.in == range.end()); + assert(result.found); + assert(equal(input, Expected[i], ranges::equal_to{}, get_val)); + } + + R range{input}; + const same_as>> auto result = + next_permutation(range, ranges::less{}, get_val); + assert(result.in == range.end()); + assert(!result.found); + assert(equal(input, Expected[0])); + } + + { // Validate iterator overload + int_wrapper input[ranges::size(Expected[0])]; + ranges::copy(Expected[0], input); + + for (int i = 1; i < number_of_permutations; ++i) { + R range{input}; + const same_as>> auto result = + next_permutation(range.begin(), range.end(), ranges::less{}, get_val); + assert(result.in == range.end()); + assert(result.found); + assert(equal(input, Expected[i])); + } + + R range{input}; + const same_as>> auto result = + next_permutation(range.begin(), range.end(), ranges::less{}, get_val); + assert(result.in == range.end()); + assert(!result.found); + assert(equal(input, Expected[0])); + } + } +}; + +template +struct prev_perm_instantiator { + template + static constexpr void call() { + using ranges::prev_permutation, ranges::prev_permutation_result, ranges::equal, ranges::iterator_t; + + constexpr auto number_of_permutations = static_cast(ranges::size(Expected)); + + { // Validate range overload + int_wrapper input[ranges::size(Expected[0])]; + ranges::copy(Expected[number_of_permutations - 1], input); + + for (int i = number_of_permutations - 1; i-- > 0;) { + R range{input}; + const same_as>> auto result = + prev_permutation(range, ranges::less{}, get_val); + assert(result.in == range.end()); + assert(result.found); + assert(equal(input, Expected[i])); + } + + R range{input}; + const same_as>> auto result = + prev_permutation(range, ranges::less{}, get_val); + assert(result.in == range.end()); + assert(!result.found); + assert(equal(input, Expected[number_of_permutations - 1])); + } + + { // Validate iterator overload + int_wrapper input[ranges::size(Expected[0])]; + ranges::copy(Expected[number_of_permutations - 1], input); + + for (int i = number_of_permutations - 1; i-- > 0;) { + R range{input}; + const same_as>> auto result = + prev_permutation(range.begin(), range.end(), ranges::less{}, get_val); + assert(result.in == range.end()); + assert(result.found); + assert(equal(input, Expected[i])); + } + + R range{input}; + const same_as>> auto result = + prev_permutation(range.begin(), range.end(), ranges::less{}, get_val); + assert(result.in == range.end()); + assert(!result.found); + assert(equal(input, Expected[number_of_permutations - 1])); + } + } +}; + +struct empty_range_test { + template + static constexpr void call() { + using ranges::next_permutation, ranges::next_permutation_result, ranges::prev_permutation, + ranges::prev_permutation_result, ranges::iterator_t; + + { // Validate range overload, next_permutation + R range{}; + const same_as>> auto result = + next_permutation(range, ranges::less{}, get_val); + assert(result.in == range.end()); + assert(!result.found); + } + + { // Validate iterator overload, next_permutation + R range{}; + const same_as>> auto result = + next_permutation(range.begin(), range.end(), ranges::less{}, get_val); + assert(result.in == range.end()); + assert(!result.found); + } + + { // Validate range overload, prev_permutation + R range{}; + const same_as>> auto result = + prev_permutation(range, ranges::less{}, get_val); + assert(result.in == range.end()); + assert(!result.found); + } + + { // Validate iterator overload, prev_permutation + R range{}; + const same_as>> auto result = + prev_permutation(range.begin(), range.end(), ranges::less{}, get_val); + assert(result.in == range.end()); + assert(!result.found); + } + } +}; + +int main() { +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-938163 + STATIC_ASSERT((test_bidi(), true)); + + STATIC_ASSERT((test_bidi, int_wrapper>(), true)); + STATIC_ASSERT((test_bidi, int_wrapper>(), true)); + STATIC_ASSERT((test_bidi, int_wrapper>(), true)); + + STATIC_ASSERT((test_bidi, int_wrapper>(), true)); + STATIC_ASSERT((test_bidi, int_wrapper>(), true)); + STATIC_ASSERT((test_bidi, int_wrapper>(), true)); +#endif // TRANSITION, VSO-938163 + + test_bidi(); + + test_bidi, int_wrapper>(); + test_bidi, int_wrapper>(); + test_bidi, int_wrapper>(); + test_bidi, int_wrapper>(); + test_bidi, int_wrapper>(); + + test_bidi, int_wrapper>(); + test_bidi, int_wrapper>(); + test_bidi, int_wrapper>(); + test_bidi, int_wrapper>(); + test_bidi, int_wrapper>(); +}