module Tests; import Core.HashMap; import Core.Random; import Core.Test; import Core.Types; import Core.ToString; import Core.Meta; import Core.Array; template struct Core::HashMap; using IntMap = Core::HashMap; static IntMap getTestIntMap() { IntMap map; map.add(1, 3).add(2, 4).add(3, 5).add(0, 20); return map; } static void checkIntMap(IntMap& map) { int* a = map.search(1); int* b = map.search(2); int* c = map.search(3); int* d = map.search(0); if(Core::testNotNull(a) && Core::testNotNull(b) && Core::testNotNull(c) && Core::testNotNull(d)) { Core::test(3, *a); Core::test(4, *b); Core::test(5, *c); Core::test(20, *d); } } static void testAdd() { IntMap map; map.add(5, 4); int* value = map.search(5); if(Core::testNotNull(value)) { Core::test(4, *value); } } static void testMultipleAdd() { IntMap map = getTestIntMap(); Core::testTrue(map.contains(0)); Core::testTrue(map.contains(1)); Core::testTrue(map.contains(2)); Core::testTrue(map.contains(3)); checkIntMap(map); } static void testSearch() { IntMap map; Core::testNull(map.search(6)); map.add(5, 4).add(10, 3).add(15, 2); Core::testNull(map.search(6)); } static void testAddReplace() { IntMap map; map.add(5, 4).add(5, 10); Core::testTrue(map.contains(5)); int* a = map.search(5); if(Core::testNotNull(a)) { Core::test(10, *a); } } static void testClear() { IntMap map; map.clear(); map.add(5, 4).add(4, 10); map.clear(); Core::testFalse(map.contains(5)); Core::testFalse(map.contains(4)); } static void testOverflow(bool light) { int limit = light ? 10'000 : 100'000; IntMap map; for(int i = 0; i < limit; i++) { map.add(i, i); } for(int i = 0; i < limit; i++) { Core::testTrue(map.contains(i)); } } static int aInstances = 0; struct ProbingTest final { int a; int b; ProbingTest(int a_, int b_) noexcept : a(a_), b(b_) { aInstances++; } ProbingTest(const ProbingTest& o) = delete; ProbingTest(ProbingTest&& o) noexcept : a(o.a), b(o.b) { aInstances++; } ~ProbingTest() { aInstances--; } ProbingTest& operator=(const ProbingTest& o) = delete; ProbingTest& operator=(ProbingTest&& o) noexcept = default; bool operator==(const ProbingTest& other) const = default; size_t toString(Core::StringBase& s) const { return s.addFormat("A({}, {})", a, b); } }; static void testEmplaceProbing() { { Core::HashMap map; ProbingTest* ar = nullptr; Core::testTrue(map.tryEmplace(ar, 0, 3, 4)); Core::testTrue(map.tryEmplace(ar, 3, 4, 5)); Core::testTrue(map.tryEmplace(ar, 20, 5, 6)); Core::testFalse(map.tryEmplace(ar, 3, 6, 7)); Core::testFalse(map.tryEmplace(ar, 20, 7, 8)); ProbingTest* a = map.search(0); ProbingTest* b = map.search(3); ProbingTest* c = map.search(20); if(Core::testNotNull(a) && Core::testNotNull(b) && Core::testNotNull(c)) { Core::test(ProbingTest(3, 4), *a); Core::test(ProbingTest(4, 5), *b); Core::test(ProbingTest(5, 6), *c); } } Core::test(0, aInstances); } static void testToString() { Core::testString("[0 = 20, 2 = 4, 1 = 3, 3 = 5]", getTestIntMap()); Core::testString("[1 = 3]", IntMap().add(1, 3)); Core::testString("[]", IntMap()); } static void testCopy() { IntMap map = getTestIntMap(); IntMap copy = map; IntMap copyA; copyA = map; checkIntMap(map); checkIntMap(copy); checkIntMap(copyA); map.add(1, 20).add(2, 30).add(3, 40); checkIntMap(copy); checkIntMap(copyA); } static void testMove() { IntMap map = getTestIntMap(); IntMap move(Core::move(map)); checkIntMap(move); } static void testMoveAssignment() { IntMap map = getTestIntMap(); IntMap move; move = Core::move(map); checkIntMap(move); } static void testEntryForEach() { IntMap map; map.add(0, 1).add(5, 4).add(10, 3).add(15, 2); int counter = 0; for(auto entry : map) { counter += entry.getKey() + entry.value; } Core::test(40, counter); const IntMap& cmap = map; counter = 0; for(auto entry : cmap) { counter += entry.getKey() + entry.value; } Core::test(40, counter); } static void testKeyForEach() { IntMap map; map.add(5, 4).add(10, 3).add(15, 2); int counter = 0; for(const int& key : map.getKeys()) { counter += key; } Core::test(30, counter); const IntMap& cmap = map; counter = 0; for(const int& key : cmap.getKeys()) { counter += key; } Core::test(30, counter); } static void testValueForEach() { IntMap map; map.add(5, 4).add(10, 3).add(15, 2); int counter = 0; for(int& value : map.getValues()) { counter += value; } Core::test(9, counter); const IntMap& cmap = map; counter = 0; for(const int& value : cmap.getValues()) { counter += value; } Core::test(9, counter); } template static void testType() { Core::HashMap m; m.add(1, 3); } static void testTypes() { testType(); testType(); testType(); testType(); testType(); testType(); testType(); testType(); testType(); testType(); testType(); } static void testInvalid() { IntMap map; int* v = nullptr; Core::testTrue(map.tryEmplace(v, 0, 2)); if(Core::testNotNull(v)) { Core::test(2, *v); } Core::testFalse(map.tryEmplace(v, 0, 6)); if(Core::testNotNull(v)) { Core::test(2, *v); } Core::test(3, map.put(0, 3)); v = map.search(0); if(Core::testNotNull(v)) { Core::test(3, *v); } map.clear(); Core::testNull(map.search(0)); } static void testInvalidPut() { IntMap map; Core::testString("[]", map); Core::test(3, map.put(0, 3)); Core::testString("[0 = 3]", map); int* v = map.search(0); if(Core::testNotNull(v)) { Core::test(3, *v); } map.clear(); Core::testFalse(map.contains(0)); Core::testString("[]", map); } static void testAddCollisions() { IntMap map; for(int i = 0; i < 16; i++) { map.add(i * 64, i); } } static void testRemove() { IntMap map; map.add(1, 3).add(2, 4).add(3, 5); Core::testTrue(map.remove(2)); Core::testFalse(map.remove(7)); int* a = map.search(1); int* b = map.search(2); int* c = map.search(3); Core::testNull(b); if(Core::testNotNull(a) && Core::testNotNull(c)) { Core::test(3, *a); Core::test(5, *c); } } static void testRemoveLong() { IntMap map; Core::Random r(5); constexpr size_t LIMIT = 75; Core::Array a; for(int i = 0; i < 10'000; i++) { i32 r1 = r.nextI32(0, LIMIT); if(r.nextBool()) { i32 r2 = r.nextI32(0, LIMIT); map.add(r1, 2); map.add(r2, 2); a[static_cast(r1)] = 1; a[static_cast(r2)] = 1; } else { map.remove(r1); a[static_cast(r1)] = 0; } Core::Array copy = a; for(int key : map.getKeys()) { Core::testTrue(copy[static_cast(key)]); copy[static_cast(key)] = 0; } for(int key : copy) { Core::test(0, key); } } } void testHashMap(bool light) { testAdd(); testMultipleAdd(); testSearch(); testAddReplace(); testClear(); testOverflow(light); testToString(); testCopy(); testMove(); testMoveAssignment(); testEntryForEach(); testKeyForEach(); testValueForEach(); testTypes(); testInvalid(); testInvalidPut(); testAddCollisions(); testRemove(); testRemoveLong(); testEmplaceProbing(); }