From 8745fbaeb17da11fec0725108737dda68c9b1442 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 17 Jul 2020 15:01:20 +0200 Subject: [PATCH 1/2] Implement range::shuffle --- stl/inc/algorithm | 64 +++++++++++++++++++ tests/std/test.lst | 1 + .../tests/P0896R4_ranges_alg_shuffle/env.lst | 4 ++ .../tests/P0896R4_ranges_alg_shuffle/test.cpp | 55 ++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 tests/std/tests/P0896R4_ranges_alg_shuffle/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_shuffle/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 80a0ce54552..1d70d2f07a4 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -4671,6 +4671,70 @@ void shuffle(_RanIt _First, _RanIt _Last, _Urng&& _Func) { // shuffle [_First, _ _Random_shuffle1(_First, _Last, _RngFunc); } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::shuffle + class _Shuffle_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Urng> + requires permutable<_It> && uniform_random_bit_generator> + _It operator()(_It _First, _Se _Last, _Urng&& _Func) const { + _Adl_verify_range(_First, _Last); + + _Rng_from_urng, remove_reference_t<_Urng>> _RngFunc(_Func); + auto _UResult = + _Shuffle_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _RngFunc); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + + template + requires permutable> && uniform_random_bit_generator> + borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Urng&& _Func) const { + auto _First = _RANGES begin(_Range); + + _Rng_from_urng, remove_reference_t<_Urng>> _RngFunc(_Func); + auto _UResult = _Shuffle_unchecked(_Ubegin(_Range), _Uend(_Range), _RngFunc); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + // clang-format on + private: + template + _NODISCARD static _It _Shuffle_unchecked(_It _First, const _Se _Last, _Urng& _Func) { + // shuffle [_First, _Last) using random function _Func + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(uniform_random_bit_generator>); + + if (_First == _Last) { + return _First; + } + using _Diff = iter_difference_t<_It>; + + auto _Target = _First; + _Diff _Target_index = 1; + for (; ++_Target != _Last; ++_Target_index) { + // randomly place an element from [_First, _Target] at _Target + const _Diff _Off = _Func(_Target_index + 1); + _STL_ASSERT(0 <= _Off && _Off <= _Target_index, "random value out of range"); + if (_Off != _Target_index) { // avoid self-move-assignment + _RANGES iter_swap(_Target, _First + _Off); + } + } + return _Target; + } + }; + + inline constexpr _Shuffle_fn shuffle{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + #if _HAS_AUTO_PTR_ETC // FUNCTION TEMPLATE random_shuffle WITH RANDOM FN template diff --git a/tests/std/test.lst b/tests/std/test.lst index 56b6f4f7191..ab1f4c9b48c 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -278,6 +278,7 @@ tests\P0896R4_ranges_alg_replace_copy_if tests\P0896R4_ranges_alg_replace_if tests\P0896R4_ranges_alg_search tests\P0896R4_ranges_alg_search_n +tests\P0896R4_ranges_alg_shuffle tests\P0896R4_ranges_alg_swap_ranges tests\P0896R4_ranges_alg_transform_binary tests\P0896R4_ranges_alg_transform_unary diff --git a/tests/std/tests/P0896R4_ranges_alg_shuffle/env.lst b/tests/std/tests/P0896R4_ranges_alg_shuffle/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_shuffle/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_shuffle/test.cpp b/tests/std/tests/P0896R4_ranges_alg_shuffle/test.cpp new file mode 100644 index 00000000000..7de12ff76a9 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_shuffle/test.cpp @@ -0,0 +1,55 @@ +// shuffleright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +mt19937 gen{random_device{}()}; + +// Validate dangling story +STATIC_ASSERT(same_as{}, gen)), ranges::dangling>); +STATIC_ASSERT(same_as{}, gen)), int*>); + +struct instantiator { + static constexpr int expected[7] = {1, 2, 3, 4, 5, 6, 7}; + + template + static void call() { + using ranges::shuffle, ranges::equal, ranges::is_sorted, ranges::iterator_t; + + { // Validate iterator + sentinel overload + int input[7] = {1, 2, 3, 4, 5, 6, 7}; + ReadWrite wrapped_input{input}; + + auto result = shuffle(wrapped_input.begin(), wrapped_input.end(), gen); + STATIC_ASSERT(same_as>); + assert(result.peek() == end(input)); + + sort(begin(input), end(input)); + assert(equal(input, expected)); + } + { // Validate range overload + int input[7] = {1, 2, 3, 4, 5, 6, 7}; + ReadWrite wrapped_input{input}; + + auto result = shuffle(wrapped_input, gen); + STATIC_ASSERT(same_as>); + assert(result.peek() == end(input)); + + sort(begin(input), end(input)); + assert(equal(input, expected)); + } + } +}; + +int main() { + test_random(); +} From bcc61a3ef59a852f40a56b18a29cad50ca0dea46 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sun, 19 Jul 2020 07:53:16 +0200 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: statementreply --- stl/inc/algorithm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 1d70d2f07a4..26a374373ce 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -4680,7 +4680,7 @@ namespace ranges { // clang-format off template _Se, class _Urng> - requires permutable<_It> && uniform_random_bit_generator> + requires permutable<_It> && uniform_random_bit_generator> _It operator()(_It _First, _Se _Last, _Urng&& _Func) const { _Adl_verify_range(_First, _Last); @@ -4693,7 +4693,7 @@ namespace ranges { } template - requires permutable> && uniform_random_bit_generator> + requires permutable> && uniform_random_bit_generator> borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Urng&& _Func) const { auto _First = _RANGES begin(_Range); @@ -4705,12 +4705,11 @@ namespace ranges { } // clang-format on private: - template - _NODISCARD static _It _Shuffle_unchecked(_It _First, const _Se _Last, _Urng& _Func) { + template + _NODISCARD static _It _Shuffle_unchecked(_It _First, const _Se _Last, _Rng& _Func) { // shuffle [_First, _Last) using random function _Func _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - _STL_INTERNAL_STATIC_ASSERT(uniform_random_bit_generator>); if (_First == _Last) { return _First;