Browse Source

More stuff moved, new bit array interface

Kajetan Johannes Hammerle 2 years ago
parent
commit
de755abdaf

+ 138 - 0
data/ArrayList.h

@@ -0,0 +1,138 @@
+#ifndef ARRAYLIST_H
+#define ARRAYLIST_H
+
+#include "utils/String.h"
+#include "utils/Utility.h"
+
+namespace Core {
+    template<typename T, int N>
+    class ArrayList final {
+        static_assert(N > 0, "ArrayList size must be positive");
+
+        struct alignas(T) Aligned {
+            char data[sizeof(T)];
+        };
+
+        Aligned data[static_cast<unsigned int>(N)];
+        int length;
+
+    public:
+        ArrayList() : length(0) {
+        }
+
+        ArrayList(const ArrayList& other) : ArrayList() {
+            copy(other);
+        }
+
+        ArrayList(ArrayList&& other) : ArrayList() {
+            move(Core::move(other));
+            other.clear();
+        }
+
+        ~ArrayList() {
+            clear();
+        }
+
+        ArrayList& operator=(const ArrayList& other) {
+            if(&other != this) {
+                clear();
+                copy(other);
+            }
+            return *this;
+        }
+
+        ArrayList& operator=(ArrayList&& other) {
+            if(&other != this) {
+                clear();
+                move(Core::move(other));
+                other.clear();
+            }
+            return *this;
+        }
+
+        T* begin() {
+            return reinterpret_cast<T*>(data);
+        }
+
+        T* end() {
+            return begin() + length;
+        }
+
+        const T* begin() const {
+            return reinterpret_cast<const T*>(data);
+        }
+
+        const T* end() const {
+            return begin() + length;
+        }
+
+        // returns true on error
+        template<typename... Args>
+        check_return bool add(Args&&... args) {
+            if(length >= N) {
+                return true;
+            }
+            new(begin() + length++) T(Core::forward<Args>(args)...);
+            return false;
+        }
+
+        T& operator[](int index) {
+            return begin()[index];
+        }
+
+        const T& operator[](int index) const {
+            return begin()[index];
+        }
+
+        int getLength() const {
+            return length;
+        }
+
+        void clear() {
+            for(int i = 0; i < length; i++) {
+                begin()[i].~T();
+            }
+            length = 0;
+        }
+
+        // returns true on error
+        check_return bool remove(int index) {
+            if(index < 0 || index >= length) {
+                return true;
+            }
+            length--;
+            if(index != length) {
+                begin()[index] = Core::move(begin()[length]);
+            }
+            begin()[length].~T();
+            return false;
+        }
+
+        void toString(String& s) const {
+            s.append("[");
+            for(int i = 0; i < length - 1; i++) {
+                s.append(begin()[i]);
+                s.append(", ");
+            }
+            if(length > 0) {
+                s.append(begin()[length - 1]);
+            }
+            s.append("]");
+        }
+
+    private:
+        void copy(const ArrayList& other) {
+            for(int i = 0; i < other.length; i++) {
+                (void)add(other[i]);
+            }
+        }
+
+        void move(ArrayList&& other) {
+            for(int i = 0; i < other.length; i++) {
+                (void)add(Core::move(other[i]));
+            }
+        }
+    };
+}
+
+#endif

+ 178 - 0
data/BitArray.cpp

