Kajetan Johannes Hammerle 2 месяцев назад
Родитель
Сommit
58381fc6b3

+ 4 - 4
CMakeLists.txt

@@ -9,8 +9,8 @@ set(SRC
     #"src/Buffer.cpp"
     #"src/Components.cpp"
     "src/File.cpp"
-    #"src/Frustum.cpp"
-    #"src/HashMap.cpp"
+    "src/Frustum.cpp"
+    "src/HashMap.cpp"
     "src/Logger.cpp"
     "src/Matrix.cpp"
     "src/Plane.cpp"
@@ -35,8 +35,8 @@ set(SRC_TESTS
     #"test/modules/BufferTests.cpp"
     #"test/modules/ComponentsTests.cpp"
     "test/modules/FileTests.cpp"
-    #"test/modules/FrustumTests.cpp"
-    #"test/modules/HashMapTests.cpp"
+    "test/modules/FrustumTests.cpp"
+    "test/modules/HashMapTests.cpp"
     #"test/modules/ListTests.cpp"
     "test/modules/MatrixTests.cpp"
     "test/modules/PlaneTests.cpp"

+ 21 - 16
include/core/Frustum.hpp

@@ -1,23 +1,28 @@
-#ifndef CORE_FRUSTUM_H
-#define CORE_FRUSTUM_H
+#ifndef CORE_FRUSTUM_HPP
+#define CORE_FRUSTUM_HPP
 
+#include "core/Array.hpp"
 #include "core/Matrix.hpp"
 #include "core/Plane.hpp"
 
-typedef struct {
-    Matrix projection;
-    Plane planes[6];
-    float tan;
-    float nearClip;
-    float farClip;
-} Frustum;
+namespace Core {
+    class Frustum final {
+        Matrix projection;
+        Array<Plane, 6> planes;
+        float tan;
+        float near;
+        float far;
 
-void initFrustum(Frustum* f, float fieldOfView, float nearClip, float farClip);
-const Matrix* updateProjection(Frustum* f, const IntVector2* size);
-void updateFrustumPlanes(
-    Frustum* f, const Vector3* pos, const Vector3* right, const Vector3* up,
-    const Vector3* front, const IntVector2* size);
-bool isInsideFrustum(const Frustum* f, const Vector3* pos);
-bool isInsideFrustumRadius(const Frustum* f, const Vector3* pos, float radius);
+    public:
+        Frustum(float fieldOfView, float nearClip, float farClip);
+        const Matrix& updateProjection(const IntVector2& size);
+        void updatePlanes(
+            const Vector3& pos, const Vector3& right, const Vector3& up,
+            const Vector3& front, const IntVector2& size);
+
+        bool isInside(const Vector3& pos) const;
+        bool isInside(const Vector3& pos, float radius) const;
+    };
+}
 
 #endif

+ 0 - 237
include/core/HashMap.hpp

@@ -1,237 +0,0 @@
-#ifndef CORE_HASHMAP_H
-#define CORE_HASHMAP_H
-
-#include "core/ToString.hpp"
-#include "core/Types.hpp"
-#include "core/Utility.hpp"
-
-size_t hashString(const char* key);
-size_t roundUp2(size_t n);
-
-#define isInvalidKeyInt(key) ((key) == 0)
-#define equalInt(a, b) ((a) == (b))
-#define hashInt(key) ((size_t)key)
-#define isInvalidKeySize(key) ((key) == 0)
-#define equalSize(a, b) ((a) == (b))
-#define hashSize(key) (key)
-
-#define HASHMAP(K, V, N)                                                       \
-    typedef struct {                                                           \
-        K* keys;                                                               \
-        V* values;                                                             \
-        size_t capacity;                                                       \
-        size_t entries;                                                        \
-    } HashMap##N;                                                              \
-                                                                               \
-    void initHashMap##N(HashMap##N* m);                                        \
-    void destroyHashMap##N(HashMap##N* m);                                     \
-    void rehashHashMap##N(HashMap##N* m, size_t minCapacity);                  \
-    V* putHashMapKey##N(HashMap##N* m, K key);                                 \
-    V* searchHashMapKey##N(const HashMap##N* m, K key);                        \
-    void clearHashMap##N(HashMap##N* m);                                       \
-    bool removeHashMapKey##N(HashMap##N* m, K key);                            \
-                                                                               \
-    typedef struct {                                                           \
-        const K* key;                                                          \
-        V* value;                                                              \
-    } HashMapNode##N;                                                          \
-                                                                               \
-    typedef struct {                                                           \
-        K* key;                                                                \
-        K* endKey;                                                             \
-        V* value;                                                              \
-        V* endValue;                                                           \
-        const HashMap##N* map;                                                 \
-        HashMapNode##N node;                                                   \
-    } HashMapIterator##N;                                                      \
-                                                                               \
-    void initHashMapIterator##N(HashMapIterator##N* mi, const HashMap##N* m);  \
-    bool hasNextHashMapNode##N(HashMapIterator##N* mi);                        \
-    HashMapNode##N* nextHashMapNode##N(HashMapIterator##N* mi);                \
-    typedef size_t (*ToStringKey##N)(                                          \
-        K const* const data, char* buffer, size_t n);                          \
-    typedef size_t (*ToStringValue##N)(                                        \
-        V const* const data, char* buffer, size_t n);                          \
-    size_t toStringHashMap##N(                                                 \
-        const HashMap##N* m, char* buffer, size_t n, ToStringKey##N keyString, \
-        ToStringValue##N valueString);
-
-#define HASHMAP_SOURCE(K, V, N)                                                \
-    static size_t searchSlot##N(HashMap##N* m, K key) {                        \
-        size_t rehashFactor = 2;                                               \
-        while(true) {                                                          \
-            rehashHashMap##N(m, m->entries* rehashFactor + 1);                 \
-            if(isInvalidKey##N(key)) {                                         \
-                return m->capacity - 1;                                        \
-            }                                                                  \
-            size_t baseHash = hash##N(key) * 514'685'581u;                     \
-            size_t end = m->capacity - 2;                                      \
-            /* rehash on bad clustering */                                     \
-            for(size_t i = 0; i <= 5; i++) {                                   \
-                size_t hash = (baseHash + i) & end;                            \
-                K keyEntry = m->keys[hash];                                    \
-                if(isInvalidKey##N(keyEntry) || equal##N(keyEntry, key)) {     \
-                    return hash;                                               \
-                }                                                              \
-            }                                                                  \
-            rehashFactor *= 2;                                                 \
-        }                                                                      \
-    }                                                                          \
-                                                                               \
-    void initHashMap##N(HashMap##N* m) {                                       \
-        m->keys = nullptr;                                                     \
-        m->values = nullptr;                                                   \
-        m->capacity = 0;                                                       \
-        m->entries = 0;                                                        \
-    }                                                                          \
-                                                                               \
-    void destroyHashMap##N(HashMap##N* m) {                                    \
-        coreFree(m->keys);                                                     \
-        coreFree(m->values);                                                   \
-        *m = (HashMap##N){0};                                                  \
-    }                                                                          \
-                                                                               \
-    void rehashHashMap##N(HashMap##N* m, size_t minCapacity) {                 \
-        if(minCapacity <= m->capacity) {                                       \
-            return;                                                            \
-        }                                                                      \
-        size_t l = roundUp2(maxSize(minCapacity, 8lu)) + 1;                    \
-        HashMap##N map;                                                        \
-        initHashMap##N(&map);                                                  \
-        size_t keyBytes = l * sizeof(K);                                       \
-        map.keys = coreAllocate(keyBytes);                                     \
-        memset(map.keys, 0, keyBytes);                                         \
-        memset(map.keys + (l - 1), 1, sizeof(K));                              \
-        map.values = coreAllocate(l * sizeof(V));                              \
-        map.capacity = l;                                                      \
-                                                                               \
-        size_t length = m->capacity;                                           \
-        if(length > 0) {                                                       \
-            length--;                                                          \
-            for(size_t i = 0; i < length; i++) {                               \
-                K keyEntry = m->keys[i];                                       \
-                if(!isInvalidKey##N(keyEntry)) {                               \
-                    *putHashMapKey##N(&map, keyEntry) = m->values[i];          \
-                }                                                              \
-            }                                                                  \
-            K keyEntry = m->keys[length];                                      \
-            if(isInvalidKey##N(keyEntry)) {                                    \
-                *putHashMapKey##N(&map, keyEntry) = m->values[length];         \
-            }                                                                  \
-        }                                                                      \
-        swap(&map, m);                                                         \
-        destroyHashMap##N(&map);                                               \
-    }                                                                          \
-                                                                               \
-    V* putHashMapKey##N(HashMap##N* m, K key) {                                \
-        size_t index = searchSlot##N(m, key);                                  \
-        K* keyEntry = m->keys + index;                                         \
-        if(equal##N(*keyEntry, key)) {                                         \
-            return m->values + index;                                          \
-        }                                                                      \
-        m->entries++;                                                          \
-        *keyEntry = key;                                                       \
-        return m->values + index;                                              \
-    }                                                                          \
-                                                                               \
-    V* searchHashMapKey##N(const HashMap##N* m, K key) {                       \
-        if(m->capacity != 0) {                                                 \
-            if(isInvalidKey##N(key)) {                                         \
-                size_t i = m->capacity - 1;                                    \
-                return isInvalidKey##N(m->keys[i]) ? m->values + i : nullptr;  \
-            }                                                                  \
-            size_t baseHash = hash##N(key) * 514'685'581u;                     \
-            size_t end = m->capacity - 2;                                      \
-            for(size_t i = 0; i <= end; i++) {                                 \
-                size_t hash = (baseHash + i) & end;                            \
-                K keyEntry = m->keys[hash];                                    \
-                if(equal##N(keyEntry, key)) {                                  \
-                    return m->values + hash;                                   \
-                } else if(isInvalidKey##N(keyEntry)) {                         \
-                    return nullptr;                                            \
-                }                                                              \
-            }                                                                  \
-        }                                                                      \
-        return nullptr;                                                        \
-    }                                                                          \
-                                                                               \
-    void clearHashMap##N(HashMap##N* m) {                                      \
-        if(m->keys == nullptr) {                                               \
-            return;                                                            \
-        }                                                                      \
-        m->entries = 0;                                                        \
-        memset(m->keys, 0, m->capacity * sizeof(K));                           \
-        memset(m->keys + (m->capacity - 1), 1, sizeof(K));                     \
-    }                                                                          \
-                                                                               \
-    bool removeHashMapKey##N(HashMap##N* m, K key) {                           \
-        /* ToDo: This is a very slow remove */                                 \
-        HashMap##N n;                                                          \
-        initHashMap##N(&n);                                                    \
-        HashMapIterator##N i;                                                  \
-        initHashMapIterator##N(&i, m);                                         \
-        bool r = false;                                                        \
-        while(hasNextHashMapNode##N(&i)) {                                     \
-            HashMapNode##N* node = nextHashMapNode##N(&i);                     \
-            if(!equal##N(key, *node->key)) {                                   \
-                *putHashMapKey##N(&n, *node->key) = *node->value;              \
-            } else {                                                           \
-                r = true;                                                      \
-            }                                                                  \
-        }                                                                      \
-        swap(&n, m);                                                           \
-        destroyHashMap##N(&n);                                                 \
-        return r;                                                              \
-    }                                                                          \
-                                                                               \
-    void initHashMapIterator##N(HashMapIterator##N* mi, const HashMap##N* m) { \
-        mi->key = m->keys;                                                     \
-        mi->endKey = mi->key + m->capacity;                                    \
-        mi->value = m->values;                                                 \
-        mi->endValue = mi->value + m->capacity;                                \
-        mi->map = m;                                                           \
-    }                                                                          \
-                                                                               \
-    bool hasNextHashMapNode##N(HashMapIterator##N* mi) {                       \
-        while(mi->key != mi->endKey) {                                         \
-            K* nextKey = mi->key + 1;                                          \
-            if(isInvalidKey##N(*mi->key) == (nextKey == mi->endKey)) {         \
-                break;                                                         \
-            }                                                                  \
-            mi->key = nextKey;                                                 \
-            mi->value++;                                                       \
-        }                                                                      \
-        return mi->key != mi->endKey;                                          \
-    }                                                                          \
-                                                                               \
-    HashMapNode##N* nextHashMapNode##N(HashMapIterator##N* mi) {               \
-        mi->node.key = mi->key;                                                \
-        mi->node.value = mi->value;                                            \
-        mi->key++;                                                             \
-        mi->value++;                                                           \
-        return &mi->node;                                                      \
-    }                                                                          \
-                                                                               \
-    size_t toStringHashMap##N(                                                 \
-        const HashMap##N* m, char* buffer, size_t n, ToStringKey##N keyString, \
-        ToStringValue##N valueString) {                                        \
-        size_t w = 0;                                                          \
-        stringAdd(&w, &buffer, &n, toString(buffer, n, "["));                  \
-        bool notFirst = false;                                                 \
-        HashMapIterator##N i;                                                  \
-        initHashMapIterator##N(&i, m);                                         \
-        while(hasNextHashMapNode##N(&i)) {                                     \
-            HashMapNode##N* node = nextHashMapNode##N(&i);                     \
-            if(notFirst) {                                                     \
-                stringAdd(&w, &buffer, &n, toString(buffer, n, ", "));         \
-            }                                                                  \
-            notFirst = true;                                                   \
-            stringAdd(&w, &buffer, &n, keyString(node->key, buffer, n));       \
-            stringAdd(&w, &buffer, &n, toString(buffer, n, " = "));            \
-            stringAdd(&w, &buffer, &n, valueString(node->value, buffer, n));   \
-        }                                                                      \
-        stringAdd(&w, &buffer, &n, toString(buffer, n, "]"));                  \
-        return w;                                                              \
-    }
-
-#endif

