//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 // // template S, class Gen> // requires permutable && // uniform_random_bit_generator> // I shuffle(I first, S last, Gen&& g); // Since C++20 // // template // requires permutable> && // uniform_random_bit_generator> // borrowed_iterator_t shuffle(R&& r, Gen&& g); // Since C++20 #include #include #include #include #include #include #include #include "almost_satisfies_types.h" #include "test_iterators.h" #include "test_macros.h" class RandGen { public: constexpr static std::size_t min() { return 0; } constexpr static std::size_t max() { return 255; } constexpr std::size_t operator()() { flip = !flip; return flip; } private: bool flip = false; }; static_assert(std::uniform_random_bit_generator); // `std::uniform_random_bit_generator` is a subset of requirements of `__libcpp_random_is_valid_urng`. Make sure that // a type satisfying the required minimum is still accepted by `ranges::shuffle`. LIBCPP_STATIC_ASSERT(!std::__libcpp_random_is_valid_urng::value); struct BadGen { constexpr static std::size_t min() { return 255; } constexpr static std::size_t max() { return 0; } constexpr std::size_t operator()() const; }; static_assert(!std::uniform_random_bit_generator); // Test constraints of the (iterator, sentinel) overload. // ====================================================== template concept HasShuffleIter = requires(Iter&& iter, Sent&& sent, Gen&& gen) { std::ranges::shuffle(std::forward(iter), std::forward(sent), std::forward(gen)); }; static_assert(HasShuffleIter); // !random_access_iterator static_assert(!HasShuffleIter); static_assert(!HasShuffleIter); // !sentinel_for static_assert(!HasShuffleIter); static_assert(!HasShuffleIter); // !permutable static_assert(!HasShuffleIter); static_assert(!HasShuffleIter); // !uniform_random_bit_generator> static_assert(!HasShuffleIter); // Test constraints of the (range) overload. // ========================================= template concept HasShuffleRange = requires(Range&& range, Gen&& gen) { std::ranges::shuffle(std::forward(range), std::forward(gen)); }; template using R = UncheckedRange; static_assert(HasShuffleRange, RandGen>); // !random_access_range static_assert(!HasShuffleRange); static_assert(!HasShuffleRange); // !permutable> static_assert(!HasShuffleRange); static_assert(!HasShuffleRange); // !uniform_random_bit_generator> static_assert(!HasShuffleRange, BadGen>); template void test_one(const std::array input, Gen gen) { { // (iterator, sentinel) overload. auto shuffled = input; auto begin = Iter(shuffled.data()); auto end = Sent(Iter(shuffled.data() + shuffled.size())); std::same_as decltype(auto) result = std::ranges::shuffle(begin, end, gen); assert(result == Iter(shuffled.data() + shuffled.size())); // TODO(ranges): uncomment `ranges::is_permutation` once https://reviews.llvm.org/D127194 lands and remove sorting. //assert(std::ranges::is_permutation(shuffled, input); { auto shuffled_sorted = shuffled; std::ranges::sort(shuffled_sorted); auto original_sorted = input; std::ranges::sort(original_sorted); assert(shuffled_sorted == original_sorted); } } { // (range) overload. auto shuffled = input; auto begin = Iter(shuffled.data()); auto end = Sent(Iter(shuffled.data() + shuffled.size())); auto range = std::ranges::subrange(begin, end); std::same_as decltype(auto) result = std::ranges::shuffle(range, gen); assert(result == Iter(shuffled.data() + shuffled.size())); // TODO(ranges): uncomment `ranges::is_permutation` once https://reviews.llvm.org/D127194 lands and remove sorting. //assert(std::ranges::is_permutation(shuffled, input); { auto shuffled_sorted = shuffled; std::ranges::sort(shuffled_sorted); auto original_sorted = input; std::ranges::sort(original_sorted); assert(shuffled_sorted == original_sorted); } } } template void test_iterators_iter_sent() { RandGen gen; // Empty sequence. test_one({}, gen); // 1-element sequence. test_one({1}, gen); // 2-element sequence. test_one({2, 1}, gen); // 3-element sequence. test_one({2, 1, 3}, gen); // Longer sequence. test_one({2, 1, 3, 6, 8, 4, 11, 5}, gen); // Longer sequence with duplicates. test_one({2, 1, 3, 6, 2, 8, 1, 6}, gen); // All elements are the same. test_one({1, 1, 1}, gen); } template void test_iterators_iter() { test_iterators_iter_sent(); test_iterators_iter_sent>(); } void test_iterators() { test_iterators_iter>(); test_iterators_iter>(); test_iterators_iter(); } // Checks the logic for wrapping the given iterator to make sure it works correctly regardless of the value category of // the given generator object. template void test_generator() { std::array in = {1, 2, 3, 4, 5, 6, 7, 8}; auto begin = in.begin(); auto end = in.end(); { // Lvalue. Gen g; std::ranges::shuffle(begin, end, g); std::ranges::shuffle(in, g); } if constexpr (CheckConst) { // Const lvalue. const Gen g; std::ranges::shuffle(begin, end, g); std::ranges::shuffle(in, g); } { // Prvalue. std::ranges::shuffle(begin, end, Gen()); std::ranges::shuffle(in, Gen()); } { // Xvalue. Gen g1, g2; std::ranges::shuffle(begin, end, std::move(g1)); std::ranges::shuffle(in, std::move(g2)); } } // Checks the logic for wrapping the given iterator to make sure it works correctly regardless of whether the given // generator class has a const or non-const invocation operator (or both). void test_generators() { struct GenBase { constexpr static std::size_t min() { return 0; } constexpr static std::size_t max() { return 255; } }; struct NonconstGen : GenBase { std::size_t operator()() { return 1; } }; struct ConstGen : GenBase { std::size_t operator()() const { return 1; } }; struct ConstAndNonconstGen : GenBase { std::size_t operator()() { return 1; } std::size_t operator()() const { return 1; } }; test_generator(); test_generator(); test_generator(); } void test() { test_iterators(); test_generators(); { // Complexity: Exactly `(last - first) - 1` swaps. { std::array in = {-2, -5, -8, -11, -10, -5, 1, 3, 9, 6, 8, 2, 4, 2}; //14 int swaps = 0; auto begin = adl::Iterator::TrackSwaps(in.data(), swaps); auto end = adl::Iterator::TrackSwaps(in.data() + in.size(), swaps); std::ranges::shuffle(begin, end, RandGen()); int expected = in.size() - 1; // Note: our implementation doesn't perform a swap when the distribution returns 0, so the actual number of swaps // might be less than specified in the standard. assert(swaps <= expected); swaps = 0; } } } int main(int, char**) { test(); // Note: `ranges::shuffle` is not `constexpr`. return 0; }