@@ -0,0 +1,178 @@
+#include "data/BitArray.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "math/Math.h"
+#include "utils/Utility.h"
+
+static int roundUpDivide(int a, int b) {
+    if(a % b == 0) {
+        return a / b;
+    }
+    return a / b + 1;
+}
+
+static constexpr int INT_BITS = sizeof(int) * 8;
+static constexpr int DIVIDE_BITS = Core::Math::roundUpLog2(INT_BITS);
+
+static int readBits(const int* data, int index, int bits) {
+    int dataIndexA = (index * bits) >> DIVIDE_BITS;
+    int dataIndexB = ((index + 1) * bits) >> DIVIDE_BITS;
+    int shifts = (index * bits) & (INT_BITS - 1);
+    if(dataIndexA == dataIndexB) {
+        return (data[dataIndexA] >> shifts) & ((1 << bits) - 1);
+    }
+    int bitsInA = INT_BITS - shifts;
+    int r = (data[dataIndexA] >> shifts) & ((1 << bitsInA) - 1);
+    r |= (data[dataIndexB] & ((1 << (bits - bitsInA)) - 1)) << bitsInA;
+    return r;
+}
+
+static void setBits(int* data, int index, int bits, int value) {
+    int mask = (1 << bits) - 1;
+    value &= mask;
+    int dataIndexA = (index * bits) >> DIVIDE_BITS;
+    int dataIndexB = ((index + 1) * bits) >> DIVIDE_BITS;
+    int shifts = (index * bits) & (INT_BITS - 1);
+    data[dataIndexA] &= ~(mask << shifts);
+    data[dataIndexA] |= (value << shifts);
+    if(dataIndexA != dataIndexB) {
+        int leftBits = bits - (INT_BITS - shifts);
+        data[dataIndexB] &= ~((1 << leftBits) - 1);
+        data[dataIndexB] |= (value >> (INT_BITS - shifts));
+    }
+}
+
+static int getArrayLength(int length, int bits) {
+    return roundUpDivide(length * bits, sizeof(int) * 8);
+}
+
+Core::BitArray::BitArray() : length(0), bits(0), data(nullptr) {
+}
+
+check_return bool Core::BitArray::copyTo(BitArray& other) const {
+    if(other.resize(length, bits)) {
+        return true;
+    }
+    for(int i = 0; i < length; i++) {
+        other.set(i, get(i));
+    }
+    return false;
+}
+
+Core::BitArray::BitArray(BitArray&& other) : BitArray() {
+    swap(other);
+}
+
+Core::BitArray::~BitArray() {
+    delete[] data;
+}
+
+Core::BitArray& Core::BitArray::operator=(BitArray&& other) {
+    swap(other);
+    return *this;
+}
+
+Core::BitArray& Core::BitArray::set(int index, int value) {
+    if(data == nullptr || index < 0 || index >= length) {
+        return *this;
+    }
+    setBits(data, index, bits, value);
+    return *this;
+}
+
+int Core::BitArray::get(int index) const {
+    if(data == nullptr || index < 0 || index >= length) {
+        return 0;
+    }
+    return readBits(data, index, bits);
+}
+
+int Core::BitArray::getLength() const {
+    return length;
+}
+
+int Core::BitArray::getBits() const {
+    return bits;
+}
+
+int Core::BitArray::getInternalByteSize() const {
+    if(bits <= 0 || length <= 0) {
+        return 0;
+    }
+    return getArrayLength(length, bits) * static_cast<int>(sizeof(int));
+}
+
+int Core::BitArray::select(int index) const {
+    if(index <= 0) {
+        return -1;
+    }
+    int found = 0;
+    int end = getArrayLength(length, bits);
+    for(int i = 0; i < end; i++) {
+        int ones = Core::popCount(data[i]);
+        found += ones;
+        if(found >= index) {
+            found -= ones;
+            int a = i * 32 - 1;
+            int d = data[i];
+            while(found < index) {
+                found += d & 1;
+                d >>= 1;
+                a++;
+            }
+            return a;
+        }
+    }
+    return -1;
+}
+
+void Core::BitArray::fill(int value) {
+    for(int i = 0; i < length; i++) {
+        set(i, value);
+    }
+}
+
+check_return bool Core::BitArray::resize(int newLength, int newBits) {
+    if(newLength <= 0 || newBits <= 0) {
+        return true;
+    }
+    int arrayLength = getArrayLength(newLength, newBits);
+    int* newData = new int[arrayLength];
+    if(newData == nullptr) {
+        return true;
+    }
+    memset(newData, 0, static_cast<size_t>(arrayLength) * sizeof(int));
+
+    int end = Math::min(length, newLength);
+    for(int i = 0; i < end; i++) {
+        setBits(newData, i, newBits, get(i));
+    }
+    for(int i = end; i < newLength; i++) {
+        setBits(newData, i, newBits, 0);
+    }
+    delete[] data;
+    data = newData;
+    bits = newBits;
+    length = newLength;
+    return false;
+}
+
+void Core::BitArray::toString(String& s) const {
+    s.append("[");
+    for(int i = 0; i < length - 1; i++) {
+        s.append(get(i));
+        s.append(", ");
+    }
+    if(length > 0) {
+        s.append(get(length - 1));
+    }
+    s.append("]");
+}
+
+void Core::BitArray::swap(BitArray& other) {
+    Core::swap(length, other.length);
+    Core::swap(bits, other.bits);
+    Core::swap(data, other.data);
+}