+ 363 - 0
include/core/ProbingHashMap.hpp

@@ -0,0 +1,363 @@
+#ifndef CORE_PROBING_HASHMAP_HPP
+#define CORE_PROBING_HASHMAP_HPP
+
+#include "core/List.hpp"
+#include "core/ToString.hpp"
+#include "core/Utility.hpp"
+
+template<typename T>
+concept Hashable = requires(const T& t) { t.hashCode(); };
+template<typename T>
+concept HashCast = requires(const T& t) { static_cast<size_t>(t); };
+
+namespace Core {
+    template<Hashable H>
+    inline size_t hashCode(const H& key) {
+        return key.hashCode();
+    }
+
+    template<HashCast H>
+    inline size_t hashCode(const H& key) {
+        return static_cast<size_t>(key);
+    }
+
+    template<typename K, typename V>
+    struct ProbingHashMap final {
+        template<typename Value>
+        class Node final {
+            friend ProbingHashMap;
+            friend List<Node>;
+            K key;
+
+        public:
+            Value& value;
+
+            const K& getKey() const {
+                return key;
+            }
+
+            size_t toString(char* s, size_t n) const {
+                size_t total = 0;
+                addString(key, s, n, total);
+                addString(" = ", s, n, total);
+                addString(value, s, n, total);
+                return total;
+            }
+
+        private:
+            Node(const K& key_, Value& value_) : key(key_), value(value_) {
+            }
+        };
+
+    private:
+        static constexpr K INVALID = {};
+
+        template<typename Value, typename R, R (*A)(const K&, Value&)>
+        class Iterator final {
+            const K* currentKey;
+            const K* endKey;
+            Value* currentValue;
+
+        public:
+            Iterator(
+                const K* key, const K* endKey_, Value* value, bool invalidSet) :
+                currentKey(key), endKey(endKey_), currentValue(value) {
+                if(!invalidSet) {
+                    skip();
+                }
+            }
+
+            Iterator& operator++() {
+                ++currentKey;
+                ++currentValue;
+                skip();
+                return *this;
+            }
+
+            bool operator!=(const Iterator& other) const {
+                return currentKey != other.currentKey;
+            }
+
+            R operator*() const {
+                return A(*currentKey, *currentValue);
+            }
+
+        private:
+            void skip() {
+                while(currentKey != endKey && *currentKey == INVALID) {
+                    ++currentKey;
+                    ++currentValue;
+                }
+            }
+        };
+
+        template<typename Value>
+        static Node<Value> access(const K& key, Value& value) {
+            return Node<Value>(key, value);
+        }
+
+        template<typename Value>
+        static Value& accessValue(const K&, Value& value) {
+            return value;
+        }
+
+        static const K& accessKey(const K& key, const V&) {
+            return key;
+        }
+
+        template<typename Value>
+        using BaseEntryIterator = Iterator<Value, Node<Value>, access<Value>>;
+        using EntryIterator = BaseEntryIterator<V>;
+        using ConstEntryIterator = BaseEntryIterator<const V>;
+
+        template<typename Value>
+        using BaseValueIterator = Iterator<Value, Value&, accessValue<Value>>;
+        using ValueIterator = BaseValueIterator<V>;
+        using ConstValueIterator = BaseValueIterator<const V>;
+
+        using ConstKeyIterator = Iterator<const V, const K&, accessKey>;
+
+        template<typename M, typename I>
+        struct IteratorAdapter final {
+            M& map;
+
+            I begin() const {
+                return {
+                    map.keys.begin(), map.keys.end(), map.values,
+                    map.invalidSet};
+            }
+
+            I end() const {
+                return {
+                    map.keys.end(), map.keys.end(), nullptr, map.invalidSet};
+            }
+        };
+
+        using ValueIteratorAdapter =
+            IteratorAdapter<ProbingHashMap, ValueIterator>;
+        using ConstValueIteratorAdapter =
+            IteratorAdapter<const ProbingHashMap, ConstValueIterator>;
+
+        using ConstKeyIteratorAdapter =
+            IteratorAdapter<const ProbingHashMap, ConstKeyIterator>;
+
+    public:
+        List<K> keys{};
+        V* values = nullptr;
+        List<int> jumps{};
+        size_t entries = 0;
+        bool invalidSet = false;
+
+    public:
+        ProbingHashMap() = default;
+
+        ProbingHashMap(const ProbingHashMap& other) {
+            for(const auto& e : other) {
+                add(e.getKey(), e.value);
+            }
+        }
+
+        ProbingHashMap(ProbingHashMap&& other) {
+            swap(other);
+        }
+
+        ~ProbingHashMap() {
+            size_t length = keys.getLength();
+            if(length > 0) {
+                for(size_t i = 1; i < length; i++) {
+                    if(keys[i] != INVALID) {
+                        values[i].~V();
+                    }
+                }
+                if(invalidSet) {
+                    values[length].~V();
+                }
+            }
+            delete[] reinterpret_cast<AlignedType<V>*>(values);
+        }
+
+        ProbingHashMap& operator=(ProbingHashMap other) {
+            swap(other);
+            return *this;
+        }
+
+        void rehash(size_t minCapacity) {
+            if(minCapacity <= keys.getLength()) {
+                return;
+            }
+            ProbingHashMap<K, V> map;
+            size_t l = (1lu << roundUpLog2(max(minCapacity, 8lu))) + 1;
+            map.keys.resize(l, INVALID);
+            map.values = reinterpret_cast<V*>(coreNewN(AlignedType<V>, l));
+            map.jumps.resize(l, INVALID);
+            size_t length = keys.getLength();
+            if(length > 0) {
+                for(size_t i = 1; i < length; i++) {
+                    if(keys[i] != INVALID) {
+                        map.add(keys[i], values[i]);
+                    }
+                }
+                if(invalidSet) {
+                    map.add(INVALID, values[length]);
+                }
+            }
+            swap(map);
+        }
+
+        template<typename... Args>
+        bool tryEmplace(V*& v, const K& key, Args&&... args) {
+            size_t index = 0;
+            if(key == INVALID) {
+                if(invalidSet) {
+                    return false;
+                }
+                rehash(entries * 2 + 1);
+                invalidSet = true;
+            } else {
+                index = searchSlot(key);
+                if(keys[index] == key) {
+                    return false;
+                }
+            }
+            keys[index] = key;
+            v = new(values + index) V(Core::forward<Args>(args)...);
+            entries++;
+            markSlot(key);
+            return true;
+        }
+
+        template<typename VA>
+        V& put(const K& key, VA&& value) {
+            size_t index = 0;
+            if(key == INVALID) {
+                if(invalidSet) {
+                    return (values[0] = Core::forward<VA>(value));
+                }
+                rehash(entries * 2 + 1);
+                invalidSet = true;
+            } else {
+                index = searchSlot(key);
+                if(keys[index] == key) {
+                    return (values[index] = Core::forward<VA>(value));
+                }
+            }
+            new(values + index) V(Core::forward<VA>(value));
+            entries++;
+            keys[index] = key;
+            markSlot(key);
+            return values[index];
+        }
+
+        template<typename VA>
+        ProbingHashMap& add(const K& key, VA&& value) {
+            put(key, Core::forward<VA>(value));
+            return *this;
+        }
+
+        const V* search(const K& key) const {
+            return searchValue<const V>(key);
+        }
+
+        V* search(const K& key) {
+            return searchValue<V>(key);
+        }
+
+        bool contains(const K& key) const {
+            return search(key) != nullptr;
+        }
+
+        ProbingHashMap& clear() {
+            ProbingHashMap<K, V> map;
+            swap(map);
+            return *this;
+        }
+
+        ConstKeyIteratorAdapter getKeys() const {
+            return {*this};
+        }
+
+        ValueIteratorAdapter getValues() {
+            return {*this};
+        }
+
+        ConstValueIteratorAdapter getValues() const {
+            return {*this};
+        }
+
+        EntryIterator begin() {
+            return {keys.begin(), keys.end(), values, invalidSet};
+        }
+
+        EntryIterator end() {
+            return {keys.end(), keys.end(), nullptr, invalidSet};
+        }
+
+        ConstEntryIterator begin() const {
+            return {keys.begin(), keys.end(), values, invalidSet};
+        }
+
+        ConstEntryIterator end() const {
+            return {keys.end(), keys.end(), nullptr, invalidSet};
+        }
+
+        void swap(ProbingHashMap& o) {
+            Core::swap(o.keys, keys);
+            Core::swap(o.values, values);
+            Core::swap(o.jumps, jumps);
+            Core::swap(o.entries, entries);
+            Core::swap(o.invalidSet, invalidSet);
+        }
+
+    private:
+        size_t searchSlot(const K& key) {
+            size_t rehashFactor = 2;
+            while(true) {
+                rehash(entries * rehashFactor + 1);
+                size_t baseHash = hashCode(key) * 514'685'581u;
+                size_t end = keys.getLength() - 2;
+                // rehash on bad clustering
+                for(size_t i = 0; i <= 5; i++) {
+                    size_t hash = 1 + ((baseHash + i) & end);
+                    if(keys[hash] == INVALID || keys[hash] == key) {
+                        return hash;
+                    }
+                }
+                rehashFactor *= 2;
+            }
+        }
+
+        void markSlot(const K& key) {
+            size_t baseHash = hashCode(key) * 514'685'581u;
+            size_t end = keys.getLength() - 2;
+            for(size_t i = 0; i <= 5; i++) {
+                size_t hash = 1 + ((baseHash + i) & end);
+                if(keys[hash] == key) {
+                    return;
+                }
+                jumps[hash]++;
+            }
+        }
+
+        template<typename Value>
+        Value* searchValue(const K& key) const {
+            if(keys.getLength() != 0) {
+                if(key == INVALID) {
+                    return invalidSet ? values : nullptr;
+                }
+                size_t baseHash = hashCode(key) * 514'685'581u;
+                size_t end = keys.getLength() - 2;
+                for(size_t i = 0; i <= end; i++) [[unlikely]] {
+                    size_t hash = 1 + ((baseHash + i) & end);
+                    if(keys[hash] == key) [[likely]] {
+                        return values + hash;
+                    } else if(keys[hash] == INVALID) {
+                        return nullptr;
+                    }
+                }
+            }
+            return nullptr;
+        }
+    };
+}
+
+#endif

