From 7fb172c2916cc3c29193939d64f966b919721466 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Thu, 19 Jan 2023 21:22:08 +0100 Subject: [PATCH 1/8] feat: begin binary tree implementation --- include/vieter_tree.h | 15 +++++++++++++++ src/tree/node.h | 13 +++++++++++++ src/tree/tree.c | 6 ++++++ 3 files changed, 34 insertions(+) create mode 100644 include/vieter_tree.h create mode 100644 src/tree/node.h create mode 100644 src/tree/tree.c diff --git a/include/vieter_tree.h b/include/vieter_tree.h new file mode 100644 index 0000000..7dda9e1 --- /dev/null +++ b/include/vieter_tree.h @@ -0,0 +1,15 @@ +#ifndef VIETER_TREE +#define VIETER_TREE + +#include + +typedef struct vieter_tree vieter_tree; + +typedef enum vieter_tree_error { + vieter_tree_ok = 0, + vieter_tree_already_present = 1 +} vieter_tree_error; + +vieter_tree_error vieter_tree_insert(uint64_t key, void *data); + +#endif diff --git a/src/tree/node.h b/src/tree/node.h new file mode 100644 index 0000000..10143ca --- /dev/null +++ b/src/tree/node.h @@ -0,0 +1,13 @@ +#ifndef VIETER_BALANCED_TREE_NODE +#define VIETER_BALANCED_TREE_NODE + +#include + +typedef struct vieter_tree_node { + uint64_t key; + void *data; + struct vieter_tree_node *left; + struct vieter_tree_node *right; +} vieter_tree_node; + +#endif diff --git a/src/tree/tree.c b/src/tree/tree.c new file mode 100644 index 0000000..4a10d5a --- /dev/null +++ b/src/tree/tree.c @@ -0,0 +1,6 @@ +#include "vieter_tree.h" +#include "node.h" + +struct vieter_tree { + uint64_t size; +}; From 2ecd2eae4d838266370b3297ede989e5e9ce8ca3 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Thu, 19 Jan 2023 22:23:19 +0100 Subject: [PATCH 2/8] feat(tree): started standard binary tree --- Makefile | 6 +++- include/vieter_tree.h | 21 +++++++++++-- src/tree/node.c | 61 ++++++++++++++++++++++++++++++++++++ src/tree/node.h | 14 +++++++-- src/tree/tree.c | 57 +++++++++++++++++++++++++++++++-- test/tree/test_binary_tree.c | 32 +++++++++++++++++++ 6 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 src/tree/node.c create mode 100644 test/tree/test_binary_tree.c diff --git a/Makefile b/Makefile index 4907163..65ac6f4 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ INC_FLAGS := $(addprefix -I,$(INC_DIRS)) # object file is also recompiled if only a header is changed. # -MP: generate a dummy target for every header file (according to the docs it # prevents some errors when removing header files) -CFLAGS ?= -MMD -MP -Wall -Werror -Wextra +CFLAGS ?= -MMD -MP -Wall -Wextra CFLAGS += $(INC_FLAGS) .PHONY: all @@ -48,6 +48,10 @@ $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c test: build-test @ $(foreach bin,$(BINS_TEST),./$(bin);) +test-mem: build-test + @ $(foreach bin,$(BINS_TEST),valgrind --tool=memcheck --error-exitcode=1 \ + --track-origins=yes --leak-check=full ./$(bin);) + .PHONY: build-test build-test: $(BINS_TEST) diff --git a/include/vieter_tree.h b/include/vieter_tree.h index 7dda9e1..be381e5 100644 --- a/include/vieter_tree.h +++ b/include/vieter_tree.h @@ -2,14 +2,29 @@ #define VIETER_TREE #include +#include typedef struct vieter_tree vieter_tree; typedef enum vieter_tree_error { - vieter_tree_ok = 0, - vieter_tree_already_present = 1 + vieter_tree_ok = 0, + vieter_tree_already_present = 1, + vieter_tree_not_present = 2 } vieter_tree_error; -vieter_tree_error vieter_tree_insert(uint64_t key, void *data); +vieter_tree *vieter_tree_init(); + +void vieter_tree_free(vieter_tree *tree); + +vieter_tree_error vieter_tree_insert(vieter_tree *tree, uint64_t key, + void *data); + +vieter_tree_error vieter_tree_search(void **out, vieter_tree *tree, + uint64_t key); + +vieter_tree_error vieter_tree_remove(void **out, vieter_tree *tree, + uint64_t key); + +uint64_t vieter_tree_size(vieter_tree *tree); #endif diff --git a/src/tree/node.c b/src/tree/node.c new file mode 100644 index 0000000..935a56d --- /dev/null +++ b/src/tree/node.c @@ -0,0 +1,61 @@ +#include "node.h" + +vieter_tree_node *vieter_tree_node_init() { + return calloc(1, sizeof(vieter_tree_node)); +} + +vieter_tree_error vieter_tree_node_insert(vieter_tree_node *node, uint64_t key, + void *data) { + vieter_tree_node *parent = node; + + while (node != NULL) { + if (node->key == key) { + return vieter_tree_already_present; + } + + parent = node; + + if (key < node->key) { + node = parent->left; + } else { + node = parent->right; + } + } + + vieter_tree_node *new_node = vieter_tree_node_init(); + new_node->key = key; + new_node->data = data; + new_node->parent = parent; + + if (key < node->key) { + parent->left = new_node; + } else { + parent->right = new_node; + } + + return vieter_tree_ok; +} + +vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *node, + uint64_t key) { + while (node != NULL) { + if (node->key == key) { + *out = node->data; + + return vieter_tree_ok; + } + + if (key < node->key) { + node = node->left; + } else { + node = node->right; + } + } + + return vieter_tree_not_present; +} + +vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *node, + uint64_t key) { + return vieter_tree_not_present; +} diff --git a/src/tree/node.h b/src/tree/node.h index 10143ca..f22a965 100644 --- a/src/tree/node.h +++ b/src/tree/node.h @@ -1,13 +1,23 @@ -#ifndef VIETER_BALANCED_TREE_NODE -#define VIETER_BALANCED_TREE_NODE +#ifndef VIETER_TREE_NODE +#define VIETER_TREE_NODE +#include "vieter_tree.h" #include typedef struct vieter_tree_node { uint64_t key; void *data; + struct vieter_tree_node *parent; struct vieter_tree_node *left; struct vieter_tree_node *right; } vieter_tree_node; +vieter_tree_node *vieter_tree_node_init(); + +vieter_tree_error vieter_tree_node_insert(vieter_tree_node *node, uint64_t key, void *data); + +vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *node, uint64_t key); + +vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *node, uint64_t key); + #endif diff --git a/src/tree/tree.c b/src/tree/tree.c index 4a10d5a..725fcda 100644 --- a/src/tree/tree.c +++ b/src/tree/tree.c @@ -1,6 +1,59 @@ -#include "vieter_tree.h" #include "node.h" +#include "vieter_tree.h" struct vieter_tree { - uint64_t size; + uint64_t size; + vieter_tree_node *root; }; + +vieter_tree *vieter_tree_init() { return calloc(1, sizeof(vieter_tree)); } + +uint64_t vieter_tree_size(vieter_tree *tree) { return tree->size; } + +vieter_tree_error vieter_tree_insert(vieter_tree *tree, uint64_t key, + void *data) { + if (tree->size == 0) { + tree->root = vieter_tree_node_init(); + tree->root->key = key; + tree->root->data = data; + tree->size = 1; + + return vieter_tree_ok; + } + + vieter_tree_error res = vieter_tree_node_insert(tree->root, key, data); + + if (res != vieter_tree_ok) { + return res; + } + + tree->size++; + + return vieter_tree_ok; +} + +vieter_tree_error vieter_tree_search(void **out, vieter_tree *tree, + uint64_t key) { + if (tree->size == 0) { + return vieter_tree_not_present; + } + + return vieter_tree_node_search(out, tree->root, key); +} + +vieter_tree_error vieter_tree_remove(void **out, vieter_tree *tree, + uint64_t key) { + if (tree->size == 0) { + return vieter_tree_not_present; + } + + vieter_tree_error res = vieter_tree_node_remove(out, tree->root, key); + + if (res != vieter_tree_ok) { + return res; + } + + tree->size--; + + return vieter_tree_ok; +} diff --git a/test/tree/test_binary_tree.c b/test/tree/test_binary_tree.c new file mode 100644 index 0000000..fd6bb1b --- /dev/null +++ b/test/tree/test_binary_tree.c @@ -0,0 +1,32 @@ +#include "acutest.h" +#include "vieter_tree.h" + +#define TEST_SIZE(tree, size) \ + TEST_CHECK(vieter_tree_size(tree) == size); \ + TEST_MSG("Size: %zu", vieter_tree_size(tree)) + +void test_init() { + vieter_tree *tree = vieter_tree_init(); + TEST_CHECK(tree != NULL); + TEST_SIZE(tree, 0); + /* vieter_tree_free(tree); */ +} + +void test_insert() { + vieter_tree *tree = vieter_tree_init(); + + for (uint64_t i = 0; i < 25; i++) { + TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok); + } + + void *out; + + for (uint64_t i = 0; i < 25; i++) { + TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_already_present); + TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); + } +} + +TEST_LIST = { + {NULL, NULL} +}; From e88bfadf2c613602ff56d7827f2c1e3077e43818 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Thu, 19 Jan 2023 22:45:22 +0100 Subject: [PATCH 3/8] feat(tree): initially working binary tree --- Makefile | 12 ++-- src/tree/node.c | 125 +++++++++++++++++++++++++++++++---- src/tree/node.h | 11 ++- test/tree/test_binary_tree.c | 21 ++++++ 4 files changed, 146 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index 65ac6f4..4137be1 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,8 @@ SRC_DIR ?= src TEST_DIR ?= test INC_DIRS ?= include +LIB := $(BUILD_DIR)/$(LIB_FILENAME) + SRCS != find '$(SRC_DIR)' -iname '*.c' SRCS_H != find $(INC_DIRS) -iname '*.h' SRCS_TEST != find '$(TEST_DIR)' -iname '*.c' @@ -34,8 +36,8 @@ all: vieter # =====COMPILATION===== .PHONY: vieter -vieter: $(BUILD_DIR)/$(LIB_FILENAME) -$(BUILD_DIR)/$(LIB_FILENAME): $(OBJS) +vieter: $(LIB) +$(LIB): $(OBJS) ar -rcs $@ $(OBJS) $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c @@ -55,10 +57,8 @@ test-mem: build-test .PHONY: build-test build-test: $(BINS_TEST) -# For simplicity, we link every object file to each of the test files. This -# might be changed later if this starts to become too slow. -$(BINS_TEST): %: %.c.o $(OBJS) - $(CC) $^ -o $@ +$(BINS_TEST): %: %.c.o $(LIB) + $(CC) -L$(BUILD_DIR) -lvieter $^ -o $@ # Each test includes the test directory, which contains the acutest header file $(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c diff --git a/src/tree/node.c b/src/tree/node.c index 935a56d..131e932 100644 --- a/src/tree/node.c +++ b/src/tree/node.c @@ -4,9 +4,35 @@ vieter_tree_node *vieter_tree_node_init() { return calloc(1, sizeof(vieter_tree_node)); } -vieter_tree_error vieter_tree_node_insert(vieter_tree_node *node, uint64_t key, +void vieter_tree_node_add_child(vieter_tree_node *parent, uint64_t key, + vieter_tree_node *child) { + if (parent == NULL) { + return; + } + + if (key < parent->key) { + parent->left = child; + } else { + parent->right = child; + } + + if (child != NULL) { + child->parent = parent; + } +} + +void vieter_tree_node_replace(vieter_tree_node *to_replace, + vieter_tree_node *replacement) { + to_replace->key = replacement->key; + to_replace->data = replacement->data; + to_replace->left = replacement->left; + to_replace->right = replacement->right; +} + +vieter_tree_error vieter_tree_node_insert(vieter_tree_node *root, uint64_t key, void *data) { - vieter_tree_node *parent = node; + vieter_tree_node *node = root; + vieter_tree_node *parent = root; while (node != NULL) { if (node->key == key) { @@ -15,7 +41,7 @@ vieter_tree_error vieter_tree_node_insert(vieter_tree_node *node, uint64_t key, parent = node; - if (key < node->key) { + if (key < parent->key) { node = parent->left; } else { node = parent->right; @@ -25,22 +51,20 @@ vieter_tree_error vieter_tree_node_insert(vieter_tree_node *node, uint64_t key, vieter_tree_node *new_node = vieter_tree_node_init(); new_node->key = key; new_node->data = data; - new_node->parent = parent; - if (key < node->key) { - parent->left = new_node; - } else { - parent->right = new_node; - } + vieter_tree_node_add_child(parent, key, new_node); return vieter_tree_ok; } -vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *node, - uint64_t key) { +vieter_tree_error vieter_tree_node_search_node(vieter_tree_node **out, + vieter_tree_node *root, + uint64_t key) { + vieter_tree_node *node = root; + while (node != NULL) { if (node->key == key) { - *out = node->data; + *out = node; return vieter_tree_ok; } @@ -55,7 +79,80 @@ vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *node, return vieter_tree_not_present; } -vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *node, +vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *root, uint64_t key) { - return vieter_tree_not_present; + vieter_tree_node *target; + vieter_tree_error res = vieter_tree_node_search_node(&target, root, key); + + if (res != vieter_tree_ok) { + return res; + } + + *out = target->data; + + return vieter_tree_ok; +} + +vieter_tree_node *vieter_tree_node_successor(vieter_tree_node *node) { + if ((node->left != NULL) ^ (node->right != NULL)) { + return node->left != NULL ? node->left : node->right; + } + + node = node->right; + + while (node->left != NULL) { + node = node->left; + } + + return node; +} + +vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, + uint64_t key) { + vieter_tree_node *target; + vieter_tree_error res = vieter_tree_node_search_node(&target, root, key); + + if (res != vieter_tree_ok) { + return res; + } + + *out = target->data; + + if (target->left == NULL && target->right == NULL) { + vieter_tree_node_add_child(target->parent, target->key, NULL); + + free(target); + } else if ((target->left == NULL) ^ (target->right == NULL)) { + vieter_tree_node *child = + target->left != NULL ? target->left : target->right; + + if (target->parent != NULL) { + vieter_tree_node_add_child(target->parent, child->key, child); + free(target); + } else { + vieter_tree_node_replace(target, child); + free(child); + } + + } else { + vieter_tree_node *replacement = target->right; + + while (replacement->left != NULL) { + replacement = replacement->left; + } + + // We use replacement->key here because the right child can be NULL, so + // reading its key isn't safe. Using replacement->key however, the child + // will still get placed into the right location because of how binary + // trees work. + vieter_tree_node_add_child(replacement->parent, replacement->key, + replacement->right); + + target->key = replacement->key; + target->data = replacement->data; + + free(replacement); + } + + return vieter_tree_ok; } diff --git a/src/tree/node.h b/src/tree/node.h index f22a965..68da70e 100644 --- a/src/tree/node.h +++ b/src/tree/node.h @@ -14,10 +14,15 @@ typedef struct vieter_tree_node { vieter_tree_node *vieter_tree_node_init(); -vieter_tree_error vieter_tree_node_insert(vieter_tree_node *node, uint64_t key, void *data); +vieter_tree_error vieter_tree_node_insert(vieter_tree_node *root, uint64_t key, void *data); -vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *node, uint64_t key); +vieter_tree_error vieter_tree_node_search_node(vieter_tree_node **out, vieter_tree_node *root, uint64_t key); +vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *root, uint64_t key); -vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *node, uint64_t key); +vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, uint64_t key); + +vieter_tree_node *vieter_tree_node_successor(vieter_tree_node *node); + +void vieter_tree_node_replace(vieter_tree_node *to_replace, vieter_tree_node *replacement); #endif diff --git a/test/tree/test_binary_tree.c b/test/tree/test_binary_tree.c index fd6bb1b..4b1ceba 100644 --- a/test/tree/test_binary_tree.c +++ b/test/tree/test_binary_tree.c @@ -17,16 +17,37 @@ void test_insert() { for (uint64_t i = 0; i < 25; i++) { TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok); + TEST_SIZE(tree, i + 1); } void *out; for (uint64_t i = 0; i < 25; i++) { + TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_already_present); TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); } } +void test_remove() { + vieter_tree *tree = vieter_tree_init(); + + for (uint64_t i = 0; i < 25; i++) { + TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok); + } + + void *out; + + for (uint64_t i = 0; i < 25; i++) { + TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); + TEST_CHECK(vieter_tree_remove(&out, tree, i) == vieter_tree_ok); + TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_not_present); + } +} + TEST_LIST = { + {"test_init", test_init}, + {"test_insert", test_insert}, + {"test_remove", test_remove}, {NULL, NULL} }; From 0a6c58be5368658c424c0d3271d9b5bb1ec4578e Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Fri, 20 Jan 2023 11:07:33 +0100 Subject: [PATCH 4/8] feat(tree): implement free function --- src/tree/node.c | 10 +++++---- src/tree/node.h | 4 ++-- src/tree/tree.c | 40 ++++++++++++++++++++++++++++++++++++ test/tree/test_binary_tree.c | 7 ++++++- 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/tree/node.c b/src/tree/node.c index 131e932..568103e 100644 --- a/src/tree/node.c +++ b/src/tree/node.c @@ -4,6 +4,8 @@ vieter_tree_node *vieter_tree_node_init() { return calloc(1, sizeof(vieter_tree_node)); } +void vieter_tree_node_free(vieter_tree_node *node) { free(node); } + void vieter_tree_node_add_child(vieter_tree_node *parent, uint64_t key, vieter_tree_node *child) { if (parent == NULL) { @@ -121,17 +123,17 @@ vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, if (target->left == NULL && target->right == NULL) { vieter_tree_node_add_child(target->parent, target->key, NULL); - free(target); + vieter_tree_node_free(target); } else if ((target->left == NULL) ^ (target->right == NULL)) { vieter_tree_node *child = target->left != NULL ? target->left : target->right; if (target->parent != NULL) { vieter_tree_node_add_child(target->parent, child->key, child); - free(target); + vieter_tree_node_free(target); } else { vieter_tree_node_replace(target, child); - free(child); + vieter_tree_node_free(child); } } else { @@ -151,7 +153,7 @@ vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, target->key = replacement->key; target->data = replacement->data; - free(replacement); + vieter_tree_node_free(replacement); } return vieter_tree_ok; diff --git a/src/tree/node.h b/src/tree/node.h index 68da70e..dc8cce0 100644 --- a/src/tree/node.h +++ b/src/tree/node.h @@ -14,6 +14,8 @@ typedef struct vieter_tree_node { vieter_tree_node *vieter_tree_node_init(); +void vieter_tree_node_free(vieter_tree_node *node); + vieter_tree_error vieter_tree_node_insert(vieter_tree_node *root, uint64_t key, void *data); vieter_tree_error vieter_tree_node_search_node(vieter_tree_node **out, vieter_tree_node *root, uint64_t key); @@ -21,8 +23,6 @@ vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *root, ui vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, uint64_t key); -vieter_tree_node *vieter_tree_node_successor(vieter_tree_node *node); - void vieter_tree_node_replace(vieter_tree_node *to_replace, vieter_tree_node *replacement); #endif diff --git a/src/tree/tree.c b/src/tree/tree.c index 725fcda..c2c7aa7 100644 --- a/src/tree/tree.c +++ b/src/tree/tree.c @@ -57,3 +57,43 @@ vieter_tree_error vieter_tree_remove(void **out, vieter_tree *tree, return vieter_tree_ok; } + +void vieter_tree_free(vieter_tree *tree) { + if (tree->size == 0) { + goto end; + } + + uint64_t capacity = 4; + uint64_t size = 1; + vieter_tree_node **stack = malloc(capacity * sizeof(vieter_tree_node *)); + stack[0] = tree->root; + + vieter_tree_node *node; + + while (size > 0) { + node = stack[size - 1]; + size--; + + if (size + 2 > capacity) { + capacity *= 2; + stack = realloc(stack, capacity * sizeof(vieter_tree_node *)); + } + + if (node->left != NULL) { + stack[size] = node->left; + size++; + } + + if (node->right != NULL) { + stack[size] = node->right; + size++; + } + + vieter_tree_node_free(node); + } + + free(stack); + +end: + free(tree); +} diff --git a/test/tree/test_binary_tree.c b/test/tree/test_binary_tree.c index 4b1ceba..7e85ebe 100644 --- a/test/tree/test_binary_tree.c +++ b/test/tree/test_binary_tree.c @@ -9,7 +9,7 @@ void test_init() { vieter_tree *tree = vieter_tree_init(); TEST_CHECK(tree != NULL); TEST_SIZE(tree, 0); - /* vieter_tree_free(tree); */ + vieter_tree_free(tree); } void test_insert() { @@ -27,6 +27,8 @@ void test_insert() { TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_already_present); TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); } + + vieter_tree_free(tree); } void test_remove() { @@ -42,7 +44,10 @@ void test_remove() { TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); TEST_CHECK(vieter_tree_remove(&out, tree, i) == vieter_tree_ok); TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_not_present); + TEST_SIZE(tree, 25 - i - 1); } + + vieter_tree_free(tree); } TEST_LIST = { From 6a6c91c64dec185f1e77bdeae4b3208be8db0064 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Fri, 20 Jan 2023 12:08:05 +0100 Subject: [PATCH 5/8] feat(tree): add node bit flags support --- src/tree/balancing.c | 3 +++ src/tree/balancing.h | 8 ++++++++ src/tree/node.c | 12 ++++++++++++ src/tree/node.h | 12 ++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 src/tree/balancing.c create mode 100644 src/tree/balancing.h diff --git a/src/tree/balancing.c b/src/tree/balancing.c new file mode 100644 index 0000000..85ae1a3 --- /dev/null +++ b/src/tree/balancing.c @@ -0,0 +1,3 @@ +#include "balancing.h" + +void vieter_tree_node_balance_after_insert(vieter_tree_node *node) {} diff --git a/src/tree/balancing.h b/src/tree/balancing.h new file mode 100644 index 0000000..3e4c6bc --- /dev/null +++ b/src/tree/balancing.h @@ -0,0 +1,8 @@ +#ifndef VIETER_TREE_BALANCING +#define VIETER_TREE_BALANCING + +#include "node.h" + +void vieter_tree_node_balance_after_insert(vieter_tree_node *node); + +#endif diff --git a/src/tree/node.c b/src/tree/node.c index 568103e..b36feed 100644 --- a/src/tree/node.c +++ b/src/tree/node.c @@ -158,3 +158,15 @@ vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, return vieter_tree_ok; } + +void vieter_tree_node_set(vieter_tree_node *node, vieter_node_flag flag) { + node->flags |= flag; +} + +void vieter_tree_node_unset(vieter_tree_node *node, vieter_node_flag flag) { + node->flags &= ~flag; +} + +bool vieter_tree_node_get(vieter_tree_node *node, vieter_node_flag flag) { + return (node->flags & flag) != 0; +} diff --git a/src/tree/node.h b/src/tree/node.h index dc8cce0..2db2145 100644 --- a/src/tree/node.h +++ b/src/tree/node.h @@ -3,6 +3,13 @@ #include "vieter_tree.h" #include +#include + +#define VIETER_NODE_BLACK ((uint8_t)1) + +typedef enum vieter_node_flag { + vieter_node_black = ((uint8_t)1) << 0 +} vieter_node_flag; typedef struct vieter_tree_node { uint64_t key; @@ -10,6 +17,7 @@ typedef struct vieter_tree_node { struct vieter_tree_node *parent; struct vieter_tree_node *left; struct vieter_tree_node *right; + uint8_t flags; } vieter_tree_node; vieter_tree_node *vieter_tree_node_init(); @@ -25,4 +33,8 @@ vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, ui void vieter_tree_node_replace(vieter_tree_node *to_replace, vieter_tree_node *replacement); +void vieter_tree_node_set(vieter_tree_node *node, vieter_node_flag flag); +void vieter_tree_node_unset(vieter_tree_node *node, vieter_node_flag flag); +bool vieter_tree_node_get(vieter_tree_node *node, vieter_node_flag flag); + #endif From f56ba20343fb2268d69a28a562b8a29856e464b4 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Fri, 20 Jan 2023 13:23:53 +0100 Subject: [PATCH 6/8] feat(tree): first attempt at balanced inserts --- Makefile | 2 +- src/tree/balancing.c | 94 +++++++++++++++++++++++++++++++++++++++++++- src/tree/node.c | 16 +------- src/tree/node.h | 3 ++ src/tree/tree.c | 1 + 5 files changed, 100 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 4137be1..d5a8348 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ INC_FLAGS := $(addprefix -I,$(INC_DIRS)) # object file is also recompiled if only a header is changed. # -MP: generate a dummy target for every header file (according to the docs it # prevents some errors when removing header files) -CFLAGS ?= -MMD -MP -Wall -Wextra +CFLAGS ?= -MMD -MP -Wall -Wextra -g CFLAGS += $(INC_FLAGS) .PHONY: all diff --git a/src/tree/balancing.c b/src/tree/balancing.c index 85ae1a3..b7466ea 100644 --- a/src/tree/balancing.c +++ b/src/tree/balancing.c @@ -1,3 +1,95 @@ #include "balancing.h" -void vieter_tree_node_balance_after_insert(vieter_tree_node *node) {} +vieter_tree_node *vieter_tree_node_balance(vieter_tree_node *node) { + vieter_tree_node *parent = node->parent; + vieter_tree_node *grand_parent = parent->parent; + vieter_tree_node *grand_grand_parent = grand_parent->parent; + vieter_tree_node *root, *left, *right; + vieter_tree_node *children[4]; + + if (node->key < parent->key && parent->key < grand_parent->key) { + root = parent; + left = node; + right = grand_parent; + + children[0] = node->left; + children[1] = node->right; + children[2] = parent->right; + children[3] = grand_parent->right; + } else if (node->key < parent->key && parent->key > grand_parent->key) { + root = node; + left = grand_parent; + right = parent; + + children[0] = grand_parent->left; + children[1] = node->left; + children[2] = node->right; + children[3] = parent->right; + } else if (node->key > parent->key && parent->key < grand_parent->key) { + root = node; + left = parent; + right = grand_parent; + + children[0] = parent->left; + children[1] = node->left; + children[2] = node->right; + children[3] = grand_parent->right; + } else { + root = parent; + left = grand_parent; + right = node; + + children[0] = grand_parent->left; + children[1] = parent->left; + children[2] = node->left; + children[3] = node->right; + } + + vieter_tree_node_set(left, vieter_node_black); + vieter_tree_node_set(right, vieter_node_black); + vieter_tree_node_unset(root, vieter_node_black); + + left->left = children[0]; + left->right = children[1]; + + if (children[0] != NULL) + children[0]->parent = left; + + if (children[1] != NULL) + children[1]->parent = left; + + right->left = children[2]; + right->right = children[3]; + + if (children[2] != NULL) + children[2]->parent = right; + + if (children[3] != NULL) + children[3]->parent = right; + + root->left = left; + root->right = right; + left->parent = root; + right->parent = root; + + if (grand_grand_parent != NULL) { + vieter_tree_node_add_child(grand_grand_parent, root->key, root); + } else { + root->parent = NULL; + } + + return root; +} + +void vieter_tree_node_balance_after_insert(vieter_tree_node *node) { + while (!vieter_tree_node_get(node, vieter_node_black) && + node->parent != NULL && + !vieter_tree_node_get(node->parent, vieter_node_black)) { + node = vieter_tree_node_balance(node); + } + + // The root must always be black + if (node->parent == NULL) { + vieter_tree_node_set(node, vieter_node_black); + } +} diff --git a/src/tree/node.c b/src/tree/node.c index b36feed..d6c2b6d 100644 --- a/src/tree/node.c +++ b/src/tree/node.c @@ -1,4 +1,5 @@ #include "node.h" +#include "balancing.h" vieter_tree_node *vieter_tree_node_init() { return calloc(1, sizeof(vieter_tree_node)); @@ -55,6 +56,7 @@ vieter_tree_error vieter_tree_node_insert(vieter_tree_node *root, uint64_t key, new_node->data = data; vieter_tree_node_add_child(parent, key, new_node); + vieter_tree_node_balance_after_insert(new_node); return vieter_tree_ok; } @@ -95,20 +97,6 @@ vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *root, return vieter_tree_ok; } -vieter_tree_node *vieter_tree_node_successor(vieter_tree_node *node) { - if ((node->left != NULL) ^ (node->right != NULL)) { - return node->left != NULL ? node->left : node->right; - } - - node = node->right; - - while (node->left != NULL) { - node = node->left; - } - - return node; -} - vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, uint64_t key) { vieter_tree_node *target; diff --git a/src/tree/node.h b/src/tree/node.h index 2db2145..522ee59 100644 --- a/src/tree/node.h +++ b/src/tree/node.h @@ -37,4 +37,7 @@ void vieter_tree_node_set(vieter_tree_node *node, vieter_node_flag flag); void vieter_tree_node_unset(vieter_tree_node *node, vieter_node_flag flag); bool vieter_tree_node_get(vieter_tree_node *node, vieter_node_flag flag); +void vieter_tree_node_add_child(vieter_tree_node *parent, uint64_t key, + vieter_tree_node *child); + #endif diff --git a/src/tree/tree.c b/src/tree/tree.c index c2c7aa7..d2579e6 100644 --- a/src/tree/tree.c +++ b/src/tree/tree.c @@ -16,6 +16,7 @@ vieter_tree_error vieter_tree_insert(vieter_tree *tree, uint64_t key, tree->root = vieter_tree_node_init(); tree->root->key = key; tree->root->data = data; + vieter_tree_node_set(tree->root, vieter_node_black); tree->size = 1; return vieter_tree_ok; From b685c72e6e21e0eeea614868fa29d1212b097932 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Fri, 20 Jan 2023 15:44:32 +0100 Subject: [PATCH 7/8] feat(tree): remove works --- src/tree/balancing.c | 49 ++++++++++++++++++++++++------------ src/tree/node.c | 12 ++++++--- test/tree/test_binary_tree.c | 10 ++++---- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/tree/balancing.c b/src/tree/balancing.c index b7466ea..9f9d896 100644 --- a/src/tree/balancing.c +++ b/src/tree/balancing.c @@ -3,22 +3,35 @@ vieter_tree_node *vieter_tree_node_balance(vieter_tree_node *node) { vieter_tree_node *parent = node->parent; vieter_tree_node *grand_parent = parent->parent; - vieter_tree_node *grand_grand_parent = grand_parent->parent; - vieter_tree_node *root, *left, *right; + vieter_tree_node *root = grand_parent; + vieter_tree_node *left, *right; vieter_tree_node *children[4]; + uint64_t key_root = root->key; + void *data_root = root->data; + if (node->key < parent->key && parent->key < grand_parent->key) { - root = parent; + root->key = parent->key; + root->data = parent->data; + + parent->key = key_root; + parent->data = data_root; + left = node; - right = grand_parent; + right = parent; children[0] = node->left; children[1] = node->right; children[2] = parent->right; children[3] = grand_parent->right; } else if (node->key < parent->key && parent->key > grand_parent->key) { - root = node; - left = grand_parent; + root->key = node->key; + root->data = node->data; + + node->key = key_root; + node->data = data_root; + + left = node; right = parent; children[0] = grand_parent->left; @@ -26,17 +39,27 @@ vieter_tree_node *vieter_tree_node_balance(vieter_tree_node *node) { children[2] = node->right; children[3] = parent->right; } else if (node->key > parent->key && parent->key < grand_parent->key) { - root = node; + root->key = node->key; + root->data = node->data; + + node->key = key_root; + node->data = data_root; + left = parent; - right = grand_parent; + right = node; children[0] = parent->left; children[1] = node->left; children[2] = node->right; children[3] = grand_parent->right; } else { - root = parent; - left = grand_parent; + root->key = parent->key; + root->data = parent->data; + + parent->key = key_root; + parent->data = data_root; + + left = parent; right = node; children[0] = grand_parent->left; @@ -72,12 +95,6 @@ vieter_tree_node *vieter_tree_node_balance(vieter_tree_node *node) { left->parent = root; right->parent = root; - if (grand_grand_parent != NULL) { - vieter_tree_node_add_child(grand_grand_parent, root->key, root); - } else { - root->parent = NULL; - } - return root; } diff --git a/src/tree/node.c b/src/tree/node.c index d6c2b6d..0d74773 100644 --- a/src/tree/node.c +++ b/src/tree/node.c @@ -24,12 +24,18 @@ void vieter_tree_node_add_child(vieter_tree_node *parent, uint64_t key, } } -void vieter_tree_node_replace(vieter_tree_node *to_replace, - vieter_tree_node *replacement) { +void vieter_tree_node_replace_with_child(vieter_tree_node *to_replace, + vieter_tree_node *replacement) { to_replace->key = replacement->key; to_replace->data = replacement->data; to_replace->left = replacement->left; to_replace->right = replacement->right; + + if (to_replace->left != NULL) + to_replace->left->parent = to_replace; + + if (to_replace->right != NULL) + to_replace->right->parent = to_replace; } vieter_tree_error vieter_tree_node_insert(vieter_tree_node *root, uint64_t key, @@ -120,7 +126,7 @@ vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, vieter_tree_node_add_child(target->parent, child->key, child); vieter_tree_node_free(target); } else { - vieter_tree_node_replace(target, child); + vieter_tree_node_replace_with_child(target, child); vieter_tree_node_free(child); } diff --git a/test/tree/test_binary_tree.c b/test/tree/test_binary_tree.c index 7e85ebe..aa7fc0f 100644 --- a/test/tree/test_binary_tree.c +++ b/test/tree/test_binary_tree.c @@ -15,14 +15,14 @@ void test_init() { void test_insert() { vieter_tree *tree = vieter_tree_init(); - for (uint64_t i = 0; i < 25; i++) { + for (uint64_t i = 0; i < 250; i++) { TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok); TEST_SIZE(tree, i + 1); } void *out; - for (uint64_t i = 0; i < 25; i++) { + for (uint64_t i = 0; i < 250; i++) { TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_already_present); TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); @@ -34,17 +34,17 @@ void test_insert() { void test_remove() { vieter_tree *tree = vieter_tree_init(); - for (uint64_t i = 0; i < 25; i++) { + for (uint64_t i = 0; i < 250; i++) { TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok); } void *out; - for (uint64_t i = 0; i < 25; i++) { + for (uint64_t i = 0; i < 250; i++) { TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); TEST_CHECK(vieter_tree_remove(&out, tree, i) == vieter_tree_ok); TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_not_present); - TEST_SIZE(tree, 25 - i - 1); + TEST_SIZE(tree, 250 - i - 1); } vieter_tree_free(tree); From 3b94bc4491146877fd71e5285ffc277bfde057d9 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 22 Jan 2023 09:42:08 +0100 Subject: [PATCH 8/8] refactor(tree): proper filenames --- src/tree/{tree.c => vieter_tree.c} | 2 +- src/tree/{balancing.c => vieter_tree_balancing.c} | 2 +- src/tree/{balancing.h => vieter_tree_balancing.h} | 2 +- src/tree/{node.c => vieter_tree_node.c} | 4 ++-- src/tree/{node.h => vieter_tree_node.h} | 0 5 files changed, 5 insertions(+), 5 deletions(-) rename src/tree/{tree.c => vieter_tree.c} (98%) rename src/tree/{balancing.c => vieter_tree_balancing.c} (98%) rename src/tree/{balancing.h => vieter_tree_balancing.h} (82%) rename src/tree/{node.c => vieter_tree_node.c} (98%) rename src/tree/{node.h => vieter_tree_node.h} (100%) diff --git a/src/tree/tree.c b/src/tree/vieter_tree.c similarity index 98% rename from src/tree/tree.c rename to src/tree/vieter_tree.c index d2579e6..72360b0 100644 --- a/src/tree/tree.c +++ b/src/tree/vieter_tree.c @@ -1,5 +1,5 @@ -#include "node.h" #include "vieter_tree.h" +#include "vieter_tree_node.h" struct vieter_tree { uint64_t size; diff --git a/src/tree/balancing.c b/src/tree/vieter_tree_balancing.c similarity index 98% rename from src/tree/balancing.c rename to src/tree/vieter_tree_balancing.c index 9f9d896..56047a0 100644 --- a/src/tree/balancing.c +++ b/src/tree/vieter_tree_balancing.c @@ -1,4 +1,4 @@ -#include "balancing.h" +#include "vieter_tree_balancing.h" vieter_tree_node *vieter_tree_node_balance(vieter_tree_node *node) { vieter_tree_node *parent = node->parent; diff --git a/src/tree/balancing.h b/src/tree/vieter_tree_balancing.h similarity index 82% rename from src/tree/balancing.h rename to src/tree/vieter_tree_balancing.h index 3e4c6bc..4492588 100644 --- a/src/tree/balancing.h +++ b/src/tree/vieter_tree_balancing.h @@ -1,7 +1,7 @@ #ifndef VIETER_TREE_BALANCING #define VIETER_TREE_BALANCING -#include "node.h" +#include "vieter_tree_node.h" void vieter_tree_node_balance_after_insert(vieter_tree_node *node); diff --git a/src/tree/node.c b/src/tree/vieter_tree_node.c similarity index 98% rename from src/tree/node.c rename to src/tree/vieter_tree_node.c index 0d74773..40407a3 100644 --- a/src/tree/node.c +++ b/src/tree/vieter_tree_node.c @@ -1,5 +1,5 @@ -#include "node.h" -#include "balancing.h" +#include "vieter_tree_node.h" +#include "vieter_tree_balancing.h" vieter_tree_node *vieter_tree_node_init() { return calloc(1, sizeof(vieter_tree_node)); diff --git a/src/tree/node.h b/src/tree/vieter_tree_node.h similarity index 100% rename from src/tree/node.h rename to src/tree/vieter_tree_node.h