From db1a6b35ef6a6ffc9a492eb2504cdc93ebd7b7a5 Mon Sep 17 00:00:00 2001 From: IlyaShurupov Date: Mon, 8 Apr 2024 16:16:37 +0300 Subject: [PATCH] AVL Tree refactor --- Containers/CMakeLists.txt | 1 + Containers/private/AVLTree.ipp | 407 +++++++++++++++++++++++++ Containers/private/Containers.cpp | 2 - Containers/public/IntervalTree.hpp | 2 +- Containers/public/Tree.hpp | 401 +++--------------------- Containers/tests/IntervalTreeTests.cpp | 9 +- Containers/tests/TreeTest.cpp | 62 +++- TODO | 3 - 8 files changed, 511 insertions(+), 376 deletions(-) create mode 100644 Containers/private/AVLTree.ipp diff --git a/Containers/CMakeLists.txt b/Containers/CMakeLists.txt index bb9bb34..2b17561 100644 --- a/Containers/CMakeLists.txt +++ b/Containers/CMakeLists.txt @@ -5,6 +5,7 @@ file(GLOB SOURCES "./private/*.cpp") file(GLOB HEADERS "./public/*.hpp") add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) target_include_directories(${PROJECT_NAME} PUBLIC ./public/) +target_include_directories(${PROJECT_NAME} PUBLIC ./private/) target_link_libraries(${PROJECT_NAME} PUBLIC Modules) ### -------------------------- Tests -------------------------- ### diff --git a/Containers/private/AVLTree.ipp b/Containers/private/AVLTree.ipp new file mode 100644 index 0000000..40a64d8 --- /dev/null +++ b/Containers/private/AVLTree.ipp @@ -0,0 +1,407 @@ +// #pragma once + +// #include "Tree.hpp" + +template +tp::AvlTree::~AvlTree() { + removeAll(); +} + +template +void tp::AvlTree::deleteNode(Node* node) { + node->~Node(); + mAlloc.deallocate(node); + mSize--; +} + +template +auto tp::AvlTree::newNode(KeyArg key, DataArg data) -> Node* { + mSize++; + auto node = new (mAlloc.allocate(sizeof(Node))) Node(key, data); + node->mLeft = &gNullNode; + node->mRight = &gNullNode; + return node; +} + +template +[[nodiscard]] tp::ualni tp::AvlTree::size() const { + return mSize; +} + +template +auto tp::AvlTree::head() const -> Node* { + return this->mRoot; +} + +template +void tp::AvlTree::insert(KeyArg key, DataArg data) { + if (!mRoot) { + mRoot = newNode(key, data); + return; + } + + if (auto parent = findInsertParent(mRoot, key)) { + restoreInvariants(insertNode(parent, key, data)); + } +} + +template +void tp::AvlTree::remove(KeyArg key) { + + auto node = findSubTree(mRoot, key); + + if (!node) return; + + if (node->mRight != &gNullNode && node->mLeft != &gNullNode) { + Node* min = minNode(node->mRight); + injectNodeInstead(node, min); + std::swap(node, min); + // auto const& newKey = min->key.getFindKey(head->mRight); + node = findSubTree(node->mRight, key); + DEBUG_ASSERT(node); + } + + if (node->mRight != &gNullNode) { + if (node->mParent) { + if (node->mParent->mLeft == node) node->mParent->mLeft = node->mRight; + else node->mParent->mRight = node->mRight; + } + node->mRight->mParent = node->mParent; + auto delNode = node; + node = node->mRight; + deleteNode(delNode); + + } else if (node->mLeft != &gNullNode) { + if (node->mParent) { + if (node->mParent->mLeft == node) node->mParent->mLeft = node->mLeft; + else node->mParent->mRight = node->mLeft; + } + node->mLeft->mParent = node->mParent; + auto delNode = node; + node = node->mLeft; + deleteNode(delNode); + + } else { + if (node->mParent) { + if (node->mParent->mLeft == node) node->mParent->mLeft = &gNullNode; + else node->mParent->mRight = &gNullNode; + } + + auto delNode = node; + node = node->mParent; + deleteNode(delNode); + } + + restoreInvariants(node); + + if (mRoot) mRoot->mParent = nullptr; +} + +template +auto tp::AvlTree::maxNode(Node* head) const -> Node* { + if (!head) return nullptr; + while (head->mRight != &gNullNode) { + head = head->mRight; + } + return head; +} + +template +auto tp::AvlTree::minNode(Node* head) const -> Node* { + if (!head) return nullptr; + while (head->mLeft != &gNullNode) { + head = head->mLeft; + } + return head; +} + +template +auto tp::AvlTree::find(KeyArg key) const -> Node* { + return findSubTree(mRoot, key); +} + +template +auto tp::AvlTree::findSubTree(Node* iter, KeyArg key) const -> Node* { + while (true) { + if (iter == &gNullNode) return nullptr; + if (iter->key.exactNode(key)) return iter; + if (iter->key.descentRight(key)) { + // key = iter->key.keyInRightSubtree(key); + iter = iter->mRight; + } else { + // key = iter->key.keyInLeftSubtree(key); + iter = iter->mLeft; + } + } +} + +template +auto tp::AvlTree::findLessOrEq(KeyArg key) const -> Node* { + Node* iter = mRoot; + while (true) { + if (iter == &gNullNode) return nullptr; + if (iter->key.exactNode(key)) return iter; + if (iter->key.descentRight(key)) { + if (iter->mRight) { + // key = iter->key.keyInRightSubtree(key); + iter = iter->mRight; + } else { + return iter; + } + } else { + if (iter->mLeft) { + // key = iter->key.keyInLeftSubtree(key); + iter = iter->mLeft; + } else { + return iter; + } + } + } +} + +template +bool tp::AvlTree::isValid() { + return findInvalidNode(head()) == nullptr; +} + +template +void tp::AvlTree::removeAll() { + if (!mRoot) return; + removeAllUtil(mRoot); + mRoot = nullptr; + mSize = 0; +} + +template +void tp::AvlTree::removeAllUtil(Node* node) { + if (node->mLeft != &gNullNode) removeAllUtil(node->mLeft); + if (node->mRight != &gNullNode) removeAllUtil(node->mRight); + deleteNode(node); +} + +template +auto tp::AvlTree::findInvalidNode(const Node* head) const -> const Node* { + // checks invariants of AVL tree + // returns first invalid node + if (!head || head == &gNullNode) return nullptr; + + if (head->mLeft != &gNullNode) { + // TODO: incomplete test + if (head->key.descentRight(head->mLeft->key.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 != &gNullNode) { + if (!head->key.descentRight(head->mRight->key.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 != &gNullNode && head->mRight != &gNullNode) { + if (max(head->mLeft->mHeight, head->mRight->mHeight) != head->mHeight - 1) { + return head; + } + } + + int balance = head->mRight->mHeight - head->mLeft->mHeight; + + if (balance > 1 || balance < -1) { + return head; + } + + const Node* ret = findInvalidNode(head->mRight); + + if (ret) { + return ret; + } + + return findInvalidNode(head->mLeft); +} + +template +auto tp::AvlTree::rotateLeft(Node* pivot) -> Node* { + // returns new head + DEBUG_ASSERT(pivot); + + Node* const head = pivot; + Node* const right = pivot->mRight; + Node* const right_left = right->mLeft; + Node* const parent = pivot->mParent; + + // parents + if (right_left != &gNullNode) right_left->mParent = head; + head->mParent = right; + right->mParent = parent; + + // children + head->mRight = right_left; + right->mLeft = head; + + // heights + head->mHeight = 1 + max(head->mLeft->mHeight, head->mRight->mHeight); + right->mHeight = 1 + max(right->mLeft->mHeight, right->mRight->mHeight); + + // cache + head->key.updateNodeCache(head); + right->key.updateNodeCache(right); + + return right; +} + +template +auto tp::AvlTree::rotateRight(Node* pivot) -> Node* { + // returns new head + DEBUG_ASSERT(pivot); + + Node* const head = pivot; + Node* const left = pivot->mLeft; + Node* const left_right = left->mRight; + Node* const parent = pivot->mParent; + + // parents + if (left_right != &gNullNode) left_right->mParent = head; + head->mParent = left; + left->mParent = parent; + + // children + head->mLeft = left_right; + left->mRight = head; + + // heights + head->mHeight = 1 + max(head->mLeft->mHeight, head->mRight->mHeight); + left->mHeight = 1 + max(left->mLeft->mHeight, left->mRight->mHeight); + + // cache + head->key.updateNodeCache(head); + left->key.updateNodeCache(left); + + return left; +} + +template +void tp::AvlTree::restoreInvariants(Node* head) { + if (!head) mRoot = nullptr; + + while (head) { + Node* headParent = head->mParent; + Node** parentHeadLink = nullptr; + + if (headParent) parentHeadLink = headParent->mLeft == head ? &headParent->mLeft : &headParent->mRight; + + head->mHeight = 1 + max(head->mRight->mHeight, head->mLeft->mHeight); + const alni balance = head->mRight->mHeight - head->mLeft->mHeight; + + if (balance < -1) { + if (head->mLeft->mLeft->mHeight >= head->mLeft->mRight->mHeight) { + head = rotateRight(head); + } else { + head->mLeft = rotateLeft(head->mLeft); + head = rotateRight(head); + } + } else if (balance > 1) { + if (head->mRight->mRight->mHeight >= head->mRight->mLeft->mHeight) { + head = rotateLeft(head); + } else { + head->mRight = rotateRight(head->mRight); + head = rotateLeft(head); + } + } + + head->key.updateNodeCache(head); + if (headParent) *parentHeadLink = head; + + mRoot = head; + head = headParent; + } + + if (mRoot) mRoot->mParent = nullptr; +} + +template +auto tp::AvlTree::insertNode(Node* head, KeyArg key, DataArg data) -> Node* { + Node* insertedNode = newNode(key, data); + + if (head->key.descentRight(key)) { + head->mRight = insertedNode; + } else { + head->mLeft = insertedNode; + } + + insertedNode->mParent = head; + return insertedNode; +} + +template +auto tp::AvlTree::findInsertParent(Node* head, KeyArg key) -> Node* { + while (true) { + if (head->key.exactNode(key)) { + return nullptr; + } + if (head->key.descentRight(key)) { + if (head->mRight == &gNullNode) { + return head; + } + head = head->mRight; + } else { + if (head->mLeft == &gNullNode) { + return head; + } + head = head->mLeft; + } + } +} + +template +void tp::AvlTree::injectNodeInstead(Node* target, Node* from) { + // swaps pointers of two nodes to preserve data location in the memory instead of swapping data directly + + // 'target' always has two nodes + // 'from' has only one child on the right on no child at all + // 'from' can be right child of the 'target' + // 'target' can or can not have parent + // 'target' can be left or right child of parent + + // from is not a child of target + Node* targetParent = target->mParent; + Node* targetLeft = target->mLeft; + Node* targetRight = target->mRight; + + bool special = target->mRight == from; + + // update parent + if (targetParent) { + if (targetParent->mRight == target) targetParent->mRight = from; + else targetParent->mLeft = from; + } + + // clone 'from' to 'target' + target->mParent = special ? from : from->mParent; + target->mLeft = &gNullNode; + target->mRight = from->mRight; + if (!special) target->mParent->mLeft = target; + + // if (target->mLeft) target->mLeft->mParent = target; + if (target->mRight) target->mRight->mParent = target; + + // clone 'target' to 'from' + from->mParent = targetParent; + from->mLeft = targetLeft; + from->mRight = special ? target : targetRight; + from->mLeft->mParent = from; + from->mRight->mParent = from; + + std::swap(from->mHeight, target->mHeight); +} diff --git a/Containers/private/Containers.cpp b/Containers/private/Containers.cpp index 20ed850..6dbda3d 100644 --- a/Containers/private/Containers.cpp +++ b/Containers/private/Containers.cpp @@ -4,8 +4,6 @@ #include namespace tp { - void* DefaultAllocator::allocate(ualni size) { return malloc(size); } - void DefaultAllocator::deallocate(void* p) { free(p); } } \ No newline at end of file diff --git a/Containers/public/IntervalTree.hpp b/Containers/public/IntervalTree.hpp index f864680..f877ed2 100644 --- a/Containers/public/IntervalTree.hpp +++ b/Containers/public/IntervalTree.hpp @@ -60,7 +60,7 @@ namespace tp { private: template void forEachIntersectionUtil(const Node* node, tType start, tType end, tFunctor functor, ualni& debug) const { - if (node == nullptr) return; + if (node->mHeight == -1) return; // sentinel node (null node) debug++; diff --git a/Containers/public/Tree.hpp b/Containers/public/Tree.hpp index c42a234..9cf702c 100644 --- a/Containers/public/Tree.hpp +++ b/Containers/public/Tree.hpp @@ -38,145 +38,42 @@ namespace tp { class Node { friend AvlTree; + public: + Node() = default; + private: Node(KeyArg aKey, DataArg aData) : key(aKey), data(aData) {} public: - Data data; - Key key; + alni mHeight = -1; Node* mLeft = nullptr; Node* mRight = nullptr; Node* mParent = nullptr; - ualni mHeight = 0; + + Data data; + Key key; }; public: AvlTree() = default; - ~AvlTree() { removeAll(); } + ~AvlTree(); - [[nodiscard]] ualni size() const { return mSize; } + ualni size() const; + Node* head() const; - Node* head() const { return this->mRoot; } + void insert(KeyArg key, DataArg data); + void remove(KeyArg key); + void removeAll(); - void insert(KeyArg key, DataArg data) { - mRoot = insertUtil(mRoot, key, data); - mRoot->mParent = nullptr; - } + Node* maxNode(Node* head) const; + Node* minNode(Node* head) const; - void remove(KeyArg key) { - mRoot = removeUtil(mRoot, key); - if (mRoot) mRoot->mParent = nullptr; - } - - Node* maxNode(Node* head) const { - if (!head) return nullptr; - while (head->mRight != nullptr) { - head = head->mRight; - } - return head; - } - - Node* minNode(Node* head) const { - if (!head) return nullptr; - while (head->mLeft != nullptr) { - head = head->mLeft; - } - return head; - } - - Node* find(KeyArg key) const { - Node* iter = mRoot; - while (true) { - if (!iter) return nullptr; - if (iter->key.exactNode(key)) return iter; - if (iter->key.descentRight(key)) { - key = iter->key.keyInRightSubtree(key); - iter = iter->mRight; - } else { - key = iter->key.keyInLeftSubtree(key); - iter = iter->mLeft; - } - } - } - - Node* findLessOrEq(KeyArg key) const { - Node* iter = mRoot; - while (true) { - if (!iter) return nullptr; - if (iter->key.exactNode(key)) return iter; - if (iter->key.descentRight(key)) { - if (iter->mRight) { - key = iter->key.keyInRightSubtree(key); - iter = iter->mRight; - } else { - return iter; - } - } else { - if (iter->mLeft) { - key = iter->key.keyInLeftSubtree(key); - iter = iter->mLeft; - } else { - return iter; - } - } - } - } - - // checks invariants of AVL tree - // returns first invalid node - const Node* findInvalidNode(const Node* head) const { - if (head == nullptr) return nullptr; - - if (head->mLeft) { - // TODO: incomplete test - if (head->key.descentRight(head->mLeft->key.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->key.descentRight(head->mRight->key.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; - } - - const Node* ret = findInvalidNode(head->mRight); - - if (ret) { - return ret; - } - - return findInvalidNode(head->mLeft); - } - - bool isValid() { return findInvalidNode(head()) == nullptr; } + Node* find(KeyArg key) const; + Node* findSubTree(Node* iter, KeyArg key) const; + Node* findLessOrEq(KeyArg key) const; template void traverse(Node* node, bool after, tFunctor functor) { @@ -186,259 +83,35 @@ namespace tp { if (after) functor(node); } - void removeAll() { - if (!mRoot) return; - removeUtil(mRoot); - mRoot = nullptr; - mSize = 0; - } - - void removeUtil(Node* node) { - if (node->mLeft) removeUtil(node->mLeft); - if (node->mRight) removeUtil(node->mRight); - deleteNode(node); - } - - public: - template - void archiveWrite(tArchiver& file) const { - FAIL("not implemented") - } - - template - void archiveRead(tArchiver&) { - FAIL("not implemented") - } + auto findInvalidNode(const Node* head) const -> const Node*; + bool isValid(); private: - inline void deleteNode(Node* node) { - node->~Node(); - mAlloc.deallocate(node); - mSize--; - } + inline void deleteNode(Node* node); + inline auto newNode(KeyArg key, DataArg data) -> Node*; - inline Node* newNode(KeyArg key, DataArg data) { return new (mAlloc.allocate(sizeof(Node))) Node(key, data); } + inline auto rotateLeft(Node* pivot) -> Node*; + inline auto rotateRight(Node* pivot) -> Node*; - // swaps pointers of two nodes to preserve data location in the memory instead of swapping data directly - inline void injectNodeInstead(Node* target, Node* from) { - // 'target' always has two nodes - // 'from' has only one child on the right on no child at all - // 'from' can be right child of the 'target' - // 'target' can or can not have parent - // 'target' can be left or right child of parent + inline void restoreInvariants(Node* head); - // from is not a child of target - Node* targetParent = target->mParent; - Node* targetLeft = target->mLeft; - Node* targetRight = target->mRight; + inline auto insertNode(Node* head, KeyArg key, DataArg data) -> Node*; + inline auto findInsertParent(Node* head, KeyArg key) -> Node*; - bool special = target->mRight == from; + inline void injectNodeInstead(Node* target, Node* from); - // update parent - if (targetParent) { - if (targetParent->mRight == target) targetParent->mRight = from; - else targetParent->mLeft = from; - } - - // clone 'from' to 'target' - target->mParent = special ? from : from->mParent; - target->mLeft = nullptr; - target->mRight = from->mRight; - if (!special) target->mParent->mLeft = target; - - // if (target->mLeft) target->mLeft->mParent = target; - if (target->mRight) target->mRight->mParent = target; - - // clone 'target' to 'from' - from->mParent = targetParent; - from->mLeft = targetLeft; - from->mRight = special ? target : targetRight; - from->mLeft->mParent = from; - from->mRight->mParent = from; - - std::swap(from->mHeight, target->mHeight); - } - - inline alni getNodeHeight(const Node* node) const { return node ? node->mHeight : -1; } - - // returns new head - inline Node* rotateLeft(Node* pivot) { - DEBUG_ASSERT(pivot); - - Node* const head = pivot; - Node* const right = pivot->mRight; - Node* const right_left = right->mLeft; - Node* const parent = pivot->mParent; - - // parents - if (right_left) right_left->mParent = head; - head->mParent = right; - right->mParent = parent; - - // children - head->mRight = right_left; - right->mLeft = head; - - // heights - head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight)); - right->mHeight = 1 + max(getNodeHeight(right->mLeft), getNodeHeight(right->mRight)); - - // cache - head->key.updateNodeCache(head); - right->key.updateNodeCache(right); - - return right; - } - - inline Node* rotateRight(Node* pivot) { - DEBUG_ASSERT(pivot); - - Node* const head = pivot; - Node* const left = pivot->mLeft; - Node* const left_right = left->mRight; - Node* const parent = pivot->mParent; - - // parents - if (left_right) left_right->mParent = head; - head->mParent = left; - left->mParent = parent; - - // children - head->mLeft = left_right; - left->mRight = head; - - // heights - head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight)); - left->mHeight = 1 + max(getNodeHeight(left->mLeft), getNodeHeight(left->mRight)); - - // cache - head->key.updateNodeCache(head); - left->key.updateNodeCache(left); - - return left; - } - - // recursively returns valid isLeft or isRight child or root - Node* insertUtil(Node* head, KeyArg key, DataArg data) { - - Node* insertedNode; - - if (head == nullptr) { - mSize++; - Node* out = newNode(key, data); - out->key.updateNodeCache(out); - return out; - } else if (head->key.exactNode(key)) { - return head; - } else if (head->key.descentRight(key)) { - insertedNode = insertUtil(head->mRight, head->key.keyInRightSubtree(key), data); - head->mRight = insertedNode; - insertedNode->mParent = head; - } else { - insertedNode = insertUtil(head->mLeft, head->key.keyInLeftSubtree(key), data); - head->mLeft = insertedNode; - insertedNode->mParent = head; - } - - // update height - head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft)); - - alni balance = alni(getNodeHeight(head->mRight) - getNodeHeight(head->mLeft)); - - if (balance > 1) { - if (head->mRight->key.descentRight(head->key.keyInRightSubtree(key))) { - return rotateLeft(head); - } else { - head->mRight = rotateRight(head->mRight); - return rotateLeft(head); - } - } else if (balance < -1) { - if (!head->mLeft->key.descentRight(head->key.keyInLeftSubtree(key))) { - return rotateRight(head); - } else { - head->mLeft = rotateLeft(head->mLeft); - return rotateRight(head); - } - } - - head->key.updateNodeCache(head); - - return head; - } - - Node* removeUtil(Node* head, KeyArg key) { - if (head == nullptr) return head; - - if (head->key.exactNode(key)) { - if (head->mRight && head->mLeft) { - Node* min = minNode(head->mRight); - injectNodeInstead(head, min); - std::swap(head, min); - // auto const& newKey = min->key.getFindKey(head->mRight); - head->mRight = removeUtil(head->mRight, key); - } else if (head->mRight) { - - if (head->mParent) { - if (head->mParent->mLeft == head) head->mParent->mLeft = head->mRight; - else head->mParent->mRight = head->mRight; - } - - head->mRight->mParent = head->mParent; - - auto delNode = head; - head = head->mRight; - deleteNode(delNode); - } else if (head->mLeft) { - - if (head->mParent) { - if (head->mParent->mLeft == head) head->mParent->mLeft = head->mLeft; - else head->mParent->mRight = head->mLeft; - } - - head->mLeft->mParent = head->mParent; - - auto delNode = head; - head = head->mLeft; - deleteNode(delNode); - } else { - deleteNode(head); - head = nullptr; - } - } else if (head->key.descentRight(key)) { - head->mRight = removeUtil(head->mRight, head->key.keyInRightSubtree(key)); - } else { - head->mLeft = removeUtil(head->mLeft, head->key.keyInLeftSubtree(key)); - } - - if (head == nullptr) return head; - - head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft)); - alni balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft); - - if (balance < -1) { - if (getNodeHeight(head->mLeft->mLeft) >= getNodeHeight(head->mLeft->mRight)) { - return rotateRight(head); - } else { - head->mLeft = rotateLeft(head->mLeft); - return rotateRight(head); - } - } else if (balance > 1) { - if (getNodeHeight(head->mRight->mRight) >= getNodeHeight(head->mRight->mLeft)) { - return rotateLeft(head); - } else { - head->mRight = rotateRight(head->mRight); - return rotateLeft(head); - } - } - - head->key.updateNodeCache(head); - - return head; - } + void removeAllUtil(Node* node); private: Node* mRoot = nullptr; ualni mSize = 0; Allocator mAlloc; + + static Node gNullNode; }; -} \ No newline at end of file + + template + AvlTree::Node AvlTree::gNullNode; +} + +#include "AVLTree.ipp" diff --git a/Containers/tests/IntervalTreeTests.cpp b/Containers/tests/IntervalTreeTests.cpp index c139787..fdf725e 100644 --- a/Containers/tests/IntervalTreeTests.cpp +++ b/Containers/tests/IntervalTreeTests.cpp @@ -51,7 +51,7 @@ SUITE(IntervalTree) { TEST(FunctionalityScale) { const int NUM_TEST_INTERVALS = 1000; - const halnf SPAN = 100; + const halnf SPAN = 1000; Buffer pool; IntervalTree intervalTree; @@ -72,9 +72,9 @@ SUITE(IntervalTree) { idx++; } - intervalTree.forEachIntersection(testInterval->start, testInterval->end, [&](alni start, ualni end, ualni data) { - result.append(data); - }); + intervalTree.forEachIntersection( + testInterval->start, testInterval->end, [&](alni start, ualni end, ualni data) { result.append(data); } + ); CHECK(correct.size() == result.size()); @@ -270,5 +270,4 @@ SUITE(IntervalTree) { CHECK(makeQuery(0, 1).found(2)); } - } \ No newline at end of file diff --git a/Containers/tests/TreeTest.cpp b/Containers/tests/TreeTest.cpp index c7135f5..353bcf8 100644 --- a/Containers/tests/TreeTest.cpp +++ b/Containers/tests/TreeTest.cpp @@ -1,5 +1,7 @@ +#include #include "Tests.hpp" #include "Tree.hpp" +#include "Timing.hpp" using namespace tp; @@ -24,7 +26,7 @@ SUITE(AvlTree) { TEST(Persistance) { AvlTree, TestClass, TestAllocator> tree; - const auto size = 10; + const auto size = 1000; struct Item { Item() : @@ -122,4 +124,62 @@ SUITE(AvlTree) { CHECK(tree.maxNode(tree.head()) == nullptr); CHECK(tree.minNode(tree.head()) == nullptr); } + + TEST(Speed) { + AvlTree, TestClass, TestAllocator> tree; + + const auto size = 1000000; + + struct Item { + Item() : + data(0) {} + bool presents = false; + TestClass data; + }; + + Item* buff = new Item[size]; + + for (auto i : Range(size)) { + buff[i].data.setVal(i); + } + + Timer insertTime; + + ualni loadSize = 0; + while (loadSize < size / 2) { + auto idx = ualni(randomFloat() * (size - 1)); + if (!buff[idx].presents) { + tree.insert((alni) buff[idx].data.getVal(), buff[idx].data); + loadSize++; + buff[idx].presents = true; + } + } + + std::cout << "AVL Tree Insert Speed " << (double) loadSize / (double) insertTime.timePassed() << "\n"; + + Timer removeTime; + + ualni unloadSize = 0; + while (unloadSize < size / 2) { + auto idx = ualni(randomFloat() * (size - 1)); + if (buff[idx].presents) { + tree.remove((alni) buff[idx].data.getVal()); + unloadSize++; + buff[idx].presents = false; + } + } + + for (auto idx = 0; idx < size; idx++) { + auto& item = buff[idx]; + if (item.presents) { + tree.remove((alni) item.data.getVal()); + unloadSize++; + item.presents = false; + } + } + + std::cout << "AVL Tree Remove Speed " << (double) unloadSize / (double) removeTime.timePassed() << "\n"; + + delete[] buff; + } } \ No newline at end of file diff --git a/TODO b/TODO index 6bc6ec7..1539bf7 100644 --- a/TODO +++ b/TODO @@ -13,13 +13,10 @@ Modules: Remove all static variable into module's data Containers: - Avl tree add sentenels Add variant size buffer Buffer add resize function Buffer improvements - Write Traverse Avl test Add check copy times - AVL dont copy data just swap Storage: Reconsider