+ 0 - 3
include/core/Vector.hpp

@@ -6,9 +6,6 @@
 #include "core/Math.hpp"
 #include "core/ToString.hpp"
 
-template<size_t R>
-concept Wusi = R > 3;
-
 namespace Core {
     template<size_t N, typename T>
     class alignas(sizeof(T) * (Core::isPowerOf2(N) ? N : 1)) Vector final {

+ 0 - 66
old/Frustum.cpp

@@ -1,66 +0,0 @@
-#include "core/math/Frustum.hpp"
-
-Core::Frustum::Frustum(float fieldOfView, float nearClip_, float farClip_)
-    : projection(), planes(),
-      tan(tanf(Core::Math::degreeToRadian(fieldOfView) * 0.5f)),
-      nearClip(nearClip_), farClip(farClip_) {
-    float diff = 1.0f / (nearClip - farClip);
-    projection.set(1, Vector4(0.0f, 1.0f / tan, 0.0f, 0.0f));
-    projection.set(2, Vector4(0.0f, 0.0f, (nearClip + farClip) * diff,
-                              (2.0f * nearClip * farClip) * diff));
-    projection.set(3, Vector4(0.0f, 0.0f, -1.0f, 0.0f));
-}
-
-const Core::Matrix& Core::Frustum::updateProjection(const IntVector2& size) {
-    projection.set(0, Vector4(static_cast<float>(size[1]) /
-                                  (tan * static_cast<float>(size[0])),
-                              0.0f, 0.0f, 0.0f));
-    return projection;
-}
-
-void Core::Frustum::updatePlanes(const Vector3& pos, const Vector3& right,
-                                 const Vector3& up, const Vector3& front,
-                                 const IntVector2& size) {
-    float aspect = static_cast<float>(size[0]) / static_cast<float>(size[1]);
-
-    float hNearHeight = tan * nearClip;
-    float hNearWidth = hNearHeight * aspect;
-
-    float hFarHeight = tan * farClip;
-    float hFarWidth = hFarHeight * aspect;
-
-    Vector3 fCenter = pos + front * farClip;
-    Vector3 fTopLeft = fCenter + (up * hFarHeight) - (right * hFarWidth);
-    Vector3 fTopRight = fCenter + (up * hFarHeight) + (right * hFarWidth);
-    Vector3 fBottomRight = fCenter - (up * hFarHeight) + (right * hFarWidth);
-
-    Vector3 nCenter = pos + front * nearClip;
-    Vector3 nTopLeft = nCenter + (up * hNearHeight) - (right * hNearWidth);
-    Vector3 nBottomLeft = nCenter - (up * hNearHeight) - (right * hNearWidth);
-    Vector3 nBottomRight = nCenter - (up * hNearHeight) + (right * hNearWidth);
-
-    planes[0] = Plane(nBottomRight, nTopLeft, nBottomLeft);     // n plane
-    planes[1] = Plane(fTopRight, fBottomRight, fTopLeft);       // f plane
-    planes[2] = Plane(nBottomRight, nBottomLeft, fBottomRight); // bottom plane
-    planes[3] = Plane(fTopLeft, nTopLeft, fTopRight);           // top plane
-    planes[4] = Plane(nBottomLeft, nTopLeft, fTopLeft);         // left plane
-    planes[5] = Plane(fBottomRight, fTopRight, nBottomRight);   // right plane
-}
-
-bool Core::Frustum::isInside(const Vector3& pos) const {
-    for(const Plane& p : planes) {
-        if(p.getSignedDistance(pos) < 0.0f) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool Core::Frustum::isInside(const Vector3& pos, float radius) const {
-    for(const Plane& p : planes) {
-        if(p.getSignedDistance(pos) < -radius) {
-            return false;
-        }
-    }
-    return true;
-}

+ 0 - 36
old/Frustum.hpp

@@ -1,36 +0,0 @@
-#ifndef CORE_FRUSTUM_HPP
-#define CORE_FRUSTUM_HPP
-
-#include "core/data/Array.hpp"
-#include "core/math/Matrix.hpp"
-#include "core/math/Plane.hpp"
-
-namespace Core {
-    class Frustum final {
-        Matrix projection;
-        Array<Plane, 6> planes;
-
-    public:
-        float tan;
-        float nearClip;
-        float farClip;
-
-        Frustum(float fieldOfView, float nearClip, float farClip);
-        const Matrix& updateProjection(const IntVector2& size);
-        void updatePlanes(const Vector3& pos, const Vector3& right,
-                          const Vector3& up, const Vector3& front,
-                          const IntVector2& size);
-
-        bool isInside(const Vector3& pos) const;
-        bool isInside(const Vector3& pos, float radius) const;
-
-        void toString(BufferString& s) const {
-            s.append("(tan = ").append(tan);
-            s.append(", nearClip = ").append(nearClip);
-            s.append(", farClip = ").append(farClip);
-            s.append(')');
-        }
-    };
-}
-
-#endif

+ 0 - 63
old/FrustumTests.cpp

@@ -1,63 +0,0 @@
-#include "../Tests.hpp"
-#include "core/math/Frustum.hpp"
-
-using V3 = Core::Vector3;
-
-static void testToString() {
-    Core::Frustum f(60.0f, 0.1f, 1000.0f);
-    CORE_TEST_STRING("(tan = 0.58, nearClip = 0.10, farClip = 1000.00)", f);
-}
-
-static void testPointIsInside() {
-    Core::IntVector2 size(200, 100);
-    Core::Frustum f(60.0f, 0.1f, 1000.0f);
-    f.updatePlanes(V3(0.0f, 0.0f, 0.0f), V3(1.0f, 0.0f, 0.0f),
-                   V3(0.0f, 1.0f, 0.0f), V3(0.0f, 0.0f, 1.0f), size);
-
-    CORE_TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 5.0f)));
-    CORE_TEST_FALSE(f.isInside(V3(0.0f, 0.0f, 1004.0f)));
-    CORE_TEST_FALSE(f.isInside(V3(0.0f, 0.0f, -5.0f)));
-    CORE_TEST_FALSE(f.isInside(V3(0.0f, 50.0f, 5.0f)));
-    CORE_TEST_FALSE(f.isInside(V3(0.0f, -50.0f, 5.0f)));
-    CORE_TEST_FALSE(f.isInside(V3(50.0f, 0.0f, 5.0f)));
-    CORE_TEST_FALSE(f.isInside(V3(-50.0f, 0.0f, 5.0f)));
-}
-
-static void testSphereIsInside() {
-    Core::IntVector2 size(200, 100);
-    Core::Frustum f(60.0f, 0.1f, 1000.0f);
-    f.updatePlanes(V3(0.0f, 0.0f, 0.0f), V3(1.0f, 0.0f, 0.0f),
-                   V3(0.0f, 1.0f, 0.0f), V3(0.0f, 0.0f, 1.0f), size);
-
-    CORE_TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 5.0f), 3.0f));
-    CORE_TEST_FALSE(f.isInside(V3(0.0f, 0.0f, 1004.0f), 3.0f));
-    CORE_TEST_FALSE(f.isInside(V3(0.0f, 0.0f, -5.0f), 3.0f));
-    CORE_TEST_FALSE(f.isInside(V3(0.0f, 50.0f, 5.0f), 3.0f));
-    CORE_TEST_FALSE(f.isInside(V3(0.0f, -50.0f, 5.0f), 3.0f));
-    CORE_TEST_FALSE(f.isInside(V3(50.0f, 0.0f, 5.0f), 3.0f));
-    CORE_TEST_FALSE(f.isInside(V3(-50.0f, 0.0f, 5.0f), 3.0f));
-
-    CORE_TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 5.0f), 3.0f));
-    CORE_TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 1004.0f), 50.0f));
-    CORE_TEST_TRUE(f.isInside(V3(0.0f, 0.0f, -5.0f), 50.0f));
-    CORE_TEST_TRUE(f.isInside(V3(0.0f, 50.0f, 5.0f), 50.0f));
-    CORE_TEST_TRUE(f.isInside(V3(0.0f, -50.0f, 5.0f), 50.0f));
-    CORE_TEST_TRUE(f.isInside(V3(50.0f, 0.0f, 5.0f), 50.0f));
-    CORE_TEST_TRUE(f.isInside(V3(-50.0f, 0.0f, 5.0f), 50.0f));
-}
-
-static void testUpdateProjection() {
-    Core::Frustum f(60.0f, 0.1f, 1000.0f);
-    CORE_TEST_STRING("[[1.30, 0.00, 0.00, 0.00], "
-                     "[0.00, 1.73, 0.00, 0.00], "
-                     "[0.00, 0.00, -1.00, -0.20], "
-                     "[0.00, 0.00, -1.00, 0.00]]",
-                     f.updateProjection(Core::IntVector2(400, 300)));
-}
-
-void Core::testFrustum() {
-    testToString();
-    testPointIsInside();
-    testSphereIsInside();
-    testUpdateProjection();
-}