+ 45 - 0
data/BitArray.h

@@ -0,0 +1,45 @@
+#ifndef CORE_BIT_ARRAY_H
+#define CORE_BIT_ARRAY_H
+
+#include "utils/Check.h"
+#include "utils/String.h"
+
+namespace Core {
+    class BitArray final {
+        int length;
+        int bits;
+        int* data;
+
+    public:
+        BitArray();
+        BitArray(const BitArray& other) = delete;
+        BitArray(BitArray&& other);
+        ~BitArray();
+        BitArray& operator=(const BitArray& other) = delete;
+        BitArray& operator=(BitArray&& other);
+
+        // returns true on error
+        check_return bool copyTo(BitArray& other) const;
+
+        BitArray& set(int index, int value);
+        int get(int index) const;
+
+        int getLength() const;
+        int getBits() const;
+        int getInternalByteSize() const;
+
+        int select(int index) const;
+
+        // returns true on error
+        check_return bool resize(int newLength, int newBits);
+
+        void fill(int value);
+
+        void toString(String& s) const;
+
+    private:
+        void swap(BitArray& other);
+    };
+}
+
+#endif

+ 8 - 0
math/Math.cpp

@@ -0,0 +1,8 @@
+#include "math/Math.h"
+
+#ifndef _GNU_SOURCE
+void sincosf(float a, float* s, float* c) {
+    *s = sinf(a);
+    *c = cosf(a);
+}
+#endif

+ 61 - 0
math/Math.h

@@ -0,0 +1,61 @@
+#ifndef CORE_MATH_H
+#define CORE_MATH_H
+
+#include <math.h>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#ifndef _GNU_SOURCE
+void sincosf(float a, float* s, float* c);
+#endif
+
+namespace Core::Math {
+    template<typename T>
+    T interpolate(const T& a, const T& b, float f) {
+        return a * (1.0f - f) + b * f;
+    }
+
+    constexpr int roundUpLog2(int i) {
+        if(i <= 0) {
+            return 0;
+        }
+        int c = 1;
+        while(((i - 1) >> c) > 0) {
+            c++;
+        }
+        return c;
+    }
+
+    template<typename T>
+    const T& min(const T& t) {
+        return t;
+    }
+
+    template<typename T, typename... Args>
+    const T& min(const T& t, Args&&... args) {
+        const T& o = min(args...);
+        return t < o ? t : o;
+    }
+
+    template<typename T>
+    const T& max(const T& t) {
+        return t;
+    }
+
+    template<typename T, typename... Args>
+    const T& max(const T& t, Args&&... args) {
+        const T& o = max(args...);
+        return o < t ? t : o;
+    }
+
+    template<typename T>
+    const T& clamp(const T& t, const T& borderA, const T& borderB) {
+        const T& low = min(borderA, borderB);
+        const T& high = max(borderA, borderB);
+        return max(low, min(high, t));
+    }
+}
+
+#endif

+ 4 - 0
meson.build

