diff --git a/src/BinaryTree/BinaryTree.c b/src/BinaryTree/BinaryTree.c new file mode 100644 index 0000000..b044c97 --- /dev/null +++ b/src/BinaryTree/BinaryTree.c @@ -0,0 +1,288 @@ +#include "BinaryTree.h" +#include +#include + +enum BinaryTreeNodePosition { + BINARYTREENODEPOSITION_SELF, + BINARYTREENODEPOSITION_LEFT, + BINARYTREENODEPOSITION_RIGHT, +}; + +typedef struct BinaryTreeWalker_s { + BinaryTreeNode* node; + enum BinaryTreeNodePosition position; +} BinaryTreeWalker; + +static size_t _TreeNodeSize(BinaryTree* tree) { + return sizeof(BinaryTreeNode) + tree->value_size; +} + +static inline size_t _DepthOf(BinaryTreeNode* node) +{ + return node == NULL ? 0 : node->depth; +} + +static BinaryTreeNode* _CreateNode(BinaryTree* tree, void* value) { + BinaryTreeNode* node = Allocator_Allocate(tree->allocator, _TreeNodeSize(tree)); + if (node != NULL) { + memcpy(node->value, value, tree->value_size); + node->depth = 1; + node->left = NULL; + node-> right = NULL; + } + + return node; +} + +int BinaryTree_Create(BinaryTree* target, BinaryTreeComparator compare, void* xdata, size_t value_size, allocator_t* allocator) { + target->root = NULL; + target->compare = compare; + target->xdata = xdata; + target->allocator = allocator; + target->value_size = value_size; + + int array_code = DynamicArray_Create(&target->walker_stack, sizeof(BinaryTreeWalker), 8, allocator); + if (array_code) return array_code; + + return EXIT_SUCCESS; +} + +static void _UpdateDepth(BinaryTreeNode* node) { + size_t depth_left = 0, depth_right = 0; + + if (node->left != NULL) { + depth_left = node->left->depth; + } + if (node->right != NULL) { + depth_right = node->right->depth; + } + + if (depth_left > depth_right) { + node->depth = depth_left; + } else { + node->depth = depth_right; + } + + node->depth += 1; +} + +static void _UpdateDepths(DynamicArray* parents) { + for (size_t i = parents->reserved - 1; i != SIZE_MAX; i--) { + BinaryTreeWalker* walker = DynamicArray_GetPointer(parents, i); + _UpdateDepth(walker->node); + } +} + +int _Insert(BinaryTree* tree, BinaryTreeNode* node) { + BinaryTreeNode** parent = &tree->root; + tree->walker_stack.reserved = 0; + + if (*parent == NULL) { + *parent = node; + } + + while ((*parent) != node) { + BinaryTreeWalker* walker; + DynamicArray_AppendEmpty(&tree->walker_stack, (void**) &walker); + // Can't be NULL because the size was just increased + + walker->node = *parent; + + int comparison = tree->compare(node->value, (*parent)->value, tree->xdata); + + if (comparison == 0) { + // equal + return EXIT_FAILURE; + } else if (comparison < 0) { + // less than + if ((*parent)->left == NULL) { + (*parent)->left = node; + } + + parent = &(*parent)->left; + walker->position = BINARYTREENODEPOSITION_LEFT; + } else { + // greater than + if ((*parent)->right == NULL) { + (*parent)->right = node; + } + + parent = &(*parent)->right; + walker->position = BINARYTREENODEPOSITION_RIGHT; + } + } + + _UpdateDepths(&tree->walker_stack); + _UpdateDepth(node); + + return EXIT_SUCCESS; +} + +int _GrowParentStack(BinaryTree* tree) +{ + size_t depth = _DepthOf(tree->root); + if (tree->walker_stack.capacity < depth + 1) { + return DynamicArray_Resize(&tree->walker_stack, tree->walker_stack.capacity * 2); + } + + return EXIT_SUCCESS; +} + +// right is heavier, shift it to the left +static void _RotateLeft(BinaryTreeNode** node) +{ + BinaryTreeNode* right = (*node)->right; + + (*node)->right = right->left; + right->left = *node; + *node = right; +} + +// left is heavier, shift it to the right +static void _RotateRight(BinaryTreeNode** node) +{ + BinaryTreeNode* left = (*node)->left; + + (*node)->left = left->right; + left->right = *node; + *node = left; +} + +static void _RebalanceNode(BinaryTreeNode** node) +{ + size_t left_depth = _DepthOf(node[0]->left); + size_t right_depth = _DepthOf(node[0]->right); + + if (left_depth < right_depth && right_depth - left_depth > 1) { + _RotateLeft(node); + _UpdateDepth((*node)->left); // old node + _UpdateDepth(*node); // rotated node + } else if (right_depth < left_depth && left_depth - right_depth > 1) { + _RotateRight(node); + _UpdateDepth((*node)->right); // old node + _UpdateDepth(*node); // rotated node + } +} + +static void _RebalanceRoot(BinaryTree* tree) +{ + _RebalanceNode(&tree->root); +} + +static void _Rebalance(BinaryTree* tree) +{ + for (size_t i = tree->walker_stack.reserved - 1; i + 1 != 0; i--) { + BinaryTreeWalker* walker = DynamicArray_GetPointer( + &tree->walker_stack, + i + ); + BinaryTreeNode** nodep = NULL; + switch (walker->position) { + case BINARYTREENODEPOSITION_LEFT: + nodep = &walker->node->left; + break; + + case BINARYTREENODEPOSITION_RIGHT: + nodep = &walker->node->right; + break; + + case BINARYTREENODEPOSITION_SELF: + // not gonna happen + break; + } + + _RebalanceNode(nodep); + } + + + _RebalanceRoot(tree); +} + +static int _BinaryTree_DestroyNode(BinaryTree* tree, BinaryTreeNode* node) +{ + Allocator_Free(tree->allocator, node, sizeof(*node) + tree->value_size); + + return EXIT_SUCCESS; +} + +int BinaryTree_Insert(BinaryTree* tree, void* value) { + if (tree == NULL) return EDESTADDRREQ; + if (value == NULL) return EINVAL; + + if (_GrowParentStack(tree)) { + return ENOMEM; + } + + BinaryTreeNode* new_node = _CreateNode(tree, value); + if (new_node == NULL) return ENOMEM; + + if (_Insert(tree, new_node)) { + _BinaryTree_DestroyNode(tree, new_node); + return EXIT_FAILURE; + } + _Rebalance(tree); + + return EXIT_SUCCESS; +} + +int BinaryTree_ForEachPostOrder(BinaryTree* tree, BinaryTreeNodeFunction action, void* context) +{ + tree->walker_stack.reserved = 0; + + { + BinaryTreeWalker* root_walker; + DynamicArray_AppendEmpty(&tree->walker_stack, (void**) &root_walker); + + root_walker->node = tree->root; + root_walker->position = BINARYTREENODEPOSITION_LEFT; + } + + while (tree->walker_stack.reserved != 0) { + BinaryTreeWalker* current = DynamicArray_GetPointer(&tree->walker_stack, tree->walker_stack.reserved - 1); + if (current->node == NULL) { + tree->walker_stack.reserved--; // remove last item + continue; + } + + BinaryTreeNode* append = NULL; + + switch (current->position) { + + case BINARYTREENODEPOSITION_SELF: + { + int error = action(context, current->node); + if (error) return ECANCELED; + + tree->walker_stack.reserved--; + break; + } + + case BINARYTREENODEPOSITION_LEFT: + current->position = BINARYTREENODEPOSITION_RIGHT; + append = current->node->left; + break; + + case BINARYTREENODEPOSITION_RIGHT: + current->position = BINARYTREENODEPOSITION_SELF; + append = current->node->right; + break; + + } + + if (append != NULL) { + BinaryTreeWalker* new_walker; + DynamicArray_AppendEmpty(&tree->walker_stack, (void**) &new_walker); + new_walker->node = append; + new_walker->position = BINARYTREENODEPOSITION_LEFT; + } + } + + return EXIT_SUCCESS; +} + +void BinaryTree_Destroy(BinaryTree* tree) +{ + BinaryTree_ForEachPostOrder(tree, (BinaryTreeNodeFunction) _BinaryTree_DestroyNode, tree); + DynamicArray_Destroy(&tree->walker_stack); + return; +} diff --git a/src/BinaryTree/BinaryTree.h b/src/BinaryTree/BinaryTree.h new file mode 100644 index 0000000..72fbe18 --- /dev/null +++ b/src/BinaryTree/BinaryTree.h @@ -0,0 +1,40 @@ +#ifndef UTILITIEC_BINARYTREE_H +#define UTILITIEC_BINARYTREE_H + +#include "../allocator-interface/allocator-interface.h" +#include "../dynamicarray/dynamicarray.h" + +typedef struct BinaryTreeNode_s { + struct BinaryTreeNode_s* left; + struct BinaryTreeNode_s* right; + + size_t depth; + + char value[]; +} BinaryTreeNode; + +typedef int (*BinaryTreeComparator) (void* this, void* other, void* xdata); + +typedef struct BinaryTree_s { + BinaryTreeNode* root; + + DynamicArray walker_stack; + + size_t value_size; + BinaryTreeComparator compare; + void* xdata; + + allocator_t* allocator; +} BinaryTree; + +int BinaryTree_Create(BinaryTree* target, BinaryTreeComparator compare, void* xdata, size_t value_size, allocator_t* allocator); +void BinaryTree_Destroy(BinaryTree* tree); + +int BinaryTree_Insert(BinaryTree* tree, void* value); +int BinaryTree_Remove(BinaryTree* tree, void* value); + +typedef int (*BinaryTreeNodeFunction) (void* context, BinaryTreeNode* node); + +int BinaryTree_ForEachPostOrder(BinaryTree* tree, BinaryTreeNodeFunction action, void* context); + +#endif diff --git a/src/BinaryTree/CMakeLists.txt b/src/BinaryTree/CMakeLists.txt new file mode 100644 index 0000000..4898ac9 --- /dev/null +++ b/src/BinaryTree/CMakeLists.txt @@ -0,0 +1 @@ +add_library(BinaryTree STATIC BinaryTree.c) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 30aeedc..0c11715 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(./allocator-interface) add_subdirectory(./FixedBuffer) add_subdirectory(./dynamicbuffer) add_subdirectory(./FreeList) +add_subdirectory(./BinaryTree) add_subdirectory(./hashmap) add_subdirectory(./QuadTree) add_subdirectory(./threading) diff --git a/tests/BinaryTree.test.c b/tests/BinaryTree.test.c new file mode 100644 index 0000000..0356153 --- /dev/null +++ b/tests/BinaryTree.test.c @@ -0,0 +1,126 @@ +#include +#include + +#include "../src/BinaryTree/BinaryTree.h" + +int doublecomparator(double* this, double* other, void* xdata) +{ + assert(xdata == (void*) 0xDEADBEEF); + if (*this > *other) { + return 1; + } else if (*this < *other) { + return -1; + } + + return 0; +} + +void testLifetime(void) +{ + allocator_t allocator; + Allocator_CreateSystemAllocator(&allocator); + BinaryTree tree; + + assert(EXIT_SUCCESS == BinaryTree_Create( + &tree, + (BinaryTreeComparator) doublecomparator, + (void*) 0xDEADBEEF, + sizeof(double), + &allocator) + ); + BinaryTree_Destroy(&tree); + + assert(allocator.reserved == 0); + + return; +} + +void testInsert(void) +{ + allocator_t allocator; + Allocator_CreateSystemAllocator(&allocator); + BinaryTree tree; + + assert(EXIT_SUCCESS == BinaryTree_Create( + &tree, + (BinaryTreeComparator) doublecomparator, + (void*) 0xDEADBEEF, + sizeof(double), + &allocator) + ); + + double value = 5; + + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + assert(tree.root != NULL); + + value = 3; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 2; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 4; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + + value = 7; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 6; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 8; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + + BinaryTree_Destroy(&tree); + assert(allocator.reserved == 0); + + return; +} + +void testEqual(void) +{ + allocator_t allocator; + Allocator_CreateSystemAllocator(&allocator); + BinaryTree tree; + + assert(EXIT_SUCCESS == BinaryTree_Create( + &tree, + (BinaryTreeComparator) doublecomparator, + (void*) 0xDEADBEEF, + sizeof(double), + &allocator) + ); + + double value = 5; + + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + assert(tree.root != NULL); + + value = 3; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 2; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 4; + assert(EXIT_SUCCESS == BinaryTree_Insert(&tree, &value)); + + value = 5; + assert(EXIT_FAILURE == BinaryTree_Insert(&tree, &value)); + + BinaryTree_Destroy(&tree); + assert(allocator.reserved == 0); + + return; +} + +int main() +{ + testLifetime(); + testInsert(); + testEqual(); + return 0; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 70dd5f3..881b58c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -36,3 +36,11 @@ target_link_libraries(regex-test StringView utf8 ) + +add_executable(BinaryTree-test BinaryTree.test.c) +target_link_libraries(BinaryTree-test + BinaryTree + dynamicarray + pointers + allocator-interface +)