#include "tests/MathTests.h"
#include "math/Math.h"
#include "tests/Test.h"

const float eps = 0.0001f;

static void testInterpolate(Test& test) {
    test.checkFloat(7.5f, Math::interpolate(5.0f, 10.0f, 0.5f), eps,
                    "interpolate 1");
    test.checkFloat(-2.0, Math::interpolate(-10.0f, 10.0f, 0.4f), eps,
                    "interpolate 2");
    test.checkFloat(10.0f, Math::interpolate(-3.0f, 10.0f, 1.0f), eps,
                    "interpolate 3");
    test.checkFloat(7.0f, Math::interpolate(7.0f, 10.0f, 0.0f), eps,
                    "interpolate 4");
    test.checkFloat(6.0f, Math::interpolate(0.0f, 10.0f, 0.6f), eps,
                    "interpolate 5");
}

static void testPopCount(Test& test) {
    test.checkEqual(4, Math::popCount(0xF), "pop count 1");
    test.checkEqual(0, Math::popCount(0x0), "pop count 2");
    test.checkEqual(2, Math::popCount(0x6), "pop count 3");
    test.checkEqual(7, Math::popCount(0x7F), "pop count 4");
    test.checkEqual(3, Math::popCount(0x2A), "pop count 5");
    test.checkEqual(32, Math::popCount(0xFFFFFFFF), "pop count 6");
    test.checkEqual(64, Math::popCount(0xFFFFFFFFFFFFFFFFL), "pop count 7");
    test.checkEqual(44, Math::popCount(0xFFFF0FFFFFFF), "pop count 8");
    test.checkEqual(32, Math::popCount(-1), "pop count 9");
}

static void testRoundUpLog2(Test& test) {
    test.checkEqual(0, Math::roundUpLog2(-5), "round up log2 1");
    test.checkEqual(0, Math::roundUpLog2(0), "round up log2 2");
    test.checkEqual(1, Math::roundUpLog2(1), "round up log2 3");
    test.checkEqual(1, Math::roundUpLog2(2), "round up log2 4");
    test.checkEqual(2, Math::roundUpLog2(3), "round up log2 5");
    test.checkEqual(2, Math::roundUpLog2(4), "round up log2 6");
    test.checkEqual(3, Math::roundUpLog2(5), "round up log2 7");
    test.checkEqual(4, Math::roundUpLog2(10), "round up log2 8");
    test.checkEqual(5, Math::roundUpLog2(20), "round up log2 9");
    test.checkEqual(16, Math::roundUpLog2(35345), "round up log2 10");
    test.checkEqual(31, Math::roundUpLog2(0x7FFFFFFF), "round up log2 11");
}

static void testMin(Test& test) {
    test.checkEqual(-5, Math::min(-5), "min 1");
    test.checkEqual(-10, Math::min(-5, 4, 3, 2, -10), "min 2");
    test.checkEqual(4, Math::min(5, 20, 4, 30), "min 3");
}

static void testMax(Test& test) {
    test.checkEqual(-5, Math::max(-5), "max 1");
    test.checkEqual(4, Math::max(-5, 4, 3, 2, -10), "max 2");
    test.checkEqual(30, Math::max(5, 20, 4, 30), "max 3");
}

static void testClamp(Test& test) {
    test.checkEqual(5, Math::clamp(1, 5, 10), "clamp 1");
    test.checkEqual(7, Math::clamp(7, 5, 10), "clamp 2");
    test.checkEqual(10, Math::clamp(20, 5, 10), "clamp 3");
    test.checkEqual(5, Math::clamp(1, 10, 5), "clamp 4");
    test.checkEqual(7, Math::clamp(7, 10, 5), "clamp 5");
    test.checkEqual(10, Math::clamp(20, 10, 5), "clamp 6");
}

void MathTests::test() {
    Test test("Math");
    testInterpolate(test);
    testPopCount(test);
    testRoundUpLog2(test);
    testMin(test);
    testMax(test);
    testClamp(test);
    test.finalize();
}