//===----------------------------------------------------------------------===// // // 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, weakly_incrementable O, class Gen> // requires (forward_iterator || random_access_iterator) && // indirectly_copyable && // uniform_random_bit_generator> // O sample(I first, S last, O out, iter_difference_t n, Gen&& g); // Since C++20 // // template // requires (forward_range || random_access_iterator) && // indirectly_copyable, O> && // uniform_random_bit_generator> // O sample(R&& r, O out, range_difference_t n, 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 HasSampleIter = requires(Iter&& iter, Sent&& sent, Out&& out, std::iter_difference_t n, Gen&& gen) { std::ranges::sample(std::forward(iter), std::forward(sent), std::forward(out), n, std::forward(gen)); }; static_assert(HasSampleIter); // !input_iterator static_assert(!HasSampleIter); static_assert(!HasSampleIter); static_assert(!HasSampleIter); // !sentinel_for static_assert(!HasSampleIter); static_assert(!HasSampleIter); // !weakly_incrementable static_assert(!HasSampleIter); // (forward_iterator || random_access_iterator) static_assert(HasSampleIter< forward_iterator, forward_iterator, cpp20_output_iterator >); static_assert(HasSampleIter< cpp20_input_iterator, sentinel_wrapper>, random_access_iterator >); // !(forward_iterator || random_access_iterator) static_assert(!HasSampleIter< cpp20_input_iterator, sentinel_wrapper>, cpp20_output_iterator >); // !indirectly_copyable static_assert(!HasSampleIter); // !uniform_random_bit_generator> static_assert(!HasSampleIter); // Test constraints of the (range) overload. // ========================================= template concept HasSampleRange = requires(Range&& range, Out&& out, std::ranges::range_difference_t n, Gen&& gen) { std::ranges::sample(std::forward(range), std::forward(out), n, std::forward(gen)); }; template using R = UncheckedRange; static_assert(HasSampleRange, int*, RandGen>); // !input_range static_assert(!HasSampleRange); static_assert(!HasSampleRange); static_assert(!HasSampleRange); // !weakly_incrementable static_assert(!HasSampleRange, WeaklyIncrementableNotMovable>); // (forward_range || random_access_iterator) static_assert(HasSampleRange< R>, cpp20_output_iterator >); static_assert(HasSampleRange< R>, random_access_iterator >); // !(forward_range || random_access_iterator) static_assert(!HasSampleRange< R>, cpp20_output_iterator >); // !indirectly_copyable static_assert(!HasSampleRange, int**>); // !uniform_random_bit_generator> static_assert(!HasSampleRange, int*, BadGen>); template void test_one(std::array in, std::size_t n, Gen gen) { assert(n <= static_cast(N)); auto verify_is_subsequence = [&] (auto output) { auto sorted_input = in; std::ranges::sort(sorted_input); auto sorted_output = std::ranges::subrange(output.begin(), output.begin() + n); std::ranges::sort(sorted_output); assert(std::ranges::includes(sorted_input, sorted_output)); }; { // (iterator, sentinel) overload. auto begin = Iter(in.data()); auto end = Sent(Iter(in.data() + in.size())); std::array output; auto out = Out(output.data()); std::same_as decltype(auto) result = std::ranges::sample( std::move(begin), std::move(end), std::move(out), n, gen); assert(base(result) == output.data() + n); verify_is_subsequence(output); // The output of `sample` is implementation-specific. } { // (range) overload. auto begin = Iter(in.data()); auto end = Sent(Iter(in.data() + in.size())); std::array output; auto out = Out(output.data()); std::same_as decltype(auto) result = std::ranges::sample(std::ranges::subrange( std::move(begin), std::move(end)), std::move(out), n, gen); assert(base(result) == output.data() + n); verify_is_subsequence(output); // The output of `sample` is implementation-specific. } } template void test_iterators_iter_sent_out() { RandGen gen; // Empty sequence. test_one({}, 0, gen); // 1-element sequence. test_one({1}, 1, gen); // 2-element sequence. test_one({1, 2}, 1, gen); test_one({1, 2}, 2, gen); // n == 0. test_one({1, 2, 3}, 0, gen); // Longer sequence. { std::array input = {1, 8, 2, 3, 4, 6, 5, 7}; for (int i = 0; i <= static_cast(input.size()); ++i){ test_one(input, i, gen); } } } template void test_iterators_iter_sent() { if constexpr (std::forward_iterator) { test_iterators_iter_sent_out>(); test_iterators_iter_sent_out>(); } test_iterators_iter_sent_out>(); test_iterators_iter_sent_out>(); test_iterators_iter_sent_out(); } template void test_iterators_iter() { if constexpr (std::sentinel_for) { test_iterators_iter_sent(); } test_iterators_iter_sent>(); } void test_iterators() { test_iterators_iter>(); test_iterators_iter>(); 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}; constexpr int N = 5; std::array output; auto begin = in.begin(); auto end = in.end(); auto out = output.begin(); { // Lvalue. Gen g; std::ranges::sample(begin, end, out, N, g); std::ranges::sample(in, out, N, g); } if constexpr (CheckConst) { // Const lvalue. const Gen g; std::ranges::sample(begin, end, out, N, g); std::ranges::sample(in, out, N, g); } { // Prvalue. std::ranges::sample(begin, end, out, N, Gen()); std::ranges::sample(in, out, N, Gen()); } { // Xvalue. Gen g1, g2; std::ranges::sample(begin, end, out, N, std::move(g1)); std::ranges::sample(in, out, N, 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(); { // Stable (if `I` models `forward_iterator`). struct OrderedValue { int value; int original_order; bool operator==(const OrderedValue&) const = default; auto operator<=>(const OrderedValue& rhs) const { return value <=> rhs.value; } }; const std::array in = {{ {1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}, {1, 7}, {1, 8} }}; { // (iterator, sentinel) overload. std::array out; std::ranges::sample(in.begin(), in.end(), out.begin(), in.size(), RandGen()); assert(out == in); } { // (range) overload. std::array out; std::ranges::sample(in, out.begin(), in.size(), RandGen()); assert(out == in); } } } int main(int, char**) { test(); // Note: `ranges::sample` is not `constexpr`. return 0; }