+ 0 - 95
old/HashCode.hpp

@@ -1,95 +0,0 @@
-#ifndef CORE_HPPASH_CODE_HPP
-#define CORE_HPPASH_CODE_HPP
-
-#include <limits.h>
-
-#include "core/utils/Meta.hpp"
-#include "core/utils/Types.hpp"
-
-namespace Core {
-    template<typename H>
-    inline size_t hashCode(const H& key) {
-        return key.hashCode();
-    }
-
-    template<typename T>
-    inline size_t hashNumber(T t) {
-        static_assert(sizeof(t) <= sizeof(size_t));
-        return static_cast<size_t>(t);
-    }
-
-    inline size_t hashCode(char key) {
-        return hashNumber(key);
-    }
-
-    inline size_t hashCode(signed char key) {
-        return hashNumber(key);
-    }
-
-    inline size_t hashCode(signed short key) {
-        return hashNumber(key);
-    }
-
-    inline size_t hashCode(signed int key) {
-        return hashNumber(key);
-    }
-
-    inline size_t hashCode(signed long key) {
-        return hashNumber(key);
-    }
-
-    inline size_t hashCode(signed long long key) {
-        return hashNumber(key);
-    }
-
-    inline size_t hashCode(unsigned char key) {
-        return hashNumber(key);
-    }
-
-    inline size_t hashCode(unsigned short key) {
-        return hashNumber(key);
-    }
-
-    inline size_t hashCode(unsigned int key) {
-        return hashNumber(key);
-    }
-
-    inline size_t hashCode(unsigned long key) {
-        return hashNumber(key);
-    }
-
-    inline size_t hashCode(unsigned long long key) {
-        return hashNumber(key);
-    }
-
-    template<typename T>
-    inline consteval T emptyValue() {
-        if constexpr(Core::IsSame<char, T>) {
-            return CHAR_MAX;
-        } else if constexpr(Core::IsSame<signed char, T>) {
-            return SCHAR_MAX;
-        } else if constexpr(Core::IsSame<signed short, T>) {
-            return SHRT_MAX;
-        } else if constexpr(Core::IsSame<signed int, T>) {
-            return INT_MAX;
-        } else if constexpr(Core::IsSame<signed long, T>) {
-            return LONG_MAX;
-        } else if constexpr(Core::IsSame<signed long long, T>) {
-            return LLONG_MAX;
-        } else if constexpr(Core::IsSame<unsigned char, T>) {
-            return UCHAR_MAX;
-        } else if constexpr(Core::IsSame<unsigned short, T>) {
-            return USHRT_MAX;
-        } else if constexpr(Core::IsSame<unsigned int, T>) {
-            return UINT_MAX;
-        } else if constexpr(Core::IsSame<unsigned long, T>) {
-            return ULONG_MAX;
-        } else if constexpr(Core::IsSame<unsigned long long, T>) {
-            return ULLONG_MAX;
-        } else {
-            return T::emptyValue();
-        }
-    }
-}
-
-#endif

+ 5 - 5
old/HashMapTests.cpp