@@ -4,6 +4,7 @@ src = [
     'utils/String.cpp',
     'utils/Logger.cpp',
     'utils/Utility.cpp',
+    'data/BitArray.cpp',
 ]
 
 src_tests = [
@@ -12,6 +13,9 @@ src_tests = [
     'tests/ArrayTests.cpp',
     'tests/StringTests.cpp',
     'tests/UtilityTests.cpp',
+    'tests/ArrayListTests.cpp',
+    'tests/BitArrayTests.cpp',
+    'tests/MathTests.cpp',
 ]
 
 compiler = meson.get_compiler('cpp')

+ 6 - 0
test/Main.cpp

@@ -1,4 +1,7 @@
+#include "tests/ArrayListTests.h"
 #include "tests/ArrayTests.h"
+#include "tests/BitArrayTests.h"
+#include "tests/MathTests.h"
 #include "tests/StringTests.h"
 #include "tests/UtilityTests.h"
 
@@ -6,5 +9,8 @@ int main() {
     Core::ArrayTests::test();
     Core::StringTests::test();
     Core::UtilityTests::test();
+    Core::ArrayListTests::test();
+    Core::BitArrayTests::test();
+    Core::MathTests::test();
     return 0;
 }

+ 1 - 17
test/Test.cpp

@@ -37,20 +37,4 @@ void Core::Test::checkTrue(bool actual, const char* text) {
 
 void Core::Test::checkFalse(bool actual, const char* text) {
     checkEqual(false, actual, text);
-}
-
-void Core::Test::checkUnsigned8(u8 wanted, u8 actual, const char* text) {
-    checkEqual(wanted, actual, text);
-}
-
-void Core::Test::checkUnsigned16(u16 wanted, u16 actual, const char* text) {
-    checkEqual(wanted, actual, text);
-}
-
-void Core::Test::checkSigned8(i8 wanted, i8 actual, const char* text) {
-    checkEqual(wanted, actual, text);
-}
-
-void Core::Test::checkSigned16(i16 wanted, i16 actual, const char* text) {
-    checkEqual(wanted, actual, text);
-}
+}

+ 0 - 4
test/Test.h

@@ -30,10 +30,6 @@ namespace Core {
                         const char* text);
         void checkTrue(bool actual, const char* text);
         void checkFalse(bool actual, const char* text);
-        void checkUnsigned8(u8 wanted, u8 actual, const char* text);
-        void checkUnsigned16(u16 wanted, u16 actual, const char* text);
-        void checkSigned8(i8 wanted, i8 actual, const char* text);
-        void checkSigned16(i16 wanted, i16 actual, const char* text);
     };
 }
 

+ 166 - 0
tests/ArrayListTests.cpp

