#include "random.h"

#include <library/cpp/unittest/registar.h>

#include <util/generic/ylimits.h>

template <class T>
static inline void AssertRange(T v, T r1, T r2) {
    UNIT_ASSERT(v >= r1);
    UNIT_ASSERT(v < r2);
}

Y_UNIT_TEST_SUITE(TRandomNumberTest) {
    template <typename T>
    void TestAll(T n) {
        for (T i = 0; i < n; ++i) {
            while (RandomNumber<T>(n) != i) {
            }
        }
    }

    template <typename T>
    void TestSome(T n) {
        for (int i = 0; i < 100; ++i) {
            UNIT_ASSERT(RandomNumber<T>(n) < n);
        }
    }

    template <typename T>
    void TestType() {
        TestAll<T>(1);
        TestAll<T>(2);
        TestAll<T>(3);
        TestAll<T>(4);
        TestAll<T>(5);
        TestAll<T>(6);
        TestAll<T>(9);
        TestAll<T>(15);
        TestAll<T>(16);
        TestSome<T>(Max<T>());
        TestSome<T>(Max<T>() - 1);
        TestSome<T>(Max<T>() - 2);
        TestSome<T>(Max<T>() - 3);
        TestSome<T>(Max<T>() - 4);
        TestSome<T>(Max<T>() - 5);
        TestSome<T>(Max<T>() - 7);
        TestSome<T>(Max<T>() - 8);
        TestSome<T>(Max<T>() - 2222);
        TestSome<T>(Max<T>() - 22222);
    }

    Y_UNIT_TEST(TestWithLimit) {
        TestType<unsigned short>();
        TestType<unsigned int>();
        TestType<unsigned long>();
        TestType<unsigned long long>();
    }

    Y_UNIT_TEST(TestRandomNumberFloat) {
        for (size_t i = 0; i < 1000; ++i) {
            AssertRange<float>(RandomNumber<float>(), 0.0, 1.0);
        }
    }

    Y_UNIT_TEST(TestRandomNumberDouble) {
        for (size_t i = 0; i < 1000; ++i) {
            AssertRange<double>(RandomNumber<double>(), 0.0, 1.0);
        }
    }

    Y_UNIT_TEST(TestRandomNumberLongDouble) {
        for (size_t i = 0; i < 1000; ++i) {
            AssertRange<long double>(RandomNumber<long double>(), 0.0, 1.0);
        }
    }

    Y_UNIT_TEST(TestBoolean) {
        while (RandomNumber<bool>()) {
        }
        while (!RandomNumber<bool>()) {
        }
    }

    Y_UNIT_TEST(TestResetSeed) {
        SetRandomSeed(42);
        for (const ui32 el : {
                102, 179, 92, 14, 106, 71, 188, 20, 102, 121, 210, 214, 74, 202, 87, 116,
                99, 103, 151, 130, 149, 52, 1, 87, 235, 157, 37, 129, 191, 187, 20, 160,
                203, 57, 21, 252, 235, 88, 48, 218, 58, 254, 169, 255, 219, 187, 207, 14,
                189, 189, 174, 189, 50, 107, 54, 243, 63, 248, 130, 228, 50, 134, 20, 72, }) {
            UNIT_ASSERT_EQUAL(RandomNumber<ui32>(1 << 8), el);
        }
    }
}
