#include <algorithm>
#include <array>
#include <concepts>
#include <functional>
#include <random>
#include <ranges>
#include <utility>
#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<RandGen>);
LIBCPP_STATIC_ASSERT(!std::__libcpp_random_is_valid_urng<RandGen>::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<BadGen>);
template <class Iter = int*, class Sent = int*, class Out = int*, class Gen = RandGen>
concept HasSampleIter =
requires(Iter&& iter, Sent&& sent, Out&& out, std::iter_difference_t<Iter> n, Gen&& gen) {
std::ranges::sample(std::forward<Iter>(iter), std::forward<Sent>(sent),
std::forward<Out>(out), n, std::forward<Gen>(gen));
};
static_assert(HasSampleIter<int*, int*, int*, RandGen>);
static_assert(!HasSampleIter<InputIteratorNotDerivedFrom>);
static_assert(!HasSampleIter<InputIteratorNotIndirectlyReadable>);
static_assert(!HasSampleIter<InputIteratorNotInputOrOutputIterator>);
static_assert(!HasSampleIter<int*, SentinelForNotSemiregular>);
static_assert(!HasSampleIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
static_assert(!HasSampleIter<int*, int*, WeaklyIncrementableNotMovable>);
static_assert(HasSampleIter<
forward_iterator<int*>, forward_iterator<int*>,
cpp20_output_iterator<int*>
>);
static_assert(HasSampleIter<
cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>,
random_access_iterator<int*>
>);
static_assert(!HasSampleIter<
cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>,
cpp20_output_iterator<int*>
>);
static_assert(!HasSampleIter<int*, int*, int**>);
static_assert(!HasSampleIter<int*, int*, int*, BadGen>);
template <class Range, class Out = int*, class Gen = RandGen>
concept HasSampleRange =
requires(Range&& range, Out&& out, std::ranges::range_difference_t<Range> n, Gen&& gen) {
std::ranges::sample(std::forward<Range>(range), std::forward<Out>(out), n, std::forward<Gen>(gen));
};
template <class T>
using R = UncheckedRange<T>;
static_assert(HasSampleRange<R<int*>, int*, RandGen>);
static_assert(!HasSampleRange<InputRangeNotDerivedFrom>);
static_assert(!HasSampleRange<InputRangeNotIndirectlyReadable>);
static_assert(!HasSampleRange<InputRangeNotInputOrOutputIterator>);
static_assert(!HasSampleRange<R<int*>, WeaklyIncrementableNotMovable>);
static_assert(HasSampleRange<
R<forward_iterator<int*>>,
cpp20_output_iterator<int*>
>);
static_assert(HasSampleRange<
R<cpp20_input_iterator<int*>>,
random_access_iterator<int*>
>);
static_assert(!HasSampleRange<
R<cpp20_input_iterator<int*>>,
cpp20_output_iterator<int*>
>);
static_assert(!HasSampleRange<R<int*>, int**>);
static_assert(!HasSampleRange<R<int*>, int*, BadGen>);
template <class Iter, class Sent, class Out, std::size_t N, class Gen>
void test_one(std::array<int, N> in, std::size_t n, Gen gen) {
assert(n <= static_cast<std::size_t>(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));
};
{
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
std::array<int, N> output;
auto out = Out(output.data());
std::same_as<Out> 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);
}
{
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
std::array<int, N> output;
auto out = Out(output.data());
std::same_as<Out> 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);
}
}
template <class Iter, class Sent, class Out>
void test_iterators_iter_sent_out() {
RandGen gen;
test_one<Iter, Sent, Out, 0>({}, 0, gen);
test_one<Iter, Sent, Out, 1>({1}, 1, gen);
test_one<Iter, Sent, Out, 2>({1, 2}, 1, gen);
test_one<Iter, Sent, Out, 2>({1, 2}, 2, gen);
test_one<Iter, Sent, Out, 3>({1, 2, 3}, 0, gen);
{
std::array input = {1, 8, 2, 3, 4, 6, 5, 7};
for (int i = 0; i <= static_cast<int>(input.size()); ++i){
test_one<Iter, Sent, Out, input.size()>(input, i, gen);
}
}
}
template <class Iter, class Sent>
void test_iterators_iter_sent() {
if constexpr (std::forward_iterator<Iter>) {
test_iterators_iter_sent_out<Iter, Sent, cpp20_output_iterator<int*>>();
test_iterators_iter_sent_out<Iter, Sent, forward_iterator<int*>>();
}
test_iterators_iter_sent_out<Iter, Sent, random_access_iterator<int*>>();
test_iterators_iter_sent_out<Iter, Sent, contiguous_iterator<int*>>();
test_iterators_iter_sent_out<Iter, Sent, int*>();
}
template <class Iter>
void test_iterators_iter() {
if constexpr (std::sentinel_for<Iter, Iter>) {
test_iterators_iter_sent<Iter, Iter>();
}
test_iterators_iter_sent<Iter, sentinel_wrapper<Iter>>();
}
void test_iterators() {
test_iterators_iter<cpp20_input_iterator<int*>>();
test_iterators_iter<random_access_iterator<int*>>();
test_iterators_iter<contiguous_iterator<int*>>();
test_iterators_iter<int*>();
test_iterators_iter<const int*>();
}
template <class Gen, bool CheckConst = true>
void test_generator() {
std::array in = {1, 2, 3, 4, 5, 6, 7, 8};
constexpr int N = 5;
std::array<int, N> output;
auto begin = in.begin();
auto end = in.end();
auto out = output.begin();
{
Gen g;
std::ranges::sample(begin, end, out, N, g);
std::ranges::sample(in, out, N, g);
}
if constexpr (CheckConst) {
const Gen g;
std::ranges::sample(begin, end, out, N, g);
std::ranges::sample(in, out, N, g);
}
{
std::ranges::sample(begin, end, out, N, Gen());
std::ranges::sample(in, out, N, Gen());
}
{
Gen g1, g2;
std::ranges::sample(begin, end, out, N, std::move(g1));
std::ranges::sample(in, out, N, std::move(g2));
}
}
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<ConstGen>();
test_generator<NonconstGen, false>();
test_generator<ConstAndNonconstGen>();
}
void test() {
test_iterators();
test_generators();
{
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<OrderedValue, 8> in = {{
{1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}, {1, 7}, {1, 8}
}};
{
std::array<OrderedValue, in.size()> out;
std::ranges::sample(in.begin(), in.end(), out.begin(), in.size(), RandGen());
assert(out == in);
}
{
std::array<OrderedValue, in.size()> out;
std::ranges::sample(in, out.begin(), in.size(), RandGen());
assert(out == in);
}
}
}
int main(int, char**) {
test();
return 0;
}