@@ -81,7 +81,7 @@ static void testClear() {
 
 template<typename T>
 static void testOverflow(bool light) {
-    int limit = light ? 10000 : 100000;
+    int limit = light ? 10'000 : 100'000;
     T map;
     map.add(INVALID, 42);
     for(int i = 0; i < limit; i++) {
@@ -170,11 +170,11 @@ static void testEmplaceProbing() {
 template<typename T>
 static void testToString() {
     if constexpr(Core::IsSame<T, IntMap>) {
-        CORE_TEST_STRING("[1 = 3, 2 = 4, 3 = 5, 2147483647 = 20]",
-                         getTestIntMap<T>());
+        CORE_TEST_STRING(
+            "[1 = 3, 2 = 4, 3 = 5, 2147483647 = 20]", getTestIntMap<T>());
     } else {
-        CORE_TEST_STRING("[2 = 4, 1 = 3, 3 = 5, 2147483647 = 20]",
-                         getTestIntMap<T>());
+        CORE_TEST_STRING(
+            "[2 = 4, 1 = 3, 3 = 5, 2147483647 = 20]", getTestIntMap<T>());
     }
     CORE_TEST_STRING("[1 = 3]", T().add(1, 3));
     CORE_TEST_STRING("[]", T());

+ 47 - 68
src/Frustum.cpp

@@ -1,94 +1,73 @@
 #include "core/Frustum.hpp"
 
-#include <math.h>
+#include <cmath>
 
-#include "core/Generic.hpp"
+using Core::Frustum;
+using V3 = Core::Vector3;
+using V4 = Core::Vector4;
 
-void initFrustum(Frustum* f, float fieldOfView, float nearClip, float farClip) {
-    f->tan = tanf(fieldOfView * 0.5f);
-    f->nearClip = nearClip;
-    f->farClip = farClip;
-
-    float diff = 1.0f / (nearClip - farClip);
-    f->projection.data[0] = V(0.0f, 0.0f, 0.0f, 0.0f);
-    f->projection.data[1] = V(0.0f, 1.0f / f->tan, 0.0f, 0.0f);
-    f->projection.data[2] =
-        V(0.0f, 0.0f, (nearClip + farClip) * diff,
-          (2.0f * nearClip * farClip) * diff);
-    f->projection.data[3] = V(0.0f, 0.0f, -1.0f, 0.0f);
+Frustum::Frustum(float fieldOfView, float nearClip, float farClip) :
+    projection(), planes(), tan(tanf(fieldOfView * 0.5f)), near(nearClip),
+    far(farClip) {
+    float diff = 1.0f / (near - far);
+    projection.set(1, V4(0.0f, 1.0f / tan, 0.0f, 0.0f));
+    projection.set(
+        2, V4(0.0f, 0.0f, (near + far) * diff, (2.0f * near * far) * diff));
+    projection.set(3, V4(0.0f, 0.0f, -1.0f, 0.0f));
 }
 
-const Matrix* updateProjection(Frustum* f, const IntVector2* size) {
-    float x = (float)size->y / (f->tan * (float)size->x);
-    f->projection.data[0] = V(x, 0.0f, 0.0f, 0.0f);
-    return &f->projection;
+const Core::Matrix& Frustum::updateProjection(const IntVector2& size) {
+    projection.set(
+        0, V4(static_cast<float>(size[1]) / (tan * static_cast<float>(size[0])),
+              0.0f, 0.0f, 0.0f));
+    return projection;
 }
 
-void updateFrustumPlanes(
-    Frustum* f, const Vector3* pos, const Vector3* right, const Vector3* up,
-    const Vector3* front, const IntVector2* size) {
-    float aspect = (float)size->x / (float)size->y;
+void Frustum::updatePlanes(
+    const V3& pos, const V3& right, const V3& up, const V3& front,
+    const IntVector2& size) {
+    float aspect = static_cast<float>(size[0]) / static_cast<float>(size[1]);
 
-    float hNearHeight = f->tan * f->nearClip;
+    float hNearHeight = tan * near;
     float hNearWidth = hNearHeight * aspect;
 
-    float hFarHeight = f->tan * f->farClip;
+    float hFarHeight = tan * far;
     float hFarWidth = hFarHeight * aspect;
 
-    Vector3 fCenter;
-    add(&fCenter, pos, mul(front, f->farClip));
-    Vector3 upFar;
-    mul(&upFar, up, hFarHeight);
-    Vector3 rightFar;
-    mul(&rightFar, right, hFarWidth);
-
-    Vector3 fTopLeft;
-    add(&fTopLeft, &fCenter, sub(&upFar, &rightFar));
-    Vector3 fTopRight;
-    add(&fTopRight, &fCenter, add(&upFar, &rightFar));
-    Vector3 fBottomRight;
-    sub(&fBottomRight, &fCenter, sub(&upFar, &rightFar));
-
-    Vector3 nCenter;
-    add(&nCenter, pos, mul(front, f->nearClip));
-    Vector3 upNear;
-    mul(&upNear, up, hNearHeight);
-    Vector3 rightNear;
-    mul(&rightNear, right, hNearWidth);
+    V3 fCenter = pos + front * far;
+    V3 upFar = up * hFarHeight;
+    V3 rightFar = right * hFarWidth;
+    V3 fTopLeft = fCenter + upFar - rightFar;
+    V3 fTopRight = fCenter + upFar + rightFar;
+    V3 fBottomRight = fCenter - upFar + rightFar;
 
-    Vector3 nTopLeft;
-    add(&nTopLeft, &nCenter, sub(&upNear, &rightNear));
-    Vector3 nBottomLeft;
-    sub(&nBottomLeft, &nCenter, add(&upNear, &rightNear));
-    Vector3 nBottomRight;
-    sub(&nBottomRight, &nCenter, sub(&upNear, &rightNear));
+    V3 nCenter = pos + front * near;
+    V3 upNear = up * hNearHeight;
+    V3 rightNear = right * hNearWidth;
+    V3 nTopLeft = nCenter + upNear - rightNear;
+    V3 nBottomLeft = nCenter - upNear - rightNear;
+    V3 nBottomRight = nCenter - upNear + rightNear;
 
-    // near plane
-    initPlane(f->planes + 0, &nBottomRight, &nTopLeft, &nBottomLeft);
-    // far plane
-    initPlane(f->planes + 1, &fTopRight, &fBottomRight, &fTopLeft);
-    // bottom plane
-    initPlane(f->planes + 2, &nBottomRight, &nBottomLeft, &fBottomRight);
-    // top plane
-    initPlane(f->planes + 3, &fTopLeft, &nTopLeft, &fTopRight);
-    // left plane
-    initPlane(f->planes + 4, &nBottomLeft, &nTopLeft, &fTopLeft);
-    // right plane
-    initPlane(f->planes + 5, &fBottomRight, &fTopRight, &nBottomRight);
+    planes[0] = Plane(nBottomRight, nTopLeft, nBottomLeft);     // near plane
+    planes[1] = Plane(fTopRight, fBottomRight, fTopLeft);       // far plane
+    planes[2] = Plane(nBottomRight, nBottomLeft, fBottomRight); // bottom plane
+    planes[3] = Plane(fTopLeft, nTopLeft, fTopRight);           // top plane
+    planes[4] = Plane(nBottomLeft, nTopLeft, fTopLeft);         // left plane
+    planes[5] = Plane(fBottomRight, fTopRight, nBottomRight);   // right plane
 }
 
-bool isInsideFrustum(const Frustum* f, const Vector3* pos) {
-    for(size_t i = 0; i < ARRAY_LENGTH(f->planes); i++) {
-        if(signedDistance(f->planes + i, pos) < 0.0f) {
+bool Frustum::isInside(const V3& pos) const {
+    for(const Plane& p : planes) {
+        if(p.signedDistance(pos) < 0.0f) {
             return false;
         }
     }
     return true;
 }
 
-bool isInsideFrustumRadius(const Frustum* f, const Vector3* pos, float radius) {
-    for(size_t i = 0; i < ARRAY_LENGTH(f->planes); i++) {
-        if(signedDistance(f->planes + i, pos) < -radius) {
+bool Frustum::isInside(const V3& pos, float radius) const {
+    for(const Plane& p : planes) {
+        if(p.signedDistance(pos) < -radius) {
             return false;
         }
     }

+ 3 - 3
src/HashMap.cpp

@@ -1,6 +1,6 @@
-#include "core/HashMap.hpp"
+#include "core/ProbingHashMap.hpp"
 
-size_t hashString(const char* key) {
+/*size_t hashString(const char* key) {
     size_t h = 0;
     while(*key != '\0') {
         h = 2'120'251'889lu * h + (size_t)(*(key++));
@@ -21,4 +21,4 @@ size_t roundUp2(size_t n) {
         n |= n >> 32;
     }
     return n + 1;
-}
+}*/

+ 2 - 2
test/Main.cpp

@@ -71,8 +71,8 @@ int main(int argAmount, const char** args) {
     // testBuffer(light);
     // testComponents();
     testFile();
-    // testFrustum();
-    // testHashMap(light);
+    testFrustum();
+    testHashMap(light);
     testMatrix();
     testPlane();
     testQuaternion();

+ 41 - 55
test/modules/FrustumTests.cpp

@@ -1,72 +1,58 @@
-#include "../Tests.h"
-#include "core/Frustum.h"
-#include "core/Utility.h"
+#include "../Tests.hpp"
+#include "core/Frustum.hpp"
+#include "core/Test.hpp"
 
-#define R60 degreeToRadian(60.0f)
+#define R60 Core::degreeToRadian(60.0f)
 
-static void testToString() {
-    Frustum f;
-    initFrustum(&f, R60, 0.1f, 1000.0f);
-    TEST_FLOAT(0.577f, f.tan, 0.01f);
-    TEST_FLOAT(0.100f, f.nearClip, 0.01f);
-    TEST_FLOAT(1000.0f, f.farClip, 0.01f);
-}
+using V3 = Core::Vector3;
 
 static void testPointIsInside() {
-    Frustum f;
-    initFrustum(&f, R60, 0.1f, 1000.0f);
-    updateFrustumPlanes(
-        &f, &V(0, 0, 0), &V(1, 0, 0), &V(0, 1, 0), &V(0, 0, 1), &IV(200, 100));
-
-    TEST_TRUE(isInsideFrustum(&f, &V(0, 0, 5)));
-    TEST_FALSE(isInsideFrustum(&f, &V(0, 0, 1004)));
-    TEST_FALSE(isInsideFrustum(&f, &V(0, 0, -5)));
-    TEST_FALSE(isInsideFrustum(&f, &V(0, 50, 5)));
-    TEST_FALSE(isInsideFrustum(&f, &V(0, -50, 5)));
-    TEST_FALSE(isInsideFrustum(&f, &V(50, 0, 5)));
-    TEST_FALSE(isInsideFrustum(&f, &V(-50, 0, 5)));
+    Core::IntVector2 size(200, 100);
+    Core::Frustum f(R60, 0.1f, 1000.0f);
+    f.updatePlanes(V3(0, 0, 0), V3(1, 0, 0), V3(0, 1, 0), V3(0, 0, 1), size);
+
+    TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 5.0f)));
+    TEST_FALSE(f.isInside(V3(0.0f, 0.0f, 1004.0f)));
+    TEST_FALSE(f.isInside(V3(0.0f, 0.0f, -5.0f)));
+    TEST_FALSE(f.isInside(V3(0.0f, 50.0f, 5.0f)));
+    TEST_FALSE(f.isInside(V3(0.0f, -50.0f, 5.0f)));
+    TEST_FALSE(f.isInside(V3(50.0f, 0.0f, 5.0f)));
+    TEST_FALSE(f.isInside(V3(-50.0f, 0.0f, 5.0f)));
 }
 
 static void testSphereIsInside() {
-    IntVector2 size = {{200, 100}};
-    Frustum f;
-    initFrustum(&f, R60, 0.1f, 1000.0f);
-    updateFrustumPlanes(
-        &f, &V(0, 0, 0), &V(1, 0, 0), &V(0, 1, 0), &V(0, 0, 1), &size);
-
-    TEST_TRUE(isInsideFrustumRadius(&f, &V(0, 0, 5), 3));
-    TEST_FALSE(isInsideFrustumRadius(&f, &V(0, 0, 1004), 3));
-    TEST_FALSE(isInsideFrustumRadius(&f, &V(0, 0, -5), 3));
-    TEST_FALSE(isInsideFrustumRadius(&f, &V(0, 50, 5), 3));
-    TEST_FALSE(isInsideFrustumRadius(&f, &V(0, -50, 5), 3));
-    TEST_FALSE(isInsideFrustumRadius(&f, &V(50, 0, 5), 3));
-    TEST_FALSE(isInsideFrustumRadius(&f, &V(-50, 0, 5), 3));
-
-    TEST_TRUE(isInsideFrustumRadius(&f, &V(0, 0, 5), 3));
-    TEST_TRUE(isInsideFrustumRadius(&f, &V(0, 0, 1004), 50));
-    TEST_TRUE(isInsideFrustumRadius(&f, &V(0, 0, -5), 50));
-    TEST_TRUE(isInsideFrustumRadius(&f, &V(0, 50, 5), 50));
-    TEST_TRUE(isInsideFrustumRadius(&f, &V(0, -50, 5), 50));
-    TEST_TRUE(isInsideFrustumRadius(&f, &V(50, 0, 5), 50));
-    TEST_TRUE(isInsideFrustumRadius(&f, &V(-50, 0, 5), 50));
+    Core::IntVector2 size(200, 100);
+    Core::Frustum f(R60, 0.1f, 1000.0f);
+    f.updatePlanes(V3(0, 0, 0), V3(1, 0, 0), V3(0, 1, 0), V3(0, 0, 1), size);
+
+    TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 5.0f), 3.0f));
+    TEST_FALSE(f.isInside(V3(0.0f, 0.0f, 1004.0f), 3.0f));
+    TEST_FALSE(f.isInside(V3(0.0f, 0.0f, -5.0f), 3.0f));
+    TEST_FALSE(f.isInside(V3(0.0f, 50.0f, 5.0f), 3.0f));
+    TEST_FALSE(f.isInside(V3(0.0f, -50.0f, 5.0f), 3.0f));
+    TEST_FALSE(f.isInside(V3(50.0f, 0.0f, 5.0f), 3.0f));
+    TEST_FALSE(f.isInside(V3(-50.0f, 0.0f, 5.0f), 3.0f));
+
+    TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 5.0f), 3.0f));
+    TEST_TRUE(f.isInside(V3(0.0f, 0.0f, 1004.0f), 50.0f));
+    TEST_TRUE(f.isInside(V3(0.0f, 0.0f, -5.0f), 50.0f));
+    TEST_TRUE(f.isInside(V3(0.0f, 50.0f, 5.0f), 50.0f));
+    TEST_TRUE(f.isInside(V3(0.0f, -50.0f, 5.0f), 50.0f));
+    TEST_TRUE(f.isInside(V3(50.0f, 0.0f, 5.0f), 50.0f));
+    TEST_TRUE(f.isInside(V3(-50.0f, 0.0f, 5.0f), 50.0f));
 }
 
 static void testUpdateProjection() {
-    Frustum f;
-    initFrustum(&f, R60, 0.1f, 1000.0f);
-    const Matrix* m = updateProjection(&f, &IV(400, 300));
-    char buffer[128];
-    toStringMatrix(m, buffer, sizeof(buffer));
+    Core::Frustum f(R60, 0.1f, 1000.0f);
     TEST_STRING(
-        "[[1.299, 0.000, 0.000, 0.000], "
-        "[0.000, 1.732, 0.000, 0.000], "
-        "[0.000, 0.000, -1.000, -0.200], "
-        "[0.000, 0.000, -1.000, 0.000]]",
-        buffer);
+        "[[1.30, 0.00, 0.00, 0.00], "
+        "[0.00, 1.73, 0.00, 0.00], "
+        "[0.00, 0.00, -1.00, -0.20], "
+        "[0.00, 0.00, -1.00, 0.00]]",
+        f.updateProjection(Core::IntVector2(400, 300)));
 }
 
 void testFrustum() {
-    testToString();
     testPointIsInside();
     testSphereIsInside();
     testUpdateProjection();

+ 342 - 254
test/modules/HashMapTests.cpp

@@ -1,331 +1,419 @@
-#include "../Tests.h"
-#include "core/HashMap.h"
-#include "core/ToString.h"
+#include "../Tests.hpp"
+#include "core/ProbingHashMap.hpp"
+#include "core/Test.hpp"
 
-HASHMAP(size_t, size_t, Size)
+template struct Core::ProbingHashMap<int, int>;
+using ProbingIntMap = Core::ProbingHashMap<int, int>;
 
-typedef struct {
-    size_t a;
-    size_t b;
-} A;
+// template struct Core::HashMap<int, int>;
+// using IntMap = Core::HashMap<int, int>;
 
-static size_t hashA(A key) {
-    return key.a ^ key.b;
-}
-
-static bool equalA(A a, A b) {
-    return a.a == b.a && a.b == b.b;
-}
+// static void testHash() {
+//     const char* s = "wusi";
+//     TEST_TRUE(hashString(s) != 0);
+// }
 
-static bool isInvalidKeyA(A key) {
-    return key.a == 0 && key.b == 0;
-}
-
-HASHMAP(A, int, A)
-HASHMAP_SOURCE(A, int, A)
-
-static HashMapSize getTestSizeMap() {
-    HashMapSize map;
-    initHashMapSize(&map);
-    *putHashMapKeySize(&map, 1) = 3;
-    *putHashMapKeySize(&map, 2) = 4;
-    *putHashMapKeySize(&map, 3) = 5;
-    *putHashMapKeySize(&map, 0) = 20;
+template<typename T>
+static T getTestIntMap() {
+    T map;
+    map.add(1, 3).add(2, 4).add(3, 5).add(0, 20);
     return map;
 }
 
-static void checkSizeMap(HashMapSize* map) {
-    size_t* a = searchHashMapKeySize(map, 1);
-    size_t* b = searchHashMapKeySize(map, 2);
-    size_t* c = searchHashMapKeySize(map, 3);
-    size_t* d = searchHashMapKeySize(map, 0);
+template<typename T>
+static void checkIntMap(T& map) {
+    int* a = map.search(1);
+    int* b = map.search(2);
+    int* c = map.search(3);
+    int* d = map.search(0);
     if(TEST_NOT_NULL(a) && TEST_NOT_NULL(b) && TEST_NOT_NULL(c) &&
        TEST_NOT_NULL(d)) {
-        TEST_SIZE(3, *a);
-        TEST_SIZE(4, *b);
-        TEST_SIZE(5, *c);
-        TEST_SIZE(20, *d);
+        TEST(3, *a);
+        TEST(4, *b);
+        TEST(5, *c);
+        TEST(20, *d);
     }
 }
 
+template<typename T>
 static void testAdd() {
-    HashMapSize map;
-    initHashMapSize(&map);
-    *putHashMapKeySize(&map, 5) = 4;
-    size_t* value = searchHashMapKeySize(&map, 5);
+    T map;
+    map.add(5, 4);
+    int* value = map.search(5);
     if(TEST_NOT_NULL(value)) {
-        TEST_SIZE(4, *value);
+        TEST(4, *value);
     }
-    destroyHashMapSize(&map);
 }
 
+template<typename T>
 static void testMultipleAdd() {
-    HashMapSize map = getTestSizeMap();
-    TEST_NOT_NULL(searchHashMapKeySize(&map, 0));
-    TEST_NOT_NULL(searchHashMapKeySize(&map, 1));
-    TEST_NOT_NULL(searchHashMapKeySize(&map, 2));
-    TEST_NOT_NULL(searchHashMapKeySize(&map, 3));
-    checkSizeMap(&map);
-    destroyHashMapSize(&map);
+    T map = getTestIntMap<T>();
+    TEST_TRUE(map.contains(0));
+    TEST_TRUE(map.contains(1));
+    TEST_TRUE(map.contains(2));
+    TEST_TRUE(map.contains(3));
+    checkIntMap(map);
 }
 
+template<typename T>
 static void testSearch() {
-    HashMapSize map = getTestSizeMap();
-    TEST_NULL(searchHashMapKeySize(&map, 6));
-    *putHashMapKeySize(&map, 5) = 4;
-    *putHashMapKeySize(&map, 10) = 3;
-    *putHashMapKeySize(&map, 15) = 2;
-    TEST_NULL(searchHashMapKeySize(&map, 6));
-    destroyHashMapSize(&map);
-}
-
-static void testSearchEmpty() {
-    HashMapSize map;
-    initHashMapSize(&map);
-    TEST_NULL(searchHashMapKeySize(&map, 6));
-    destroyHashMapSize(&map);
+    T map;
+    TEST_NULL(map.search(6));
+    map.add(5, 4).add(10, 3).add(15, 2);
+    TEST_NULL(map.search(6));
 }
 
+template<typename T>
 static void testAddReplace() {
-    HashMapSize map = getTestSizeMap();
-    *putHashMapKeySize(&map, 5) = 4;
-    *putHashMapKeySize(&map, 5) = 10;
-    TEST_NOT_NULL(searchHashMapKeySize(&map, 5));
-    size_t* a = searchHashMapKeySize(&map, 5);
+    T map;
+    map.add(5, 4).add(5, 10);
+    TEST_TRUE(map.contains(5));
+    int* a = map.search(5);
     if(TEST_NOT_NULL(a)) {
-        TEST_SIZE(10, *a);
+        TEST(10, *a);
     }
-    destroyHashMapSize(&map);
 }
 
+template<typename T>
 static void testClear() {
-    HashMapSize map = getTestSizeMap();
-    *putHashMapKeySize(&map, 5) = 4;
-    *putHashMapKeySize(&map, 4) = 10;
-    clearHashMapSize(&map);
-    TEST_NULL(searchHashMapKeySize(&map, 5));
-    TEST_NULL(searchHashMapKeySize(&map, 4));
-    destroyHashMapSize(&map);
-}
-
-static void testClearEmpty() {
-    HashMapSize map;
-    initHashMapSize(&map);
-    clearHashMapSize(&map);
-    destroyHashMapSize(&map);
+    T map;
+    map.clear();
+    map.add(5, 4).add(4, 10);
+    map.clear();
+    TEST_FALSE(map.contains(5));
+    TEST_FALSE(map.contains(4));
 }
 
+template<typename T>
 static void testOverflow(bool light) {
-    size_t limit = light ? 10'000 : 100'000;
-    HashMapSize map = getTestSizeMap();
-    for(size_t i = 0; i < limit; i++) {
-        *putHashMapKeySize(&map, i) = i;
+    int limit = light ? 10'000 : 100'000;
+    T map;
+    for(int i = 0; i < limit; i++) {
+        map.add(i, i);
     }
-    for(size_t i = 0; i < limit; i++) {
-        TEST_NOT_NULL(searchHashMapKeySize(&map, i));
+    for(int i = 0; i < limit; i++) {
+        TEST_TRUE(map.contains(i));
     }
-    destroyHashMapSize(&map);
 }
 
-static void testToString() {
-    HashMapSize map = getTestSizeMap();
-    char buffer[128];
-    size_t n = toStringHashMapSize(
-        &map, buffer, sizeof(buffer), toStringSize, toStringSize);
-    TEST_SIZE(29, n);
-    TEST_STRING("[2 = 4, 1 = 3, 3 = 5, 0 = 20]", buffer);
-
-    clearHashMapSize(&map);
-    *putHashMapKeySize(&map, 1) = 3;
-    n = toStringHashMapSize(
-        &map, buffer, sizeof(buffer), toStringSize, toStringSize);
-    TEST_SIZE(7, n);
-    TEST_STRING("[1 = 3]", buffer);
-
-    clearHashMapSize(&map);
-    n = toStringHashMapSize(
-        &map, buffer, sizeof(buffer), toStringSize, toStringSize);
-    TEST_SIZE(2, n);
-    TEST_STRING("[]", buffer);
-
-    destroyHashMapSize(&map);
-}
+static int aInstances = 0;
 
-static void testEntryForEach() {
-    HashMapSize map;
-    initHashMapSize(&map);
-    *putHashMapKeySize(&map, 0) = 1;
-    *putHashMapKeySize(&map, 5) = 4;
-    *putHashMapKeySize(&map, 10) = 3;
-    *putHashMapKeySize(&map, 15) = 2;
-
-    size_t counter = 0;
-    HashMapIteratorSize i;
-    initHashMapIteratorSize(&i, &map);
-    while(hasNextHashMapNodeSize(&i)) {
-        HashMapNodeSize* n = nextHashMapNodeSize(&i);
-        counter += *n->key + *n->value;
+struct HashMapTest {
+    int a;
+    int b;
+
+    HashMapTest(int a_, int b_) : a(a_), b(b_) {
     }
-    TEST_SIZE(40, counter);
 
-    destroyHashMapSize(&map);
-}
+    // none of these should be needed for the hashmap
+    HashMapTest(const HashMapTest&) = delete;
+    HashMapTest(HashMapTest&&) = delete;
+    HashMapTest& operator=(const HashMapTest&) = delete;
+    HashMapTest& operator=(HashMapTest&&) = delete;
 
-static void testInvalidPut() {
-    HashMapSize map;
-    initHashMapSize(&map);
+    bool operator==(const HashMapTest& other) const {
+        return a == other.a && b == other.b;
+    }
 
-    char buffer[128];
-    toStringHashMapSize(
-        &map, buffer, sizeof(buffer), toStringSize, toStringSize);
-    TEST_STRING("[]", buffer);
+    size_t toString(char* s, size_t n) const {
+        size_t total = 0;
+        addString("A(", s, n, total);
+        addString(a, s, n, total);
+        addString(", ", s, n, total);
+        addString(b, s, n, total);
+        addString(")", s, n, total);
+        return total;
+    }
+};
 
-    *putHashMapKeySize(&map, 0) = 3;
-    size_t* v = searchHashMapKeySize(&map, 0);
-    if(TEST_NOT_NULL(v)) {
-        TEST_SIZE(3, *v);
+struct ProbingTest final : public HashMapTest {
+    ProbingTest(int a_, int b_) : HashMapTest(a_, b_) {
+        aInstances++;
+    }
+
+    ProbingTest(const ProbingTest& o) : HashMapTest(o.a, o.b) {
+        aInstances++;
     }
-    toStringHashMapSize(
-        &map, buffer, sizeof(buffer), toStringSize, toStringSize);
-    TEST_STRING("[0 = 3]", buffer);
 
-    clearHashMapSize(&map);
-    TEST_NULL(searchHashMapKeySize(&map, 0));
+    ProbingTest(ProbingTest&& o) : HashMapTest(o.a, o.b) {
+        aInstances++;
+    }
+
+    ~ProbingTest() {
+        aInstances--;
+    }
 
-    destroyHashMapSize(&map);
+    ProbingTest& operator=(ProbingTest o) {
+        a = o.a;
+        b = o.b;
+        return *this;
+    }
+};
+
+static void testEmplaceProbing() {
+    {
+        Core::ProbingHashMap<int, ProbingTest> map;
+
+        ProbingTest* ar = nullptr;
+        TEST_TRUE(map.tryEmplace(ar, 0, 3, 4));
+        TEST_TRUE(map.tryEmplace(ar, 3, 4, 5));
+        TEST_TRUE(map.tryEmplace(ar, 20, 5, 6));
+        TEST_FALSE(map.tryEmplace(ar, 3, 6, 7));
+        TEST_FALSE(map.tryEmplace(ar, 20, 7, 8));
+
+        ProbingTest* a = map.search(0);
+        ProbingTest* b = map.search(3);
+        ProbingTest* c = map.search(20);
+
+        if(TEST_NOT_NULL(a) && TEST_NOT_NULL(b) && TEST_NOT_NULL(c)) {
+            TEST(ProbingTest(3, 4), *a);
+            TEST(ProbingTest(4, 5), *b);
+            TEST(ProbingTest(5, 6), *c);
+        }
+    }
+    TEST(0, aInstances);
 }
 
-static void testAddCollisions() {
-    HashMapSize map = getTestSizeMap();
-    for(size_t i = 0; i < 16; i++) {
-        *putHashMapKeySize(&map, i * 64) = i;
+template<typename T>
+static void testToString() {
+    if constexpr(Core::IsSame<T, int>) {
+        TEST_STRING(
+            "[1 = 3, 2 = 4, 3 = 5, 2147483647 = 20]", getTestIntMap<T>());
+    } else {
+        TEST_STRING("[0 = 20, 2 = 4, 1 = 3, 3 = 5]", getTestIntMap<T>());
     }
-    destroyHashMapSize(&map);
+    TEST_STRING("[1 = 3]", T().add(1, 3));
+    TEST_STRING("[]", T());
 }
 
-static void testRemove() {
-    HashMapSize map;
-    initHashMapSize(&map);
-    *putHashMapKeySize(&map, 1) = 3;
-    *putHashMapKeySize(&map, 2) = 4;
-    *putHashMapKeySize(&map, 3) = 5;
+template<typename T>
+static void testCopy() {
+    T map = getTestIntMap<T>();
+    T copy = map;
+    T copyA;
+    copyA = map;
+    checkIntMap(map);
+    checkIntMap(copy);
+    checkIntMap(copyA);
+    map.add(1, 20).add(2, 30).add(3, 40);
+    checkIntMap(copy);
+    checkIntMap(copyA);
+}
 
-    TEST_TRUE(removeHashMapKeySize(&map, 2));
-    TEST_FALSE(removeHashMapKeySize(&map, 7));
+template<typename T>
+static void testMove() {
+    T map = getTestIntMap<T>();
+    T move(Core::move(map));
+    checkIntMap(move);
+}
 
-    size_t* a = searchHashMapKeySize(&map, 1);
-    size_t* b = searchHashMapKeySize(&map, 2);
-    size_t* c = searchHashMapKeySize(&map, 3);
+template<typename T>
+static void testMoveAssignment() {
+    T map = getTestIntMap<T>();
+    T move;
+    move = Core::move(map);
+    checkIntMap(move);
+}
 
-    TEST_NULL(b);
-    if(TEST_NOT_NULL(a) && TEST_NOT_NULL(c)) {
-        TEST_SIZE(3, *a);
-        TEST_SIZE(5, *c);
-    }
+template<typename T>
+static void testEntryForEach() {
+    T map;
+    map.add(0, 1).add(5, 4).add(10, 3).add(15, 2);
 
-    destroyHashMapSize(&map);
-}
+    int counter = 0;
+    for(auto entry : map) {
+        counter += entry.getKey() + entry.value;
+    }
+    TEST(40, counter);
 
-static void testHash() {
-    const char* s = "wusi";
-    TEST_TRUE(hashString(s) != 0);
+    const T& cmap = map;
+    counter = 0;
+    for(auto entry : cmap) {
+        counter += entry.getKey() + entry.value;
+    }
+    TEST(40, counter);
 }
 
-static size_t toStringA(const A* a, char* buffer, size_t n) {
-    return toString(buffer, n, "{%zu, %zu}", a->a, a->b);
-}
+template<typename T>
+static void testKeyForEach() {
+    T map;
+    map.add(5, 4).add(10, 3).add(15, 2);
 
-static void testSearchStruct() {
-    HashMapA map;
-    initHashMapA(&map);
-    clearHashMapA(&map);
-    A a = {1, 2};
-    A b = {1, 3};
-    A c = {0, 0};
-    TEST_NULL(searchHashMapKeyA(&map, a));
-    TEST_NULL(searchHashMapKeyA(&map, b));
-    TEST_NULL(searchHashMapKeyA(&map, c));
-    *putHashMapKeyA(&map, a) = 3;
-    *putHashMapKeyA(&map, (A){5, 6}) = 5;
-
-    char buffer[128];
-    size_t n =
-        toStringHashMapA(&map, buffer, sizeof(buffer), toStringA, toStringInt);
-    TEST_SIZE(24, n);
-    TEST_STRING("[{5, 6} = 5, {1, 2} = 3]", buffer);
-
-    int* ap = searchHashMapKeyA(&map, a);
-    if(TEST_NOT_NULL(ap)) {
-        TEST_INT(3, *ap);
+    int counter = 0;
+    for(const int& key : map.getKeys()) {
+        counter += key;
     }
-    TEST_NULL(searchHashMapKeyA(&map, b));
-    TEST_NULL(searchHashMapKeyA(&map, c));
+    TEST(30, counter);
 
-    *putHashMapKeyA(&map, c) = 4;
-    int* cp = searchHashMapKeyA(&map, c);
-    if(TEST_NOT_NULL(cp)) {
-        TEST_INT(4, *cp);
+    const T& cmap = map;
+    counter = 0;
+    for(const int& key : cmap.getKeys()) {
+        counter += key;
     }
+    TEST(30, counter);
+}
+
+template<typename T>
+static void testValueForEach() {
+    T map;
+    map.add(5, 4).add(10, 3).add(15, 2);
 
-    for(size_t i = 0; i < 16; i++) {
-        *putHashMapKeyA(&map, (A){i * 64, 0}) = (int)i;
+    int counter = 0;
+    for(int& value : map.getValues()) {
+        counter += value;
     }
+    TEST(9, counter);
 
-    HashMapIteratorA iter;
-    initHashMapIteratorA(&iter, &map);
-    size_t sum = 0;
-    while(hasNextHashMapNodeA(&iter)) {
-        HashMapNodeA* node = nextHashMapNodeA(&iter);
-        sum += node->key->b;
+    const T& cmap = map;
+    counter = 0;
+    for(const int& value : cmap.getValues()) {
+        counter += value;
     }
-    TEST_SIZE(8, sum);
+    TEST(9, counter);
+}
+
+template<typename T>
+static void testType() {
+    Core::ProbingHashMap<T, int> m;
+    m.add(1, 3);
+}
 
-    removeHashMapKeyA(&map, (A){0, 1});
-    removeHashMapKeyA(&map, (A){0, 0});
-    clearHashMapA(&map);
+template<typename T>
+static void testTypes() {
+    testType<char>();
+    testType<signed char>();
+    testType<signed short>();
+    testType<signed int>();
+    testType<signed long>();
+    testType<signed long long>();
+    testType<unsigned char>();
+    testType<unsigned short>();
+    testType<unsigned int>();
+    testType<unsigned long>();
+    testType<unsigned long long>();
+}
 
-    destroyHashMapA(&map);
+template<typename T>
+static void testInvalid() {
+    T map;
+    int* v;
+    TEST_TRUE(map.tryEmplace(v, 0, 2));
+    if(TEST_NOT_NULL(v)) {
+        TEST(2, *v);
+    }
+    TEST_FALSE(map.tryEmplace(v, 0, 6));
+    if(TEST_NOT_NULL(v)) {
+        TEST(2, *v);
+    }
+    TEST(3, map.put(0, 3));
+    v = map.search(0);
+    if(TEST_NOT_NULL(v)) {
+        TEST(3, *v);
+    }
+    map.clear();
+    TEST_NULL(map.search(0));
 }
 
-static void testSearchSize() {
-    HashMapSize map;
-    initHashMapSize(&map);
-    TEST_NULL(searchHashMapKeySize(&map, 0));
-    TEST_NULL(searchHashMapKeySize(&map, 1));
-    TEST_NULL(searchHashMapKeySize(&map, 2));
-    *putHashMapKeySize(&map, 1) = 3;
-
-    size_t* ap = searchHashMapKeySize(&map, 1);
-    if(TEST_NOT_NULL(ap)) {
-        TEST_SIZE(3, *ap);
+template<typename T>
+static void testInvalidPut() {
+    T map;
+    TEST_STRING("[]", map);
+    TEST(3, map.put(0, 3));
+    TEST_STRING("[0 = 3]", map);
+    int* v = map.search(0);
+    if(TEST_NOT_NULL(v)) {
+        TEST(3, *v);
     }
-    TEST_NULL(searchHashMapKeySize(&map, 0));
-    TEST_NULL(searchHashMapKeySize(&map, 2));
+    map.clear();
+    TEST_FALSE(map.contains(0));
+    TEST_STRING("[]", map);
+}
 
-    *putHashMapKeySize(&map, 0) = 4;
-    size_t* cp = searchHashMapKeySize(&map, 0);
-    if(TEST_NOT_NULL(cp)) {
-        TEST_SIZE(4, *cp);
+template<typename T>
+static void testAddCollisions() {
+    T map;
+    for(int i = 0; i < 16; i++) {
+        map.add(i * 64, i);
     }
+}
 
-    destroyHashMapSize(&map);
+template<typename T>
+static void testMap(bool light) {
+    testAdd<T>();
+    testMultipleAdd<T>();
+    testSearch<T>();
+    testAddReplace<T>();
+    testClear<T>();
+    testOverflow<T>(light);
+    testToString<T>();
+    testCopy<T>();
+    testMove<T>();
+    testMoveAssignment<T>();
+    testEntryForEach<T>();
+    testKeyForEach<T>();
+    testValueForEach<T>();
+    testTypes<T>();
+    testInvalid<T>();
+    testInvalidPut<T>();
+    testAddCollisions<T>();
 }
 
+// static void testEmplace() {
+//     Core::HashMap<int, HashMapTest> map;
+//
+//     HashMapTest* ar = nullptr;
+//     CORE_TEST_TRUE(map.tryEmplace(ar, 0, 3, 4));
+//     CORE_TEST_TRUE(map.tryEmplace(ar, 3, 4, 5));
+//     CORE_TEST_TRUE(map.tryEmplace(ar, 20, 5, 6));
+//     CORE_TEST_FALSE(map.tryEmplace(ar, 3, 6, 7));
+//     CORE_TEST_FALSE(map.tryEmplace(ar, 20, 7, 8));
+//
+//     HashMapTest* a = map.search(0);
+//     HashMapTest* b = map.search(3);
+//     HashMapTest* c = map.search(20);
+//
+//     if(CORE_TEST_NOT_NULL(a) && CORE_TEST_NOT_NULL(b) &&
+//        CORE_TEST_NOT_NULL(c)) {
+//         CORE_TEST_EQUAL(HashMapTest(3, 4), *a);
+//         CORE_TEST_EQUAL(HashMapTest(4, 5), *b);
+//         CORE_TEST_EQUAL(HashMapTest(5, 6), *c);
+//     }
+// }
+
+// static void testRemove() {
+//     IntMap map;
+//     map.add(1, 3).add(2, 4).add(3, 5);
+//
+//     TEST_TRUE(map.remove(2));
+//     TEST_FALSE(map.remove(7));
+//
+//     int* a = map.search(1);
+//     int* b = map.search(2);
+//     int* c = map.search(3);
+//
+//     TEST_NULL(b);
+//     if(TEST_NOT_NULL(a) && TEST_NOT_NULL(c)) {
+//         TEST(3, *a);
+//         TEST(5, *c);
+//     }
+// }
+
 void testHashMap(bool light) {
-    testAdd();
-    testMultipleAdd();
-    testSearch();
-    testSearchEmpty();
-    testAddReplace();
-    testClear();
-    testClearEmpty();
-    testOverflow(light);
-    testToString();
-    testEntryForEach();
-    testInvalidPut();
-    testAddCollisions();
-    testRemove();
-    testHash();
-    testSearchStruct();
-    testSearchSize();
+    if(!light) {
+        // testHash();
+        testMap<ProbingIntMap>(light);
+        // testMap<IntMap>(light);
+        // testEmplace();
+        testEmplaceProbing();
+        // testRemove();
+    }
+    ProbingIntMap map;
+    map.add(1, 2);
+    map.add(17, 3);
+    map.add(33, 4);
+    map.add(33, 6);
+    map.add(49, 5);
+    LOG_WARNING("#", map);
+    LOG_WARNING("#", map.keys);
+    LOG_WARNING("#", map.jumps);
 }