#include "../Tests.h"
#include "core/BitArray.h"

static void testSetRead() {
    BitArray bits;
    initBitArray(&bits, 4, 3);
    setBits(&bits, 0, 1);
    setBits(&bits, 1, 2);
    setBits(&bits, 2, 3);
    setBits(&bits, 3, 4);
    TEST_U64(1, getBits(&bits, 0));
    TEST_U64(2, getBits(&bits, 1));
    TEST_U64(3, getBits(&bits, 2));
    TEST_U64(4, getBits(&bits, 3));
    destroyBitArray(&bits);
}

static void testBigSetRead() {
    BitArray bits;
    initBitArray(&bits, 100, 13);
    for(size_t i = 0; i < bits.length; i++) {
        setBits(&bits, i, i);
    }
    for(size_t i = 0; i < bits.length; i++) {
        TEST_U64(i, getBits(&bits, i));
    }
    destroyBitArray(&bits);
}

static void testRandomSetReadResize() {
    u64 data[100];
    BitArray bits;
    initBitArray(&bits, 100, 13);
    u64 seed = 534;
    for(int k = 0; k < 20; k++) {
        for(u64 i = 0; i < bits.length; i++) {
            seed = seed * 636'455 + 53'453;
            setBits(&bits, i, seed);
            data[i] = seed & 0x1FFF;
        }
    }
    for(size_t i = 0; i < bits.length; i++) {
        TEST_U64(data[i], getBits(&bits, i));
    }
    setBitLength(&bits, bits.length, bits.bits + 1u);
    TEST_U64(14, bits.bits);
    TEST_U64(100, bits.length);
    for(size_t i = 0; i < bits.length; i++) {
        TEST_U64(data[i], getBits(&bits, i));
    }
    destroyBitArray(&bits);
}

static void testSelect() {
    BitArray bits;
    initBitArray(&bits, 90, 1);
    setAllBits(&bits, 0);
    setBits(&bits, 0, 1);
    setBits(&bits, 5, 1);
    setBits(&bits, 20, 1);
    setBits(&bits, 31, 1);
    setBits(&bits, 32, 1);
    setBits(&bits, 33, 1);
    setBits(&bits, 60, 1);
    TEST_I64(-1, selectBits(&bits, 0));
    TEST_I64(0, selectBits(&bits, 1));
    TEST_I64(5, selectBits(&bits, 2));
    TEST_I64(20, selectBits(&bits, 3));
    TEST_I64(31, selectBits(&bits, 4));
    TEST_I64(32, selectBits(&bits, 5));
    TEST_I64(33, selectBits(&bits, 6));
    TEST_I64(60, selectBits(&bits, 7));
    TEST_I64(-1, selectBits(&bits, 8));
    destroyBitArray(&bits);
}

static void testToString1() {
    BitArray bits;
    initBitArray(&bits, 4, 3);
    setBits(&bits, 0, 1);
    setBits(&bits, 1, 2);
    setBits(&bits, 2, 3);
    setBits(&bits, 3, 4);
    char buffer[128];
    size_t n = toStringBitArray(&bits, buffer, sizeof(buffer));
    TEST_SIZE(12, n);
    TEST_STRING("[1, 2, 3, 4]", buffer);
    destroyBitArray(&bits);
}

static void testToString2() {
    BitArray bits;
    initBitArray(&bits, 1, 3);
    setBits(&bits, 0, 1);
    char buffer[128];
    size_t n = toStringBitArray(&bits, buffer, sizeof(buffer));
    TEST_SIZE(3, n);
    TEST_STRING("[1]", buffer);
    destroyBitArray(&bits);
}

static void testToString3() {
    BitArray bits;
    initBitArray(&bits, 0, 0);
    char buffer[128];
    size_t n = toStringBitArray(&bits, buffer, sizeof(buffer));
    TEST_SIZE(2, n);
    TEST_STRING("[]", buffer);
    destroyBitArray(&bits);
}

static void testResizeExact() {
    BitArray bits;
    // the size in bytes matches the internal storage type
    size_t elements = sizeof(u64);
    initBitArray(&bits, elements, 8);
    for(size_t i = 0; i < elements; i++) {
        setBits(&bits, i, i);
    }
    for(size_t i = 0; i < elements; i++) {
        TEST_U64(i, getBits(&bits, i));
    }
    destroyBitArray(&bits);
}

static void testInvalidArgument() {
    BitArray bits;
    initBitArray(&bits, 0, 0);
    setBitLength(&bits, 0, 5);
    TEST_SIZE(0, bits.length);
    TEST_SIZE(0, bits.bits);
    setBitLength(&bits, 5, 0);
    TEST_SIZE(0, bits.length);
    TEST_SIZE(0, bits.bits);
    setBitLength(&bits, 0, 0);
    TEST_SIZE(0, bits.length);
    TEST_SIZE(0, bits.bits);
    setBitLength(&bits, 1, 65);
    TEST_SIZE(1, bits.length);
    TEST_SIZE(64, bits.bits);
    setBitLength(&bits, 5, 68);
    TEST_SIZE(5, bits.length);
    TEST_SIZE(64, bits.bits);
    destroyBitArray(&bits);
}

void testBitArray() {
    testSetRead();
    testBigSetRead();
    testRandomSetReadResize();
    testSelect();
    testToString1();
    testToString2();
    testToString3();
    testResizeExact();
    testInvalidArgument();
}