#ifdef TESTS_BUILD
#define CATCH_CONFIG_MAIN

#include "../extern/catch.hpp"
#include "../src/Random.hpp"

TEST_CASE("Random ints", "[Utilities]") {
    auto rand = ArbUt::Random(10);
    CHECK(rand.Get() == 1234817989);
    CHECK(rand.Get() == 1171957426);
    CHECK(rand.Get() == 275100647);
    CHECK(rand.Get() == 1033685688);
    CHECK(rand.Get() == 180895192);
    CHECK(rand.Get() == 135557292);
    CHECK(rand.Get() == 716914271);
    CHECK(rand.Get() == 1012211222);
    CHECK(rand.Get() == -2109244634);
    CHECK(rand.Get() == -1647742638);
}

TEST_CASE("Random ints with limit", "[Utilities]") {
    auto rand = ArbUt::Random(10);
    CHECK(rand.Get(10) == 2);
    CHECK(rand.Get(10) == 2);
    CHECK(rand.Get(10) == 0);
    CHECK(rand.Get(10) == 2);
    CHECK(rand.Get(10) == 0);
    CHECK(rand.Get(10) == 0);
    CHECK(rand.Get(10) == 1);
    CHECK(rand.Get(10) == 2);
    CHECK(rand.Get(10) == 5);
    CHECK(rand.Get(10) == 6);

    CHECK(rand.Get(2) == 0);
    CHECK(rand.Get(2) == 0);
    CHECK(rand.Get(2) == 1);
    CHECK(rand.Get(2) == 1);
    CHECK(rand.Get(2) == 0);
    CHECK(rand.Get(2) == 0);
    CHECK(rand.Get(2) == 0);
    CHECK(rand.Get(2) == 1);
    CHECK(rand.Get(2) == 0);
}

TEST_CASE("Random ints with upper and bottom", "[Utilities]") {
    auto rand = ArbUt::Random(10);
    CHECK(rand.Get(10, 30) == 15);
    CHECK(rand.Get(10, 30) == 15);
    CHECK(rand.Get(10, 30) == 11);
    CHECK(rand.Get(10, 30) == 14);
    CHECK(rand.Get(10, 30) == 10);
    CHECK(rand.Get(10, 30) == 10);
    CHECK(rand.Get(10, 30) == 13);
    CHECK(rand.Get(10, 30) == 14);
    CHECK(rand.Get(10, 30) == 20);
    CHECK(rand.Get(10, 30) == 22);
}

TEST_CASE("Random distribution (max 0, min 1)", "[Utilities]") {
    auto rand = ArbUt::Random(10);
    const int size = 100000;
    int arr[size];
    for (size_t i = 0; i < size; i++) {
        arr[i] = rand.Get(0, 1);
    }
    for (size_t i = 0; i < size; i++) {
        if (arr[i] != 0)
            FAIL("We expected a value of 0 here, but got a " + std::to_string(arr[i]));
    }
}

TEST_CASE("Random distribution (max 0, min 2)", "[Utilities]") {
    auto rand = ArbUt::Random(10);
    const int size = 100000;
    int arr[size];
    for (size_t i = 0; i < size; i++) {
        arr[i] = rand.Get(0, 2);
    }
    auto numZeros = 0;
    auto numOnes = 0;
    for (size_t i = 0; i < size; i++) {
        if (arr[i] != 0 && arr[i] != 1)
            FAIL("We expected a value of 0 or 1 here, but got a " + std::to_string(arr[i]));
        if (arr[i] == 0)
            numZeros++;
        else
            numOnes++;
    }
    auto div = static_cast<float>(numZeros) / static_cast<float>(numOnes);
    INFO("Distribution: " << numZeros << "/" << numOnes);
    CHECK(Approx(div).margin(0.01) == 1);
}

TEST_CASE("Random distribution (max 0, min 3)", "[Utilities]") {
    auto rand = ArbUt::Random(10);
    const size_t size = 100000;
    int arr[size];
    for (size_t i = 0; i < size; i++) {
        arr[i] = rand.Get(0, 3);
    }
    auto numZeros = 0;
    auto numOnes = 0;
    auto numTwos = 0;
    for (size_t i = 0; i < size; i++) {
        if (arr[i] != 0 && arr[i] != 1 && arr[i] != 2)
            FAIL("We expected a value between 0 and 2 here, but got a " + std::to_string(arr[i]));
        if (arr[i] == 0)
            numZeros++;
        else if (arr[i] == 1)
            numOnes++;
        else
            numTwos++;
    }
    INFO("Distribution: " << numZeros << "/" << numOnes << "/" << numTwos);
    CHECK(Approx(static_cast<float>(numZeros) / static_cast<float>(numOnes)).margin(0.01) == 1);
    CHECK(Approx(static_cast<float>(numZeros) / static_cast<float>(numTwos)).margin(0.01) == 1);
    CHECK(Approx(static_cast<float>(numOnes) / static_cast<float>(numTwos)).margin(0.01) == 1);
}

#endif