From e5ba03599b646e6c17e878452c97a242fcbe2934 Mon Sep 17 00:00:00 2001 From: Edgaru089 Date: Sun, 7 Nov 2021 01:33:42 +0800 Subject: [PATCH] util: tree-based associative container --- util/test_tree.c | 80 +++++++++++++++++++++ util/tree.c | 164 +++++++++++++++++++++++++++++++++++++++++++ util/tree.h | 75 ++++++++++++++++++++ util/tree_internal.h | 60 ++++++++++++++++ 4 files changed, 379 insertions(+) create mode 100644 util/test_tree.c create mode 100644 util/tree.c create mode 100644 util/tree.h create mode 100644 util/tree_internal.h diff --git a/util/test_tree.c b/util/test_tree.c new file mode 100644 index 0000000..1633d6f --- /dev/null +++ b/util/test_tree.c @@ -0,0 +1,80 @@ +//$CC -g test_tree.c tree.c ../driver/random/random.c + +#include "stdlib.h" +#include "stdio.h" + +#include "tree.h" + + +tree_Tree *tree; + +void insert(int a) { + bool new; + int *d = tree_Insert(tree, a, &new); + if (new) + *d = 1; + else + (*d)++; +} + +int count(int a) { + int *d = tree_Find(tree, a); + if (!d) + return 0; + else + return *d; +} + +void delete (int a) { + tree_Node *node = tree_FindNode(tree, a); + if (!node) + return; + + (*(int *)node->data)--; + if ((*(int *)node->data) == 0) + tree_Delete(tree, node); +} + + +int main() { + tree = tree_Create(sizeof(int)); + + insert(1); + insert(2); + insert(3); + insert(6); + insert(1); + + printf("%d\n", count(2)); + printf("%d\n", count(4)); + printf("%d\n", count(1)); + delete (1); + printf("%d\n", count(1)); + delete (1); + printf("%d\n", count(1)); + delete (1); + printf("%d\n", count(1)); + delete (2); + printf("%d\n", count(1)); + + insert(7); + insert(10); + insert(54); + insert(18); + insert(63); + insert(39); + + tree_Node *i = tree_FirstNode(tree); + while (i) { + printf(" %d", (int)i->key); + i = tree_Node_Next(i); + } + printf("\n"); + + i = tree_LastNode(tree); + while (i) { + printf(" %d", (int)i->key); + i = tree_Node_Previous(i); + } + printf("\n"); +} diff --git a/util/tree.c b/util/tree.c new file mode 100644 index 0000000..5d4db32 --- /dev/null +++ b/util/tree.c @@ -0,0 +1,164 @@ + +#include "tree.h" +#include "tree_internal.h" + +#include "../main.h" +#include "../memory/memory.h" +#include "../driver/random/random.h" +#include "string.h" + + +// Create allocates and creates a new default Tree object. +tree_Tree *tree_Create(uintptr_t objectSize) { + tree_Tree *t = kMalloc(sizeof(tree_Tree)); + t->objectSize = objectSize; + t->size = 0; + t->root = 0; + return t; +} + + +static inline tree_Node *__tree_NewNode(tree_Tree *t, uintptr_t key, tree_Node *father, uintptr_t internal) { + t->size++; + + tree_Node *node = kMalloc(sizeof(tree_Node) - 1 + t->objectSize); + node->left = node->right = 0; + node->father = father; + node->key = key; + node->internal = internal; + return node; +} + +static void __tree_DestroyNodes(tree_Node *node) { + if (node->left) + __tree_DestroyNodes(node->left); + if (node->right) + __tree_DestroyNodes(node->right); + kFree(node); +} + +void tree_Destroy(tree_Tree *tree) { + if (tree->root) + __tree_DestroyNodes(tree->root); + kFree(tree); +} + + +// Will not return NULL +// SysV ABI because of the 6 arguments +SYSV_ABI static tree_Node *__tree_InsertNodes(tree_Tree *t, tree_Node *node, tree_Node *father, uintptr_t key, tree_Node **result, bool *added) { + if (!node) { + if (added) + *added = true; + return *result = __tree_NewNode(t, key, father, random_Rand() ^ key); + } else if (key < node->key) { + node->left = __tree_InsertNodes(t, node->left, node, key, result, added); + return node; + } else if (key > node->key) { + node->right = __tree_InsertNodes(t, node->right, node, key, result, added); + return node; + } else { + if (added) + *added = false; + *result = node; + return node; + } +} + +tree_Node *tree_InsertNode(tree_Tree *t, uintptr_t key, bool *added) { + tree_Node *result; + t->root = __tree_InsertNodes(t, t->root, 0, key, &result, added); + + if (*added) + __tree_treap_Adjust(result, &t->root); + + return result; +} + +void *tree_Insert(tree_Tree *t, uintptr_t key, bool *added) { + return tree_InsertNode(t, key, added)->data; +} + + +static tree_Node *__tree_FindNode(tree_Node *node, uintptr_t key) { + if (!node) + return NULL; + else if (key < node->key) + return __tree_FindNode(node->left, key); + else if (key > node->key) + return __tree_FindNode(node->right, key); + else + return node; +} + +tree_Node *tree_FindNode(tree_Tree *t, uintptr_t key) { + return __tree_FindNode(t->root, key); +} + +void *tree_Find(tree_Tree *t, uintptr_t key) { + tree_Node *node = tree_FindNode(t, key); + if (!node) + return NULL; + else + return node->data; +} + + +void tree_Delete(tree_Tree *t, tree_Node *node) { + while (node->left && node->right) + if (node->left->internal < node->right->internal) + __tree_Rotate(node->left, &t->root); + else + __tree_Rotate(node->right, &t->root); + + if (node == t->root) + t->root = (node->left ? node->left : node->right); + __tree_Connect(node->father, (node->left ? node->left : node->right), __tree_Tell(node)); + + kFree(node); +} + + +tree_Node *tree_FirstNode(tree_Tree *tree) { + tree_Node *result = tree->root; + while (result->left) + result = result->left; + return result; +} + +tree_Node *tree_LastNode(tree_Tree *tree) { + tree_Node *result = tree->root; + while (result->right) + result = result->right; + return result; +} + + +tree_Node *tree_Node_Next(tree_Node *node) { + if (node->right) { + tree_Node *result = node->right; + while (result->left) + result = result->left; + return result; + } else { + tree_Node *result = node; + while (result->father && __tree_Tell(result) == __tree_Right) + result = result->father; + return result->father; + } +} + +// Node_Previous returns the previous node. Returns NULL if first. +tree_Node *tree_Node_Previous(tree_Node *node) { + if (node->left) { + tree_Node *result = node->left; + while (result->right) + result = result->right; + return result; + } else { + tree_Node *result = node; + while (result->father && __tree_Tell(result) == __tree_Left) + result = result->father; + return result->father; + } +} diff --git a/util/tree.h b/util/tree.h new file mode 100644 index 0000000..b8679f7 --- /dev/null +++ b/util/tree.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct __tree_Node { + uintptr_t key; // node key + struct __tree_Node *left, *right; // left and right sons + struct __tree_Node *father; // father node + uintptr_t internal; // internal data for balanced trees + char data[1]; // placeholder for object data +} tree_Node; + +// Tree is a basic tree-based associative container. +// +// Right now it's a Treap. +typedef struct { + uintptr_t objectSize; // size in bytes of the object + tree_Node *root; // root of the tree, NULL if empty + uintptr_t size; // number of objects in the tree +} tree_Tree; + +// Create allocates and creates a new default Tree object. +tree_Tree *tree_Create(uintptr_t objectSize); + +// Destroy properly frees all data related to the structure, and itself. +void tree_Destroy(tree_Tree *tree); + +// Insert inserts a new object (or locates an existing one). +// +// If *added is not NULL, it is set to true if the key does +// not exist and is actually added. +// +// Newly allocated data is not zeroed, nor initialized in any way. +// +// Returns the pointer to the newly allocated (or existing) data. +void *tree_Insert(tree_Tree *tree, uintptr_t key, bool *added); + +// InsertNode does the same as Insert, but returns Node* instead of data. +tree_Node *tree_InsertNode(tree_Tree *tree, uintptr_t key, bool *added); + +// Find locates an existing object by its key. +// +// Returns NULL if the object does not exist. +void *tree_Find(tree_Tree *tree, uintptr_t key); + +// FindNode returns an existing tree node by its key. +// +// Used for iterating the tree objects. +tree_Node *tree_FindNode(tree_Tree *tree, uintptr_t key); + +// FirstNode returns the first node in increasing order. +tree_Node *tree_FirstNode(tree_Tree *tree); + +// LastNode returns the last node in increasing order. +tree_Node *tree_LastNode(tree_Tree *tree); + +// Delete deletes an existing node from the tree. +void tree_Delete(tree_Tree *tree, tree_Node *node); + +// Node_Next returns the next node. Returns NULL if the node is the last. +tree_Node *tree_Node_Next(tree_Node *node); + +// Node_Previous returns the previous node. Returns NULL if first. +tree_Node *tree_Node_Previous(tree_Node *node); + + +#ifdef __cplusplus +} +#endif diff --git a/util/tree_internal.h b/util/tree_internal.h new file mode 100644 index 0000000..207cd35 --- /dev/null +++ b/util/tree_internal.h @@ -0,0 +1,60 @@ +#pragma once + +#include "tree.h" + + +typedef enum { + __tree_Left, + __tree_Right, +} __tree_ConnectType; + +static inline __tree_ConnectType __tree_ConnectType_Invert(__tree_ConnectType type) { + return (__tree_ConnectType)(!type); +} + +static inline __tree_ConnectType __tree_Tell(tree_Node *son) { + if (!son->father) + return __tree_Left; + if (son->father->left == son) + return __tree_Left; + else + return __tree_Right; +} + +static inline tree_Node *__tree_Get(tree_Node *father, __tree_ConnectType type) { + return (type == __tree_Left) ? father->left : father->right; +} + +static inline void __tree_Connect(tree_Node *father, tree_Node *son, __tree_ConnectType type) { + if (son) + son->father = father; + if (father) { + if (type == __tree_Left) + father->left = son; + else + father->right = son; + } +} + +// Rotates the node up. +static inline void __tree_Rotate(tree_Node *node, tree_Node **root) { + if (!node->father) + return; + + __tree_ConnectType type = __tree_Tell(node); + + tree_Node *f = node->father, + *b = __tree_Get(node, __tree_ConnectType_Invert(type)); + __tree_Connect(f->father, node, __tree_Tell(f)); + __tree_Connect(node, f, __tree_ConnectType_Invert(type)); + __tree_Connect(f, b, type); + + if (!node->father) + *root = node; +} + +// Adjust the tree as a Treap +static inline void __tree_treap_Adjust(tree_Node *node, tree_Node **root) { + while (node->father && node->father->internal > node->internal) + __tree_Rotate(node, root); +}