@@ -0,0 +1,166 @@
+#include "tests/ArrayListTests.h"
+
+#include "data/ArrayList.h"
+#include "test/Test.h"
+
+using IntList = Core::ArrayList<int, 20>;
+
+static void testAdd(Core::Test& test) {
+    IntList list;
+    test.checkFalse(list.add(5), "add works 1");
+
+    test.checkEqual(5, list[0], "contains added value");
+    test.checkEqual(1, list.getLength(), "sizes is increased by add");
+}
+
+static void testMultipleAdd(Core::Test& test) {
+    IntList list;
+    test.checkFalse(list.add(4), "add works 2");
+    test.checkFalse(list.add(3), "add works 3");
+    test.checkFalse(list.add(2), "add works 4");
+    test.checkEqual(4, list[0], "contains added value 1");
+    test.checkEqual(3, list[1], "contains added value 2");
+    test.checkEqual(2, list[2], "contains added value 3");
+    test.checkEqual(3, list.getLength(), "sizes is increased by add");
+}
+
+static void testAddReplace(Core::Test& test) {
+    IntList list;
+    test.checkFalse(list.add(5), "add works 5");
+    list[0] = 3;
+    test.checkEqual(3, list[0], "value is overwritten");
+}
+
+static void testClear(Core::Test& test) {
+    IntList list;
+    test.checkFalse(list.add(5), "add works 6");
+    test.checkFalse(list.add(4), "add works 7");
+    list.clear();
+    test.checkEqual(0, list.getLength(), "length is 0 after clear");
+}
+
+static void testOverflow(Core::Test& test) {
+    IntList list;
+    for(int i = 0; i < 20; i++) {
+        test.checkEqual(false, list.add(i),
+                        "add returns false without overflow");
+    }
+    for(int i = 0; i < 1000000; i++) {
+        test.checkEqual(true, list.add(i), "add returns true with overflow");
+    }
+    for(int i = 0; i < list.getLength(); i++) {
+        test.checkEqual(i, list[i], "still contains values after overflow");
+    }
+    test.checkEqual(true, true, "survives overflow");
+}
+
+static void testCopy(Core::Test& test) {
+    IntList list;
+    test.checkFalse(list.add(1), "add works 8");
+    test.checkFalse(list.add(2), "add works 9");
+    test.checkFalse(list.add(3), "add works 10");
+
+    IntList copy(list);
+    test.checkEqual(list.getLength(), copy.getLength(), "copy has same length");
+    for(int i = 0; i < copy.getLength() && i < list.getLength(); i++) {
+        test.checkEqual(list[i], copy[i], "copy has same values");
+    }
+}
+
+static void testCopyAssignment(Core::Test& test) {
+    IntList list;
+    test.checkFalse(list.add(1), "add works 11");
+    test.checkFalse(list.add(2), "add works 12");
+    test.checkFalse(list.add(3), "add works 13");
+
+    IntList copy;
+    copy = list;
+    test.checkEqual(list.getLength(), copy.getLength(),
+                    "copy assignment has same length");
+    for(int i = 0; i < copy.getLength() && i < list.getLength(); i++) {
+        test.checkEqual(list[i], copy[i], "copy assignment has same values");
+    }
+}
+
+static void testMove(Core::Test& test) {
+    IntList list;
+    test.checkFalse(list.add(1), "add works 14");
+    test.checkFalse(list.add(2), "add works 15");
+    test.checkFalse(list.add(3), "add works 16");
+
+    IntList move(Core::move(list));
+    test.checkEqual(0, list.getLength(), "moved has length 0");
+    test.checkEqual(3, move.getLength(), "moved passes length");
+    test.checkEqual(1, move[0], "moved passes values");
+    test.checkEqual(2, move[1], "moved passes values");
+    test.checkEqual(3, move[2], "moved passes values");
+}
+
+static void testMoveAssignment(Core::Test& test) {
+    IntList list;
+    test.checkFalse(list.add(1), "add works 17");
+    test.checkFalse(list.add(2), "add works 18");
+    test.checkFalse(list.add(3), "add works 19");
+
+    IntList move;
+    move = Core::move(list);
+    test.checkEqual(0, list.getLength(), "assignment moved has length 0");
+    test.checkEqual(3, move.getLength(), "assignment moved passes length");
+    test.checkEqual(1, move[0], "assignment moved passes values");
+    test.checkEqual(2, move[1], "assignment moved passes values");
+    test.checkEqual(3, move[2], "assignment moved passes values");
+}
+
+static void testToString1(Core::Test& test) {
+    IntList list;
+    test.checkFalse(list.add(1), "add works 20");
+    test.checkFalse(list.add(243), "add works 21");
+    test.checkFalse(list.add(-423), "add works 22");
+    test.checkEqual(Core::String("[1, 243, -423]"), Core::String(list),
+                    "to string 1");
+}
+
+static void testToString2(Core::Test& test) {
+    IntList list;
+    test.checkFalse(list.add(1), "add works 23");
+    test.checkEqual(Core::String("[1]"), Core::String(list), "to string 2");
+}
+
+static void testToString3(Core::Test& test) {
+    IntList list;
+    test.checkEqual(Core::String("[]"), Core::String(list), "to string 3");
+}
+
+static void testRemove(Core::Test& test) {
+    IntList list;
+    test.checkFalse(list.add(4), "add works 24");
+    test.checkFalse(list.add(3), "add works 25");
+    test.checkFalse(list.add(2), "add works 26");
+    test.checkFalse(list.remove(0), "remove works 1");
+    test.checkEqual(2, list[0], "remove 1");
+    test.checkEqual(3, list[1], "remove 2");
+    test.checkEqual(2, list.getLength(), "remove 3");
+    test.checkFalse(list.remove(1), "remove works 2");
+    test.checkEqual(2, list[0], "remove 4");
+    test.checkEqual(1, list.getLength(), "remove 5");
+    test.checkFalse(list.remove(0), "remove works 3");
+    test.checkEqual(0, list.getLength(), "remove 6");
+}
+
+void Core::ArrayListTests::test() {
+    Core::Test test("ArrayList");
+    testAdd(test);
+    testMultipleAdd(test);
+    testAddReplace(test);
+    testClear(test);
+    testOverflow(test);
+    testCopy(test);
+    testCopyAssignment(test);
+    testMove(test);
+    testMoveAssignment(test);
+    testToString1(test);
+    testToString2(test);
+    testToString3(test);
+    testRemove(test);
+    test.finalize();
+}

+ 8 - 0
tests/ArrayListTests.h

@@ -0,0 +1,8 @@
+#ifndef CORE_ARRAY_LIST_TESTS_H
+#define CORE_ARRAY_LIST_TESTS_H
+
+namespace Core::ArrayListTests {
+    void test();
+}
+
+#endif

+ 130 - 0
tests/BitArrayTests.cpp

