diff --git a/Containers/public/AvlTree.hpp b/Containers/public/AvlTree.hpp index 72a1ef5..bde6645 100644 --- a/Containers/public/AvlTree.hpp +++ b/Containers/public/AvlTree.hpp @@ -16,9 +16,9 @@ namespace tp { inline bool descentLeft(AvlNumericKey in) const { return in.val < val; } inline bool exactNode(AvlNumericKey in) const { return in.val == val; } - inline AvlNumericKey getFindKey(/**/) const { return val; } - inline AvlNumericKey keyInRightSubtree(AvlNumericKey in) const { return in.val; } - inline AvlNumericKey keyInLeftSubtree(AvlNumericKey in) const { return in.val; } + inline AvlNumericKey getFindKey(/**/) const { return *this; } + inline AvlNumericKey keyInRightSubtree(AvlNumericKey in) const { return in; } + inline AvlNumericKey keyInLeftSubtree(AvlNumericKey in) const { return in; } inline void updateTreeCacheCallBack() {} }; @@ -47,7 +47,7 @@ namespace tp { private: inline bool descentRight(KeyArg aKey) const { return key.descentRight(aKey); } - inline bool descentLeft(KeyArg aKey) const { return key.descentRight(aKey); } + inline bool descentLeft(KeyArg aKey) const { return key.descentLeft(aKey); } inline bool exactNode(KeyArg aKey) const { return key.exactNode(aKey); } inline KeyArg getFindKey(const Node* node = nullptr) const { return key.getFindKey(/*node*/); } @@ -73,7 +73,13 @@ namespace tp { return new (mAlloc.allocate(sizeof(Node))) Node(key, data); } - inline ualni getNodeHeight(const Node* node) const { + inline void injectNodeInstead(Node* place, Node* inject) { + // TODO : swap instead of copy + place->data = inject->data; + place->key = inject->key; + } + + inline alni getNodeHeight(const Node* node) const { return node ? node->mHeight : -1; } @@ -194,17 +200,18 @@ namespace tp { if (head->exactNode(key)) { if (head->mRight && head->mLeft) { Node* min = minNode(head->mRight); - head->data = min->data; - head->mRight = removeUtil(head->mRight, min->getFindKey(head->mRight)); + auto const& newKey = min->getFindKey(head->mRight); + injectNodeInstead(head, min); + head->mRight = removeUtil(head->mRight, newKey); } else if (head->mRight) { - head->data = head->mRight->data; + injectNodeInstead(head, head->mRight); deleteNode(head->mRight); head->mRight = nullptr; mSize--; } else if (head->mLeft) { - head->data = head->mLeft->data; + injectNodeInstead(head, head->mLeft); deleteNode(head->mLeft); head->mLeft = nullptr; mSize--; @@ -337,13 +344,19 @@ namespace tp { // TODO: incomplete test if (!head->descentLeft(head->mLeft->getFindKey(head))) return head; if (head->mLeft->mParent != head) return head; + if (!head->mRight && head->mLeft->mHeight != head->mHeight - 1) return head; } if (head->mRight) { if (!head->descentRight(head->mRight->getFindKey(head))) return head; if (head->mRight->mParent != head) return head; + if (!head->mLeft && head->mRight->mHeight != head->mHeight - 1) return head; } + if (head->mLeft && head->mRight) { + if (max(head->mLeft->mHeight, head->mRight->mHeight) != head->mHeight - 1) return head; + } + int balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft); if (balance > 1 || balance < -1) return head; diff --git a/Containers/tests/AvlTest.cpp b/Containers/tests/AvlTest.cpp index 5c57729..1aada9d 100644 --- a/Containers/tests/AvlTest.cpp +++ b/Containers/tests/AvlTest.cpp @@ -26,6 +26,106 @@ TEST_DEF_STATIC(Simple) { TEST(tree.head() == nullptr); } +TEST_DEF_STATIC(Persistance) { + AvlTree, TestClass, TestAllocator> tree; + + const auto size = 1000; + + struct Item { + Item() : data(0) {} + bool presents = false; + TestClass data; + }; + + Item buff[size]; + + for (auto i : Range(size)) { + buff[i].data.setVal(i); + } + + // random load + ualni loadSize = 0; + while (loadSize < size / 2) { + ualni idx = rand() % (size - 1); + DEBUG_ASSERT(idx < size) + if (!buff[idx].presents) { + tree.insert((alni) buff[idx].data.getVal(), buff[idx].data); + loadSize++; + buff[idx].presents = true; + + TEST(tree.isValid()); + TEST(tree.size() == loadSize); + } + } + + for (auto& item : buff) { + if (item.presents) continue; + tree.insert((alni) item.data.getVal(), item.data); + loadSize++; + item.presents = true; + + TEST(tree.isValid()); + TEST(tree.size() == loadSize); + } + + TEST(tree.size() == size); + TEST(tree.maxNode(tree.head())->data.getVal() == size - 1); + TEST(tree.minNode(tree.head())->data.getVal() == 0); + + // find + for (auto item : buff) { + auto node = tree.find((alni) item.data.getVal()); + TEST(node); + if (!node) continue; + TEST(node->data.getVal() == item.data.getVal()); + } + + TEST(!tree.find(size + 1)); + TEST(!tree.find(-1)); + + // random unload + ualni unloadSize = 0; + while (unloadSize < size / 2) { + ualni idx = rand() % (size - 1); + if (buff[idx].presents) { + + tree.remove((alni) buff[idx].data.getVal()); + + unloadSize++; + buff[idx].presents = false; + + // find + for (auto item : buff) { + if (!item.presents) continue; + auto node = tree.find((alni) item.data.getVal()); + TEST(node); + if (!node) continue; + TEST(node->data.getVal() == item.data.getVal()); + } + + TEST(tree.isValid()); + TEST(tree.size() == size - unloadSize); + } + } + + for (auto& item : buff) { + if (item.presents) { + tree.remove((alni) item.data.getVal()); + unloadSize++; + item.presents = false; + + TEST(tree.isValid()); + TEST(tree.size() == size - unloadSize); + } + } + + TEST(tree.size() == 0); + TEST(tree.head() == nullptr); + TEST(tree.maxNode(tree.head()) == nullptr); + TEST(tree.minNode(tree.head()) == nullptr); +} + TEST_DEF(Avl) { testSimple(); + testPersistance(); } \ No newline at end of file diff --git a/Module/tests/Tests.cpp b/Module/tests/Tests.cpp index 3a04aa1..4e21c99 100644 --- a/Module/tests/Tests.cpp +++ b/Module/tests/Tests.cpp @@ -1,6 +1,8 @@ #include "Module.hpp" +#include "Common.hpp" + int main() { tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleBase, nullptr }; tp::ModuleManifest TestModule("Test", nullptr, nullptr, ModuleDependencies); @@ -11,5 +13,10 @@ int main() { ASSERT(tp::gEnvironment.mWidth == tp::Environment::ArchWidth::X64); + ASSERT(tp::max(2, 1) == 2); + ASSERT(tp::max(-1, 0) == 0); + ASSERT(tp::max(0, -1) == 0); + ASSERT(tp::min(0, -1) == -1); + TestModule.deinitialize(); } \ No newline at end of file