#include "rand/xorshift.hpp"
#include "rand/lcg.hpp"
#include "rand/mwc64x.hpp"
#include "rand/pcg.hpp"
#include "rand/rdrand.hpp"
#include "rand/system.hpp"
#include "rand/xoshiro.hpp"
#include "rand/splitmix64.hpp"

#include "tap.hpp"
#include "maths.hpp"
#include "types/string.hpp"
#include "introspection/name.hpp"


/// //////////////////////////////////////////////////////////////////////////////
/// Check that outputs from a UniformRandomBitGenerator are roughly divisible
/// between a number of fixed width buckets.
///
/// This is not anything close to a strict statistical test. It is more aimed
/// at link testing and basic functionality verification within a small
/// resource footprint (ie, fast unit testing).
template <typename GeneratorT, typename ...Args>
static void
test_buckets (cruft::TAP::logger &tap, Args&& ...args)
{
    constexpr unsigned BUCKET_BITS  = 8u;
    constexpr size_t   BUCKET_COUNT = 1u << BUCKET_BITS;
    constexpr unsigned EXPECTED     = 1024u;
    constexpr unsigned ITERATIONS = BUCKET_COUNT * EXPECTED;

    GeneratorT gen (std::forward<Args> (args)...);
    std::uniform_int_distribution<int> dist (0, BUCKET_COUNT - 1);

    unsigned buckets[BUCKET_COUNT] = {};
    for (unsigned i = 0; i < ITERATIONS; ++i)
        ++buckets[dist (gen)];

    tap.expect (
        std::find_if (std::cbegin (buckets),
                      std::cend   (buckets),
                      [] (auto v) { return v < EXPECTED * 7 / 8; }) == std::cend (buckets),
        "bucket counts for {:s}", cruft::introspection::name::full<GeneratorT> ()
    );
}


///////////////////////////////////////////////////////////////////////////////
int
main (int,char**)
{
    cruft::TAP::logger tap;

    test_buckets<cruft::rand::splitmix64> (tap, 0x1234u);
    test_buckets<cruft::rand::xoshiro256plusplus> (tap, 0x1234u);
    test_buckets<cruft::rand::xorshift<uint32_t>> (tap, 0x1234u);
    test_buckets<cruft::rand::xorshift<uint64_t>> (tap, 0x1234u);
    test_buckets<cruft::rand::lcg_t> (tap, 0x1234u);
    test_buckets<cruft::rand::mwc64x> (tap, 0x1234u);
    test_buckets<cruft::rand::pcg_xsh_rr<>> (tap, 0x1234u);
    test_buckets<cruft::rand::system> (tap);
    test_buckets<cruft::rand::rdrand> (tap);

    return tap.status ();
}