@@ -0,0 +1,130 @@
+#include "tests/BitArrayTests.h"
+
+#include "data/BitArray.h"
+#include "test/Test.h"
+
+static void testSetRead(Core::Test& test) {
+    Core::BitArray bits;
+    test.checkFalse(bits.resize(4, 3), "resize works 1");
+    bits.set(0, 1).set(1, 2).set(2, 3).set(3, 4);
+    test.checkEqual(1, bits.get(0), "set and read correct value 1");
+    test.checkEqual(2, bits.get(1), "set and read correct value 2");
+    test.checkEqual(3, bits.get(2), "set and read correct value 3");
+    test.checkEqual(4, bits.get(3), "set and read correct value 4");
+}
+
+static void testOutOfBoundsSetRead(Core::Test& test) {
+    Core::BitArray bits;
+    bits.set(0, 1).set(1, 2).set(2, 3).set(3, 4);
+    test.checkEqual(0, bits.get(0), "set and read default value 1");
+    test.checkEqual(0, bits.get(1), "set and read default value 2");
+    test.checkEqual(0, bits.get(2), "set and read default value 3");
+    test.checkEqual(0, bits.get(3), "set and read default value 4");
+}
+
+static void testBigSetRead(Core::Test& test) {
+    Core::BitArray bits;
+    test.checkFalse(bits.resize(100, 13), "resize works 2");
+    for(int i = 0; i < bits.getLength(); i++) {
+        bits.set(i, i);
+    }
+    for(int i = 0; i < bits.getLength(); i++) {
+        test.checkEqual(i, bits.get(i),
+                        "set and read correct value over long array");
+    }
+}
+
+static void testRandomSetReadResize(Core::Test& test) {
+    const int length = 100;
+    int data[length];
+    Core::BitArray bits;
+    test.checkFalse(bits.resize(100, 13), "resize works 3");
+    int seed = 534;
+    for(int k = 0; k < 20; k++) {
+        for(int i = 0; i < bits.getLength(); i++) {
+            seed = seed * 636455 + 53453;
+            bits.set(i, seed & (0x1FFF));
+            data[i] = seed & (0x1FFF);
+        }
+    }
+    for(int i = 0; i < bits.getLength(); i++) {
+        test.checkEqual(data[i], bits.get(i),
+                        "set and read correct value with random input");
+    }
+    test.checkFalse(bits.resize(bits.getLength(), bits.getBits() + 1),
+                    "resize works 4");
+    test.checkEqual(14, bits.getBits(), "corrects bits after resize");
+    test.checkEqual(100, bits.getLength(), "correct length after resize");
+    for(int i = 0; i < bits.getLength(); i++) {
+        test.checkEqual(
+            data[i], bits.get(i),
+            "set and read correct value with random input after resize");
+    }
+}
+
+static void testReadOnly(Core::Test& test) {
+    Core::BitArray bits;
+    test.checkFalse(bits.resize(4, 3), "resize works 5");
+    bits.set(0, 1).set(1, 2).set(2, 3).set(3, 4);
+    Core::BitArray copy;
+    test.checkFalse(bits.copyTo(copy), "copy works");
+    const Core::BitArray bits2 = Core::move(copy);
+    test.checkEqual(1, bits2.get(0), "can read from const 1");
+    test.checkEqual(2, bits2.get(1), "can read from const 2");
+    test.checkEqual(3, bits2.get(2), "can read from const 3");
+    test.checkEqual(4, bits2.get(3), "can read from const 4");
+}
+
+static void testSelect(Core::Test& test) {
+    Core::BitArray bits;
+    test.checkFalse(bits.resize(90, 1), "resize works 6");
+    bits.fill(0);
+    bits.set(0, 1).set(5, 1).set(20, 1).set(31, 1);
+    bits.set(32, 1).set(33, 1).set(60, 1);
+    test.checkEqual(-1, bits.select(-1), "select -1 bit");
+    test.checkEqual(-1, bits.select(0), "select 0 bit");
+    test.checkEqual(0, bits.select(1), "select 1 bit");
+    test.checkEqual(5, bits.select(2), "select 2 bit");
+    test.checkEqual(20, bits.select(3), "select 3 bit");
+    test.checkEqual(31, bits.select(4), "select 4 bit");
+    test.checkEqual(32, bits.select(5), "select 5 bit");
+    test.checkEqual(33, bits.select(6), "select 6 bit");
+    test.checkEqual(60, bits.select(7), "select 7 bit");
+    test.checkEqual(-1, bits.select(8), "select 8 bit");
+}
+
+static void testToString1(Core::Test& test) {
+    Core::BitArray bits;
+    test.checkFalse(bits.resize(4, 3), "resize works 7");
+    bits.set(0, 1).set(1, 2).set(2, 3).set(3, 4);
+    test.checkEqual(Core::String("[1, 2, 3, 4]"), Core::String(bits),
+                    "bit array to string 1");
+}
+
+static void testToString2(Core::Test& test) {
+    Core::BitArray bits;
+    test.checkFalse(bits.resize(1, 3), "resize works 8");
+    bits.set(0, 1);
+    test.checkEqual(Core::String("[1]"), Core::String(bits),
+                    "bit array to string 2");
+}
+
+static void testToString3(Core::Test& test) {
+    Core::BitArray bits;
+    test.checkEqual(Core::String("[]"), Core::String(bits),
+                    "bit array to string 3");
+}
+
+void Core::BitArrayTests::test() {
+    Core::Test test("Core::BitArray");
+    testSetRead(test);
+    testOutOfBoundsSetRead(test);
+    testBigSetRead(test);
+    testRandomSetReadResize(test);
+    testReadOnly(test);
+    testSelect(test);
+    testToString1(test);
+    testToString2(test);
+    testToString3(test);
+    test.finalize();
+}

