Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -4671,6 +4671,69 @@ 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 <random_access_iterator _It, sentinel_for<_It> _Se, class _Urng>
requires permutable<_It> && uniform_random_bit_generator<remove_reference_t<_Urng>>
_It operator()(_It _First, _Se _Last, _Urng&& _Func) const {
_Adl_verify_range(_First, _Last);

_Rng_from_urng<iter_difference_t<_It>, 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 <random_access_range _Rng, class _Urng>
requires permutable<iterator_t<_Rng>> && uniform_random_bit_generator<remove_reference_t<_Urng>>
borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Urng&& _Func) const {
auto _First = _RANGES begin(_Range);

_Rng_from_urng<range_difference_t<_Rng>, 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 <class _It, class _Se, class _Rng>
_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>);

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 <class _RanIt, class _RngFn>
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_shuffle/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_matrix.lst
55 changes: 55 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_shuffle/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// shuffleright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <concepts>
#include <random>
#include <ranges>
#include <utility>

#include <range_algorithm_support.hpp>

using namespace std;

mt19937 gen{random_device{}()};

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::shuffle(borrowed<false>{}, gen)), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(ranges::shuffle(borrowed<true>{}, gen)), int*>);

struct instantiator {
static constexpr int expected[7] = {1, 2, 3, 4, 5, 6, 7};

template <ranges::random_access_range ReadWrite>
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<decltype(result), iterator_t<ReadWrite>>);
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<decltype(result), iterator_t<ReadWrite>>);
assert(result.peek() == end(input));

sort(begin(input), end(input));
assert(equal(input, expected));
}
}
};

int main() {
test_random<instantiator, int>();
}