#include "../Tests.hpp"
#include "core/data/Array.hpp"
#include "core/utils/Random.hpp"

static void testAverage(bool light) {
    int limit = light ? 100000 : 1000000;
    Core::Random r(553);
    float sum = 0;
    for(int i = 0; i < limit; i++) {
        sum += static_cast<float>(r.next(2, 10));
    }
    sum /= static_cast<float>(limit);
    CORE_TEST_FLOAT(6.0f, sum, 0.01f);
}

static void testCoin(bool light) {
    int limit = light ? 100000 : 1000000;
    Core::Random r(553);
    Core::Array<int, 2> c(0);
    for(int i = 0; i < limit; i++) {
        c[r.nextBool()]++;
    }
    CORE_TEST_FLOAT(0.0f,
                    static_cast<float>(c[0] - c[1]) / static_cast<float>(limit),
                    0.003f);
}

static void testDistribution(bool light) {
    int limit = light ? 100000 : 1000000;
    Core::Random r(553);
    Core::Array<int, 102> c(0);
    for(int i = 0; i < limit; i++) {
        c[r.next(1, c.getLength() - 2)]++;
    }
    CORE_TEST_EQUAL(0, c[0]);
    CORE_TEST_EQUAL(0, c[c.getLength() - 1]);
    for(int i = 1; i < c.getLength() - 1; i++) {
        CORE_TEST_FLOAT(0.01f,
                        static_cast<float>(c[i]) / static_cast<float>(limit),
                        0.001f);
    }
}

static void testFloatAverage(bool light) {
    int limit = light ? 100000 : 1000000;
    Core::Random r(553);
    float sum = 0;
    for(int i = 0; i < limit; i++) {
        sum += r.nextFloat();
    }
    sum /= static_cast<float>(limit);
    CORE_TEST_FLOAT(0.5f, sum, 0.001f);
}

static void testFloatCoin(bool light) {
    int limit = light ? 100000 : 1000000;
    Core::Random r(553);
    Core::Array<int, 2> c(0);
    for(int i = 0; i < limit; i++) {
        c[r.nextFloat() < 0.5f]++;
    }
    CORE_TEST_FLOAT(0.0f,
                    static_cast<float>(c[0] - c[1]) / static_cast<float>(limit),
                    0.003f);
}

static void testFloatDistribution(bool light) {
    int limit = light ? 100000 : 1000000;
    Core::Random r(553);
    Core::Array<int, 102> c(0);
    for(int i = 0; i < limit; i++) {
        c[static_cast<int>(r.nextFloat(1.0f, c.getLength() - 1))]++;
    }
    CORE_TEST_EQUAL(0, c[0]);
    CORE_TEST_EQUAL(0, c[c.getLength() - 1]);
    for(int i = 1; i < c.getLength() - 1; i++) {
        CORE_TEST_FLOAT(0.01f,
                        static_cast<float>(c[i]) / static_cast<float>(limit),
                        0.003f);
    }
}

void Core::testRandom(bool light) {
    testAverage(light);
    testCoin(light);
    testDistribution(light);
    testFloatAverage(light);
    testFloatCoin(light);
    testFloatDistribution(light);
}