+ 8 - 0
tests/BitArrayTests.h

@@ -0,0 +1,8 @@
+#ifndef CORE_BIT_ARRAY_TESTS_H
+#define CORE_BIT_ARRAY_TESTS_H
+
+namespace Core::BitArrayTests {
+    void test();
+}
+
+#endif

+ 65 - 0
tests/MathTests.cpp

@@ -0,0 +1,65 @@
+#include "tests/MathTests.h"
+
+#include "math/Math.h"
+#include "test/Test.h"
+
+constexpr float eps = 0.0001f;
+
+static void testInterpolate(Core::Test& test) {
+    test.checkFloat(7.5f, Core::Math::interpolate(5.0f, 10.0f, 0.5f), eps,
+                    "interpolate 1");
+    test.checkFloat(-2.0, Core::Math::interpolate(-10.0f, 10.0f, 0.4f), eps,
+                    "interpolate 2");
+    test.checkFloat(10.0f, Core::Math::interpolate(-3.0f, 10.0f, 1.0f), eps,
+                    "interpolate 3");
+    test.checkFloat(7.0f, Core::Math::interpolate(7.0f, 10.0f, 0.0f), eps,
+                    "interpolate 4");
+    test.checkFloat(6.0f, Core::Math::interpolate(0.0f, 10.0f, 0.6f), eps,
+                    "interpolate 5");
+}
+
+static void testRoundUpLog2(Core::Test& test) {
+    test.checkEqual(0, Core::Math::roundUpLog2(-5), "round up log2 1");
+    test.checkEqual(0, Core::Math::roundUpLog2(0), "round up log2 2");
+    test.checkEqual(1, Core::Math::roundUpLog2(1), "round up log2 3");
+    test.checkEqual(1, Core::Math::roundUpLog2(2), "round up log2 4");
+    test.checkEqual(2, Core::Math::roundUpLog2(3), "round up log2 5");
+    test.checkEqual(2, Core::Math::roundUpLog2(4), "round up log2 6");
+    test.checkEqual(3, Core::Math::roundUpLog2(5), "round up log2 7");
+    test.checkEqual(4, Core::Math::roundUpLog2(10), "round up log2 8");
+    test.checkEqual(5, Core::Math::roundUpLog2(20), "round up log2 9");
+    test.checkEqual(16, Core::Math::roundUpLog2(35345), "round up log2 10");
+    test.checkEqual(31, Core::Math::roundUpLog2(0x7FFFFFFF),
+                    "round up log2 11");
+}
+
+static void testMin(Core::Test& test) {
+    test.checkEqual(-5, Core::Math::min(-5), "min 1");
+    test.checkEqual(-10, Core::Math::min(-5, 4, 3, 2, -10), "min 2");
+    test.checkEqual(4, Core::Math::min(5, 20, 4, 30), "min 3");
+}
+
+static void testMax(Core::Test& test) {
+    test.checkEqual(-5, Core::Math::max(-5), "max 1");
+    test.checkEqual(4, Core::Math::max(-5, 4, 3, 2, -10), "max 2");
+    test.checkEqual(30, Core::Math::max(5, 20, 4, 30), "max 3");
+}
+
+static void testClamp(Core::Test& test) {
+    test.checkEqual(5, Core::Math::clamp(1, 5, 10), "clamp 1");
+    test.checkEqual(7, Core::Math::clamp(7, 5, 10), "clamp 2");
+    test.checkEqual(10, Core::Math::clamp(20, 5, 10), "clamp 3");
+    test.checkEqual(5, Core::Math::clamp(1, 10, 5), "clamp 4");
+    test.checkEqual(7, Core::Math::clamp(7, 10, 5), "clamp 5");
+    test.checkEqual(10, Core::Math::clamp(20, 10, 5), "clamp 6");
+}
+
+void Core::MathTests::test() {
+    Core::Test test("Math");
+    testInterpolate(test);
+    testRoundUpLog2(test);
+    testMin(test);
+    testMax(test);
+    testClamp(test);
+    test.finalize();
+}

+ 8 - 0
tests/MathTests.h

@@ -0,0 +1,8 @@
+#ifndef CORE_MATH_TESTS_H
+#define CORE_MATH_TESTS_H
+
+namespace Core::MathTests {
+    void test();
+}
+
+#endif

+ 4 - 4
tests/StringTests.cpp

@@ -34,10 +34,10 @@ static void testStringAppend(Core::Test& test) {
 
 static void testCharacters(Core::Test& test) {
     Core::String s("test");
-    test.checkEqual(static_cast<u32>('t'), s[0], "character read 1");
-    test.checkEqual(static_cast<u32>('e'), s[1], "character read 2");
-    test.checkEqual(static_cast<u32>('s'), s[2], "character read 3");
-    test.checkEqual(static_cast<u32>('t'), s[3], "character read 4");
+    test.checkEqual<u32>('t', s[0], "character read 1");
+    test.checkEqual<u32>('e', s[1], "character read 2");
+    test.checkEqual<u32>('s', s[2], "character read 3");
+    test.checkEqual<u32>('t', s[3], "character read 4");
 }
 
 static void testLength(Core::Test& test) {

+ 6 - 6
utils/Utility.cpp

@@ -3,24 +3,24 @@
 /*
 void* operator new(size_t bytes) noexcept {
     return malloc(bytes);
-}
+}*/
 
 void* operator new[](size_t bytes) noexcept {
     return malloc(bytes);
 }
 
-void operator delete(void* p) noexcept {
+/*void operator delete(void* p) noexcept {
     free(p);
-}
+}*/
 
 void operator delete[](void* p) noexcept {
     free(p);
 }
 
-void operator delete(void* p, size_t bytes) noexcept {
+/*void operator delete(void* p, size_t bytes) noexcept {
     (void)bytes;
     free(p);
-}
+}*/
 
 void operator delete[](void* p, size_t bytes) noexcept {
     (void)bytes;
@@ -32,7 +32,7 @@ void* operator new(size_t bytes, void* p) noexcept {
     return p;
 }
 
-void* operator new[](size_t bytes, void* p) noexcept {
+/*void* operator new[](size_t bytes, void* p) noexcept {
     (void)bytes;
     return p;
 }*/

+ 6 - 6
utils/Utility.h

@@ -1,7 +1,7 @@
 #ifndef CORE_UTILITY_H
 #define CORE_UTILITY_H
 
-// #include <stdlib.h>
+#include <stdlib.h>
 
 namespace Core {
     namespace Internal {
@@ -32,7 +32,7 @@ namespace Core {
     }
 
     template<typename T>
-    using RemoveReference = typename Internal::RemoveReferenceBase<T>::t;
+    using RemoveReference = Internal::RemoveReferenceBase<T>::t;
 
     template<typename T, typename U>
     constexpr bool IsSame = Internal::IsSameBase<T, U>::value;
@@ -72,12 +72,12 @@ namespace Core {
 }
 
 // void* operator new(size_t bytes) noexcept;
-// void* operator new[](size_t bytes) noexcept;
+void* operator new[](size_t bytes) noexcept;
 // void operator delete(void* p) noexcept;
-// void operator delete[](void* p) noexcept;
+void operator delete[](void* p) noexcept;
 // void operator delete(void* p, size_t bytes) noexcept;
-// void operator delete[](void* p, size_t bytes) noexcept;
-// void* operator new(size_t bytes, void* p) noexcept;
+void operator delete[](void* p, size_t bytes) noexcept;
+void* operator new(size_t bytes, void* p) noexcept;
 // void* operator new[](size_t bytes, void* p) noexcept;
 
 #endif