From 050e99b413314e680cfc50787889f901905d3a57 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sat, 21 Jan 2023 16:31:22 +0100 Subject: [PATCH 01/24] feat(heap): code skeleton --- Makefile | 2 +- include/vieter_heap.h | 26 ++++++++++++++++++++++++++ src/heap/heap.c | 28 ++++++++++++++++++++++++++++ test/heap/test_heap.c | 18 ++++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 include/vieter_heap.h create mode 100644 src/heap/heap.c create mode 100644 test/heap/test_heap.c diff --git a/Makefile b/Makefile index 4907163..edca96b 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 diff --git a/include/vieter_heap.h b/include/vieter_heap.h new file mode 100644 index 0000000..ffb7da2 --- /dev/null +++ b/include/vieter_heap.h @@ -0,0 +1,26 @@ +#ifndef VIETER_HEAP +#define VIETER_HEAP + +#include +#include + +typedef struct vieter_heap vieter_heap; + +typedef enum vieter_heap_error { + vieter_heap_ok = 0, + vieter_heap_empty = 1 +} vieter_heap_error; + +vieter_heap *vieter_heap_init(); + +void vieter_heap_free(vieter_heap *heap); + +uint64_t vieter_heap_size(vieter_heap *heap); + +vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, void *data); + +vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap); + +vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap); + +#endif diff --git a/src/heap/heap.c b/src/heap/heap.c new file mode 100644 index 0000000..7e3669a --- /dev/null +++ b/src/heap/heap.c @@ -0,0 +1,28 @@ +#include "vieter_heap.h" + +struct vieter_heap { + uint64_t size; +}; + +vieter_heap *vieter_heap_init() { + return NULL; +} + +uint64_t vieter_heap_size(vieter_heap *heap) { + return heap->size; +} + +void vieter_heap_free(vieter_heap *heap) {} + + +vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, void *data) { + return vieter_heap_ok; +} + +vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { + return vieter_heap_ok; +} + +vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap) { + return vieter_heap_ok; +} diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c new file mode 100644 index 0000000..4079569 --- /dev/null +++ b/test/heap/test_heap.c @@ -0,0 +1,18 @@ +#include "acutest.h" +#include "vieter_heap.h" + +#define TEST_SIZE(heap, size) \ + TEST_CHECK(vieter_heap_size(heap) == size); \ + TEST_MSG("Size: %zu", vieter_heap_size(heap)) + +void test_init() { + vieter_heap *heap = vieter_heap_init(); + TEST_CHECK(heap != NULL); + TEST_SIZE(heap, 0); + vieter_heap_free(heap); +} + +TEST_LIST = { + {"test_init", test_init}, + {NULL, NULL} +}; From 6823050c2fecf6246366f61a4d42051d01a2c4a6 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 22 Jan 2023 09:40:35 +0100 Subject: [PATCH 02/24] refactor(heap): properly organised code --- include/vieter_heap.h | 9 +++++---- src/heap/heap.c | 28 --------------------------- src/heap/vieter_heap.c | 38 +++++++++++++++++++++++++++++++++++++ src/heap/vieter_heap_tree.c | 38 +++++++++++++++++++++++++++++++++++++ src/heap/vieter_heap_tree.h | 30 +++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 32 deletions(-) delete mode 100644 src/heap/heap.c create mode 100644 src/heap/vieter_heap.c create mode 100644 src/heap/vieter_heap_tree.c create mode 100644 src/heap/vieter_heap_tree.h diff --git a/include/vieter_heap.h b/include/vieter_heap.h index ffb7da2..c7a9706 100644 --- a/include/vieter_heap.h +++ b/include/vieter_heap.h @@ -1,14 +1,14 @@ #ifndef VIETER_HEAP #define VIETER_HEAP -#include #include +#include typedef struct vieter_heap vieter_heap; typedef enum vieter_heap_error { - vieter_heap_ok = 0, - vieter_heap_empty = 1 + vieter_heap_ok = 0, + vieter_heap_empty = 1 } vieter_heap_error; vieter_heap *vieter_heap_init(); @@ -17,7 +17,8 @@ void vieter_heap_free(vieter_heap *heap); uint64_t vieter_heap_size(vieter_heap *heap); -vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, void *data); +vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, + void *data); vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap); diff --git a/src/heap/heap.c b/src/heap/heap.c deleted file mode 100644 index 7e3669a..0000000 --- a/src/heap/heap.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "vieter_heap.h" - -struct vieter_heap { - uint64_t size; -}; - -vieter_heap *vieter_heap_init() { - return NULL; -} - -uint64_t vieter_heap_size(vieter_heap *heap) { - return heap->size; -} - -void vieter_heap_free(vieter_heap *heap) {} - - -vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, void *data) { - return vieter_heap_ok; -} - -vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { - return vieter_heap_ok; -} - -vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap) { - return vieter_heap_ok; -} diff --git a/src/heap/vieter_heap.c b/src/heap/vieter_heap.c new file mode 100644 index 0000000..6d543df --- /dev/null +++ b/src/heap/vieter_heap.c @@ -0,0 +1,38 @@ +#include "vieter_heap.h" +#include "vieter_heap_tree.h" + +#include + +struct vieter_heap { + vieter_heap_tree *tree; +}; + +vieter_heap *vieter_heap_init() { return calloc(1, sizeof(vieter_heap)); } + +uint64_t vieter_heap_size(vieter_heap *heap) { return 0; } + +void vieter_heap_free(vieter_heap *heap) {} + +vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, + void *data) { + vieter_heap_node *new_node = vieter_heap_node_init(); + new_node->key = key; + new_node->data = data; + vieter_heap_tree *new_tree = vieter_heap_tree_init(new_node, NULL, 1); + + if (heap->tree == NULL) { + heap->tree = new_tree; + } else { + heap->tree = vieter_heap_tree_merge(heap->tree, new_tree); + } + + return vieter_heap_ok; +} + +vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { + return vieter_heap_ok; +} + +vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap) { + return vieter_heap_ok; +} diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c new file mode 100644 index 0000000..e65fb56 --- /dev/null +++ b/src/heap/vieter_heap_tree.c @@ -0,0 +1,38 @@ +#include "vieter_heap_tree.h" + +vieter_heap_node *vieter_heap_node_init() { + return calloc(1, sizeof(vieter_heap_node)); +} + +vieter_heap_node *vieter_heap_node_merge_same_order(vieter_heap_node *root_a, + vieter_heap_node *root_b) { + vieter_heap_node *new_root, *new_child; + + if (root_a->key <= root_b->key) { + new_root = root_a; + new_child = root_b; + } else { + new_root = root_b; + new_child = root_a; + } + + new_root->next_largest_order = new_root->largest_order; + new_root->largest_order = new_child; + + return new_root; +} + +vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, + vieter_heap_tree *next, + uint64_t order) { + vieter_heap_tree *tree = malloc(sizeof(vieter_heap_tree)); + + tree->root = root; + tree->next = next; + tree->order = order; + + return tree; +} + +vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, + vieter_heap_tree *tree_b) {} diff --git a/src/heap/vieter_heap_tree.h b/src/heap/vieter_heap_tree.h new file mode 100644 index 0000000..73e1611 --- /dev/null +++ b/src/heap/vieter_heap_tree.h @@ -0,0 +1,30 @@ +#ifndef VIETER_HEAP_TREE +#define VIETER_HEAP_TREE + +#include +#include + +typedef struct vieter_heap_node { + uint64_t key; + void *data; + struct vieter_heap_node *largest_order; + struct vieter_heap_node *next_largest_order; +} vieter_heap_node; + +vieter_heap_node *vieter_heap_node_init(); + +void vieter_heap_node_free(vieter_heap_node *node); + +vieter_heap_node *vieter_heap_node_merge_same_order(vieter_heap_node *root_a, vieter_heap_node *root_b); + +typedef struct vieter_heap_tree { + uint64_t order; + vieter_heap_node *root; + struct vieter_heap_tree *next; +} vieter_heap_tree; + +vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, vieter_heap_tree *next, uint64_t order); + +vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, vieter_heap_tree *tree_b); + +#endif From ef625ed14e18e654c0f21b357ea509e551267da5 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 22 Jan 2023 10:08:15 +0100 Subject: [PATCH 03/24] chore: allow tests to access internal methods --- Makefile | 10 +++++++--- test/heap/test_heap.c | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index edca96b..30b6a31 100644 --- a/Makefile +++ b/Makefile @@ -56,11 +56,15 @@ build-test: $(BINS_TEST) $(BINS_TEST): %: %.c.o $(OBJS) $(CC) $^ -o $@ -# Each test includes the test directory, which contains the acutest header file +# Allow with the include directory, each test includes $(TEST_DIR) (which +# contains the acutest.h header file), and the src directory of the module it's +# testing. This allows tests to access internal methods, which aren't publicly +# exposed. $(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c mkdir -p $(dir $@) - $(CC) $(CFLAGS) -I$(TEST_DIR) -c $< -o $@ - + $(CC) $(CFLAGS) -I$(TEST_DIR) \ + -I$(dir $(@:$(BUILD_DIR)/$(TEST_DIR)/%=$(SRC_DIR)/%)) \ + -c $< -o $@ # =====MAINTENANCE===== .PHONY: lint diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c index 4079569..832404d 100644 --- a/test/heap/test_heap.c +++ b/test/heap/test_heap.c @@ -1,5 +1,6 @@ #include "acutest.h" #include "vieter_heap.h" +#include "vieter_heap_tree.h" #define TEST_SIZE(heap, size) \ TEST_CHECK(vieter_heap_size(heap) == size); \ From 2cc974accc6ff049c4bacbb28ab18ed4bbe7f9de Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 22 Jan 2023 11:56:11 +0100 Subject: [PATCH 04/24] chore: updated readme --- README.md | 36 +++++++++++++++---- ...{expression.c => vieter_cron_expression.c} | 0 src/cron/{parse.c => vieter_cron_parse.c} | 0 src/heap/vieter_heap_tree.h | 2 +- 4 files changed, 30 insertions(+), 8 deletions(-) rename src/cron/{expression.c => vieter_cron_expression.c} (100%) rename src/cron/{parse.c => vieter_cron_parse.c} (100%) diff --git a/README.md b/README.md index 724ba54..9381fb1 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,12 @@ # libvieter -This library powers part of Vieter, most noteably the sections that can easily +This library powers part of Vieter, most notably the sections that can easily be implemented in C (or just parts I want to implement in C because it's fun). -The goal of this library is to be completely self-contained, meaning any -required data structures have to be implemented as well. It can only depend on -the C standard libraries. +The goal of this library is to be as self-contained as possible; data +structures should be implemented manually if possible. -Currently it contains the following: - -* Cron expression parser & next time calculator +See the [source code](/src) for the list of modules. ## Development @@ -18,6 +15,27 @@ Currently it contains the following: Everything is handled by the provided Makefile. To compile the static library, simply run `make`. +### Project structure + +Each module has its own subdirectory inside `src`, e.g. `src/cron`. This +directory contains the actual implementation of a module, along with any +internally used header files. Each internal function should be defined in a +header file, as to make testing these possible. + +Each module should also have its own header file inside the `include` +directory. This header file defines the public API that the library exposes for +this specific module. + +Any code in a module may only import internal headers from that module, along +with any of the public API header files. Modules should not depend on each +other's internal implementationns. + +Each module should contain a README describing its contents. + +All file names, function names... (even internals) should follow snake case +convention and have a prefix unique to that module, starting with `vieter_`. +For example, the `cron` modules uses the `vieter_cron_` prefix for everything. + ### Testing This library uses [Acutest](https://github.com/mity/acutest) for its tests. @@ -25,6 +43,10 @@ Tests should be placed in the `test` subdirectory, further divided into directories that correspond those in `src`. Test files should begin with `test_`, and their format should follow the expected format for Acutest. +Each `test_` is compiled separately into a binary, linked with libvieter. A +test file can import any of the public API header files, along with any header +files defined in its respective module. This allows testing internal functions. + To run the tests, simply run `make test`. If you wish to only run a specific test binary, you can find them in `build/test`. diff --git a/src/cron/expression.c b/src/cron/vieter_cron_expression.c similarity index 100% rename from src/cron/expression.c rename to src/cron/vieter_cron_expression.c diff --git a/src/cron/parse.c b/src/cron/vieter_cron_parse.c similarity index 100% rename from src/cron/parse.c rename to src/cron/vieter_cron_parse.c diff --git a/src/heap/vieter_heap_tree.h b/src/heap/vieter_heap_tree.h index 73e1611..18808e1 100644 --- a/src/heap/vieter_heap_tree.h +++ b/src/heap/vieter_heap_tree.h @@ -15,7 +15,7 @@ vieter_heap_node *vieter_heap_node_init(); void vieter_heap_node_free(vieter_heap_node *node); -vieter_heap_node *vieter_heap_node_merge_same_order(vieter_heap_node *root_a, vieter_heap_node *root_b); +vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, vieter_heap_node *root_b); typedef struct vieter_heap_tree { uint64_t order; From 86097693890af2d5d555f57bc779dc768d12ec55 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 22 Jan 2023 12:17:56 +0100 Subject: [PATCH 05/24] chore: better separate ci jobs --- .woodpecker/lint.yml | 15 +++++++++++++++ .woodpecker/test-mem.yml | 18 ++++++++++++++++++ .woodpecker.yml => .woodpecker/test.yml | 9 +-------- Makefile | 5 +++++ 4 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 .woodpecker/lint.yml create mode 100644 .woodpecker/test-mem.yml rename .woodpecker.yml => .woodpecker/test.yml (81%) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml new file mode 100644 index 0000000..7c0f90e --- /dev/null +++ b/.woodpecker/lint.yml @@ -0,0 +1,15 @@ +variables: + &image 'git.rustybever.be/chewing_bever/c-devop:alpine3.17' + +branches: + exclude: [ main ] +platform: linux/amd64 + +pipeline: + lint: + image: *image + pull: true + commands: + - make lint + when: + event: [push, pull_request] diff --git a/.woodpecker/test-mem.yml b/.woodpecker/test-mem.yml new file mode 100644 index 0000000..a9ce77b --- /dev/null +++ b/.woodpecker/test-mem.yml @@ -0,0 +1,18 @@ +variables: + &image 'git.rustybever.be/chewing_bever/c-devop:alpine3.17' + +branches: + exclude: [ main ] +platform: linux/amd64 + +depends_on: + - test + +pipeline: + test: + image: *image + pull: true + commands: + - make test-mem + when: + event: [push, pull_request] diff --git a/.woodpecker.yml b/.woodpecker/test.yml similarity index 81% rename from .woodpecker.yml rename to .woodpecker/test.yml index a7e8fe3..6f636ca 100644 --- a/.woodpecker.yml +++ b/.woodpecker/test.yml @@ -11,16 +11,9 @@ branches: platform: ${PLATFORM} pipeline: - lint: - image: *image - pull: true - commands: - - make lint - when: - event: [push, pull_request] - build: image: *image + pull: true commands: - make - make clean diff --git a/Makefile b/Makefile index 30b6a31..777d356 100644 --- a/Makefile +++ b/Makefile @@ -48,6 +48,11 @@ $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c test: build-test @ $(foreach bin,$(BINS_TEST),./$(bin);) +.PHONY: test-mem +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) From 13a63d548c98699b5faf1bd5c680594a58b12e85 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 22 Jan 2023 12:32:47 +0100 Subject: [PATCH 06/24] docs: added docstrings to public headers --- include/vieter_cron.h | 24 ++++++++++++++++++++++-- include/vieter_heap.h | 18 ++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/include/vieter_cron.h b/include/vieter_cron.h index 9088d4d..8c50fff 100644 --- a/include/vieter_cron.h +++ b/include/vieter_cron.h @@ -35,18 +35,38 @@ typedef struct vieter_cron_simple_time { int minute; } vieter_cron_simple_time; +/* + * Allocate and initialize a new empty cron expression. + */ vieter_cron_expression *ce_init(); +/* + * Deallocate a cron expression. + */ void vieter_cron_expr_free(vieter_cron_expression *ce); +/* + * Given a cron expression and a reference time, calculate the next time after + * the reference time that this expression matches. + */ void vieter_cron_expr_next(vieter_cron_simple_time *out, vieter_cron_expression *ce, vieter_cron_simple_time *ref); +/* + * Convencience wrapper around vieter_cron_expr_next that uses the current time + * as the reference time. + */ void vieter_cron_expr_next_from_now(vieter_cron_simple_time *out, vieter_cron_expression *ce); -enum vieter_cron_parse_error vieter_cron_expr_parse(vieter_cron_expression *out, - const char *expression); +/* + * Try to parse a string into a cron expression. Note that the cron expression + * is updated in-place, meaning it can contain invalid information if the + * function returns an error. The cron expression should only be used if the + * function succeeded. + */ +vieter_cron_parse_error vieter_cron_expr_parse(vieter_cron_expression *out, + const char *expression); #endif diff --git a/include/vieter_heap.h b/include/vieter_heap.h index c7a9706..4c8d1e5 100644 --- a/include/vieter_heap.h +++ b/include/vieter_heap.h @@ -11,17 +11,35 @@ typedef enum vieter_heap_error { vieter_heap_empty = 1 } vieter_heap_error; +/* + * Allocate and initalize an empty heap. + */ vieter_heap *vieter_heap_init(); +/* + * Deallocate a heap. + */ void vieter_heap_free(vieter_heap *heap); +/* + * Return how many elements are currently in the heap. + */ uint64_t vieter_heap_size(vieter_heap *heap); +/* + * Insert a new value into the heap. + */ vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, void *data); +/* + * Remove the smallest element from the heap. + */ vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap); +/* + * Get the smallest element in the heap without removing it. + */ vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap); #endif From d11d0749608136d3a9c4e64e3411487a161d28cb Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 22 Jan 2023 12:35:55 +0100 Subject: [PATCH 07/24] chore: some reorganising --- README.md | 3 +++ include/vieter_cron.h | 1 - include/vieter_heap.h | 1 - src/cron/vieter_cron_parse.c | 4 ++++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9381fb1..0c33ab0 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,9 @@ All file names, function names... (even internals) should follow snake case convention and have a prefix unique to that module, starting with `vieter_`. For example, the `cron` modules uses the `vieter_cron_` prefix for everything. +Header files should only import what they explicitely need. If some function is +only used in a .c file, the import should be placed in the .c file instead. + ### Testing This library uses [Acutest](https://github.com/mity/acutest) for its tests. diff --git a/include/vieter_cron.h b/include/vieter_cron.h index 8c50fff..81a2630 100644 --- a/include/vieter_cron.h +++ b/include/vieter_cron.h @@ -1,7 +1,6 @@ #ifndef VIETER_CRON #define VIETER_CRON -#include #include #include #include diff --git a/include/vieter_heap.h b/include/vieter_heap.h index 4c8d1e5..5d08ebc 100644 --- a/include/vieter_heap.h +++ b/include/vieter_heap.h @@ -1,7 +1,6 @@ #ifndef VIETER_HEAP #define VIETER_HEAP -#include #include typedef struct vieter_heap vieter_heap; diff --git a/src/cron/vieter_cron_parse.c b/src/cron/vieter_cron_parse.c index 5c8da06..a82d1c3 100644 --- a/src/cron/vieter_cron_parse.c +++ b/src/cron/vieter_cron_parse.c @@ -1,4 +1,8 @@ #include "vieter_cron.h" +#include +#include +#include +#include // This prefix is needed to properly compile const uint8_t parse_month_days[] = {31, 28, 31, 30, 31, 30, From 0c673a2751ae9381b082ae3e5851f51367938ff1 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 22 Jan 2023 12:46:09 +0100 Subject: [PATCH 08/24] chore: make lint job check for warnings --- .woodpecker/lint.yml | 1 + Makefile | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 7c0f90e..70feabe 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -11,5 +11,6 @@ pipeline: pull: true commands: - make lint + - make CFLAGS='-Werror' when: event: [push, pull_request] diff --git a/Makefile b/Makefile index 777d356..f6ed2d9 100644 --- a/Makefile +++ b/Makefile @@ -25,8 +25,8 @@ 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 += $(INC_FLAGS) +CFLAGS ?= -MMD -MP +VIETERCFLAGS := $(INC_FLAGS) $(CFLAGS) -Wall -Wextra .PHONY: all all: vieter @@ -40,7 +40,7 @@ $(BUILD_DIR)/$(LIB_FILENAME): $(OBJS) $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c mkdir -p $(dir $@) - $(CC) $(CFLAGS) -c $< -o $@ + $(CC) $(VIETERCFLAGS) -c $< -o $@ # =====TESTING===== @@ -67,7 +67,7 @@ $(BINS_TEST): %: %.c.o $(OBJS) # exposed. $(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c mkdir -p $(dir $@) - $(CC) $(CFLAGS) -I$(TEST_DIR) \ + $(CC) $(VIETERCFLAGS) -I$(TEST_DIR) \ -I$(dir $(@:$(BUILD_DIR)/$(TEST_DIR)/%=$(SRC_DIR)/%)) \ -c $< -o $@ From 16b78b8431362c479890c517095bf179b2d356d5 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 22 Jan 2023 13:02:11 +0100 Subject: [PATCH 09/24] chore: improve ci lint --- .woodpecker/lint.yml | 2 +- Makefile | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 70feabe..b9d2806 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -11,6 +11,6 @@ pipeline: pull: true commands: - make lint - - make CFLAGS='-Werror' + - make objs CFLAGS='-Werror -fsyntax-only' when: event: [push, pull_request] diff --git a/Makefile b/Makefile index f6ed2d9..4a26c17 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,10 @@ all: vieter # =====COMPILATION===== +# Utility used by the CI to lint +.PHONY: objs +objs: $(OBJS) + .PHONY: vieter vieter: $(BUILD_DIR)/$(LIB_FILENAME) $(BUILD_DIR)/$(LIB_FILENAME): $(OBJS) From c1ad26cf0c8f4e5578230a1d3d1b69c478f30a30 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 22 Jan 2023 20:34:05 +0100 Subject: [PATCH 10/24] feat(heap): initially working insert --- Makefile | 2 +- src/heap/vieter_heap.c | 30 +++++++++- src/heap/vieter_heap_tree.c | 113 ++++++++++++++++++++++++++++++------ src/heap/vieter_heap_tree.h | 5 +- test/heap/test_heap.c | 34 +++++++++++ 5 files changed, 161 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index 4a26c17..5795f7c 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 +CFLAGS ?= -MMD -MP -g VIETERCFLAGS := $(INC_FLAGS) $(CFLAGS) -Wall -Wextra .PHONY: all diff --git a/src/heap/vieter_heap.c b/src/heap/vieter_heap.c index 6d543df..ceecdbb 100644 --- a/src/heap/vieter_heap.c +++ b/src/heap/vieter_heap.c @@ -9,7 +9,18 @@ struct vieter_heap { vieter_heap *vieter_heap_init() { return calloc(1, sizeof(vieter_heap)); } -uint64_t vieter_heap_size(vieter_heap *heap) { return 0; } +uint64_t vieter_heap_size(vieter_heap *heap) { + uint64_t size = 0; + vieter_heap_tree *tree = heap->tree; + + while (tree != NULL) { + size |= 1 << tree->order; + + tree = tree->next; + } + + return size; +} void vieter_heap_free(vieter_heap *heap) {} @@ -34,5 +45,22 @@ vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { } vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap) { + if (heap->tree == NULL) { + return vieter_heap_empty; + } + + uint64_t smallest_key; + + vieter_heap_tree *tree = heap->tree; + + while (tree != NULL) { + if (tree->root->key < smallest_key) { + smallest_key = tree->root->key; + *out = tree->root->data; + } + + tree = tree->next; + } + return vieter_heap_ok; } diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c index e65fb56..b3e3d1e 100644 --- a/src/heap/vieter_heap_tree.c +++ b/src/heap/vieter_heap_tree.c @@ -4,24 +4,6 @@ vieter_heap_node *vieter_heap_node_init() { return calloc(1, sizeof(vieter_heap_node)); } -vieter_heap_node *vieter_heap_node_merge_same_order(vieter_heap_node *root_a, - vieter_heap_node *root_b) { - vieter_heap_node *new_root, *new_child; - - if (root_a->key <= root_b->key) { - new_root = root_a; - new_child = root_b; - } else { - new_root = root_b; - new_child = root_a; - } - - new_root->next_largest_order = new_root->largest_order; - new_root->largest_order = new_child; - - return new_root; -} - vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, vieter_heap_tree *next, uint64_t order) { @@ -34,5 +16,98 @@ vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, return tree; } +void vieter_heap_tree_swap(vieter_heap_tree *t1, vieter_heap_tree *t2) { + vieter_heap_tree temp = { + .order = t1->order, .root = t1->root, .next = t1->next}; + + t1->order = t2->order; + t1->root = t2->root; + t1->next = t2->next; + + t2->order = temp.order; + t2->root = temp.root; + t2->next = temp.next; +} + +vieter_heap_tree *vieter_heap_tree_merge_same_order(vieter_heap_tree *tree_a, + vieter_heap_tree *tree_b) { + vieter_heap_tree *new_tree; + + if (tree_a->root->key <= tree_b->root->key) { + new_tree = tree_a; + tree_a->root->next_largest_order = tree_a->root->largest_order; + tree_a->root->largest_order = tree_b->root; + + free(tree_b); + } else { + new_tree = tree_b; + tree_b->root->next_largest_order = tree_b->root->largest_order; + tree_b->root->largest_order = tree_a->root; + + free(tree_a); + } + + new_tree->order++; + + return new_tree; +} + vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, - vieter_heap_tree *tree_b) {} + vieter_heap_tree *tree_b) { + vieter_heap_tree *tree, *target; + + if (tree_a->order <= tree_b->order) { + target = tree_a; + tree = tree_b; + } else { + target = tree_b; + tree = tree_a; + } + + vieter_heap_tree *next_tree, *next_target; + vieter_heap_tree *previous_target = NULL; + + while (target != NULL && target != NULL) { + if (target->order == tree->order) { + next_tree = tree->next; + next_target = target->next; + + target = vieter_heap_tree_merge_same_order(target, tree); + + target->next = next_target; + + if (previous_target != NULL) { + previous_target->next = target; + } + + tree = next_tree; + + // If this merge produces a binomial tree whose size is already in + // target, it will be the next target. Therefore, we can target's + // trees until we no longer have a duplicate depth. + while (target->next != NULL && target->next->order == target->order) { + next_target = target->next; + target = vieter_heap_tree_merge_same_order(target, target->next); + target->next = next_target; + } + } else if (target->order > tree->order) { + if (previous_target == NULL) { + previous_target = tree; + } else { + previous_target->next = tree; + } + + tree->next = target; + + tree = tree->next; + } else { + previous_target = target; + target = target->next; + } + } + + // Append final part of tree to target + target->next = tree; + + return target; +} diff --git a/src/heap/vieter_heap_tree.h b/src/heap/vieter_heap_tree.h index 18808e1..03b61a4 100644 --- a/src/heap/vieter_heap_tree.h +++ b/src/heap/vieter_heap_tree.h @@ -15,8 +15,6 @@ vieter_heap_node *vieter_heap_node_init(); void vieter_heap_node_free(vieter_heap_node *node); -vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, vieter_heap_node *root_b); - typedef struct vieter_heap_tree { uint64_t order; vieter_heap_node *root; @@ -27,4 +25,7 @@ vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, vieter_heap_tree vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, vieter_heap_tree *tree_b); +vieter_heap_tree *vieter_heap_tree_merge_same_order(vieter_heap_tree *tree_a, + vieter_heap_tree *tree_b); + #endif diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c index 832404d..7367f28 100644 --- a/test/heap/test_heap.c +++ b/test/heap/test_heap.c @@ -13,7 +13,41 @@ void test_init() { vieter_heap_free(heap); } +void test_merge_same_order() { + vieter_heap_node *root_a = vieter_heap_node_init(); + root_a->key = 1; + vieter_heap_tree *tree_a = vieter_heap_tree_init(root_a, NULL, 1); + + vieter_heap_node *root_b = vieter_heap_node_init(); + root_b->key = 2; + vieter_heap_tree *tree_b = vieter_heap_tree_init(root_b, NULL, 1); + + vieter_heap_tree *merged = vieter_heap_tree_merge_same_order(tree_a, tree_b); + + TEST_CHECK(merged == tree_a); + TEST_CHECK(merged->root->key == 1); + TEST_CHECK(merged->root->largest_order == root_b); + TEST_CHECK(merged->root->next_largest_order == NULL); +} + +void test_insert() { + vieter_heap *heap = vieter_heap_init(); + TEST_SIZE(heap, 0); + + void *data; + + for (uint64_t i = 50; i <= 0; i--) { + vieter_heap_insert(heap, i, (void *)i); + TEST_SIZE(heap, (uint64_t)51 - i); + + TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); + TEST_CHECK(data == (void*)i); + } +} + TEST_LIST = { {"test_init", test_init}, + {"test_merge_same_order", test_merge_same_order}, + {"test_insert", test_insert}, {NULL, NULL} }; From 3c8c33b47aa76ed8677c0abd372580972c724999 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Tue, 24 Jan 2023 12:07:30 +0100 Subject: [PATCH 11/24] fix(heap): some insert fixes --- src/heap/vieter_heap.c | 39 +++++++++++++++---- src/heap/vieter_heap_tree.c | 76 +++++++++++++++++++++++++++++-------- src/heap/vieter_heap_tree.h | 11 +++++- test/heap/test_heap.c | 39 +++++++++++++++++-- 4 files changed, 135 insertions(+), 30 deletions(-) diff --git a/src/heap/vieter_heap.c b/src/heap/vieter_heap.c index ceecdbb..8e036b6 100644 --- a/src/heap/vieter_heap.c +++ b/src/heap/vieter_heap.c @@ -14,7 +14,7 @@ uint64_t vieter_heap_size(vieter_heap *heap) { vieter_heap_tree *tree = heap->tree; while (tree != NULL) { - size |= 1 << tree->order; + size |= (uint64_t)1 << tree->order; tree = tree->next; } @@ -22,14 +22,25 @@ uint64_t vieter_heap_size(vieter_heap *heap) { return size; } -void vieter_heap_free(vieter_heap *heap) {} +void vieter_heap_free(vieter_heap *heap) { + vieter_heap_tree *tree = heap->tree; + vieter_heap_tree *next; + + while (tree != NULL) { + next = tree->next; + vieter_heap_tree_free(tree); + tree = next; + } + + free(heap); +} vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, void *data) { vieter_heap_node *new_node = vieter_heap_node_init(); new_node->key = key; new_node->data = data; - vieter_heap_tree *new_tree = vieter_heap_tree_init(new_node, NULL, 1); + vieter_heap_tree *new_tree = vieter_heap_tree_init(new_node, NULL, 0); if (heap->tree == NULL) { heap->tree = new_tree; @@ -41,6 +52,18 @@ vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, } vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { + if (heap->tree == NULL) { + return vieter_heap_empty; + } + + if (heap->tree->order == 0) { + *out = heap->tree->root->data; + vieter_heap_tree_free(heap->tree); + heap->tree = NULL; + + return vieter_heap_ok; + } + return vieter_heap_ok; } @@ -49,17 +72,17 @@ vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap) { return vieter_heap_empty; } - uint64_t smallest_key; - vieter_heap_tree *tree = heap->tree; + uint64_t smallest_key = tree->root->key; + *out = tree->root->data; + + while (tree->next != NULL) { + tree = tree->next; - while (tree != NULL) { if (tree->root->key < smallest_key) { smallest_key = tree->root->key; *out = tree->root->data; } - - tree = tree->next; } return vieter_heap_ok; diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c index b3e3d1e..8dcc29b 100644 --- a/src/heap/vieter_heap_tree.c +++ b/src/heap/vieter_heap_tree.c @@ -4,9 +4,39 @@ vieter_heap_node *vieter_heap_node_init() { return calloc(1, sizeof(vieter_heap_node)); } +void vieter_heap_node_free(vieter_heap_node *node) { free(node); } + +void vieter_heap_tree_free(vieter_heap_tree *tree) { + uint64_t size = 1; + vieter_heap_node **stack = + malloc(((uint64_t)1 << tree->order) * sizeof(vieter_heap_node *)); + stack[0] = tree->root; + + vieter_heap_node *node; + + while (size > 0) { + node = stack[size - 1]; + size--; + + if (node->largest_order != NULL) { + stack[size] = node->largest_order; + size++; + } + + if (node->next_largest_order != NULL) { + stack[size] = node->next_largest_order; + size++; + } + + vieter_heap_node_free(node); + } + + free(stack); + free(tree); +} + vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, - vieter_heap_tree *next, - uint64_t order) { + vieter_heap_tree *next, uint8_t order) { vieter_heap_tree *tree = malloc(sizeof(vieter_heap_tree)); tree->root = root; @@ -54,7 +84,7 @@ vieter_heap_tree *vieter_heap_tree_merge_same_order(vieter_heap_tree *tree_a, vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, vieter_heap_tree *tree_b) { - vieter_heap_tree *tree, *target; + vieter_heap_tree *tree, *target, *out; if (tree_a->order <= tree_b->order) { target = tree_a; @@ -67,7 +97,7 @@ vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, vieter_heap_tree *next_tree, *next_target; vieter_heap_tree *previous_target = NULL; - while (target != NULL && target != NULL) { + while (target != NULL && tree != NULL) { if (target->order == tree->order) { next_tree = tree->next; next_target = target->next; @@ -76,38 +106,52 @@ vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, target->next = next_target; - if (previous_target != NULL) { - previous_target->next = target; - } - - tree = next_tree; - // If this merge produces a binomial tree whose size is already in - // target, it will be the next target. Therefore, we can target's + // target, it will be the next target. Therefore, we can merge target's // trees until we no longer have a duplicate depth. while (target->next != NULL && target->next->order == target->order) { - next_target = target->next; + next_target = target->next->next; target = vieter_heap_tree_merge_same_order(target, target->next); target->next = next_target; } + + if (previous_target != NULL) { + previous_target->next = target; + } else { + out = target; + } + + tree = next_tree; } else if (target->order > tree->order) { + next_tree = tree->next; + if (previous_target == NULL) { previous_target = tree; + out = tree; } else { previous_target->next = tree; } tree->next = target; - - tree = tree->next; + tree = next_tree; } else { + if (previous_target == NULL) { + out = target; + } + previous_target = target; target = target->next; } } // Append final part of tree to target - target->next = tree; + if (target == NULL) { + previous_target->next = tree; + } + + return out; +} + +vieter_heap_tree *vieter_heap_tree_pop(vieter_heap_tree *tree) { - return target; } diff --git a/src/heap/vieter_heap_tree.h b/src/heap/vieter_heap_tree.h index 03b61a4..117e1a9 100644 --- a/src/heap/vieter_heap_tree.h +++ b/src/heap/vieter_heap_tree.h @@ -16,16 +16,23 @@ vieter_heap_node *vieter_heap_node_init(); void vieter_heap_node_free(vieter_heap_node *node); typedef struct vieter_heap_tree { - uint64_t order; + uint8_t order; vieter_heap_node *root; struct vieter_heap_tree *next; } vieter_heap_tree; -vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, vieter_heap_tree *next, uint64_t order); +vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, vieter_heap_tree *next, uint8_t order); + +/* + * Deallocate a tree object, along with its underlying tree structure. + */ +void vieter_heap_tree_free(vieter_heap_tree *tree); vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, vieter_heap_tree *tree_b); vieter_heap_tree *vieter_heap_tree_merge_same_order(vieter_heap_tree *tree_a, vieter_heap_tree *tree_b); +vieter_heap_tree *vieter_heap_tree_pop(vieter_heap_tree *tree); + #endif diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c index 7367f28..38d9095 100644 --- a/test/heap/test_heap.c +++ b/test/heap/test_heap.c @@ -28,6 +28,8 @@ void test_merge_same_order() { TEST_CHECK(merged->root->key == 1); TEST_CHECK(merged->root->largest_order == root_b); TEST_CHECK(merged->root->next_largest_order == NULL); + + vieter_heap_tree_free(merged); } void test_insert() { @@ -36,18 +38,47 @@ void test_insert() { void *data; - for (uint64_t i = 50; i <= 0; i--) { + for (uint64_t i = 50; i > 0; i--) { vieter_heap_insert(heap, i, (void *)i); TEST_SIZE(heap, (uint64_t)51 - i); + data = 0; + + TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); + TEST_CHECK_(data == (void *)i, "%lX == %lX", (uint64_t)data, i); + } + + vieter_heap_free(heap); +} + +void test_pop() { + vieter_heap *heap = vieter_heap_init(); + TEST_SIZE(heap, 0); + + void *data; + + for (uint64_t i = 50; i > 0; i--) { + vieter_heap_insert(heap, i, (void *)i); + TEST_SIZE(heap, (uint64_t)50 - i); + TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); TEST_CHECK(data == (void*)i); } + + data = NULL; + + for (uint64_t i = 1; i <= 50; i++) { + TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); + TEST_CHECK(data == (void*)i); + TEST_SIZE(heap, (uint64_t)50 - i); + } + + vieter_heap_free(heap); } TEST_LIST = { - {"test_init", test_init}, - {"test_merge_same_order", test_merge_same_order}, - {"test_insert", test_insert}, + {"init", test_init}, + {"merge same order", test_merge_same_order}, + {"insert", test_insert}, {NULL, NULL} }; From 6cf4eaaf0b36faeea39ec3c262ec474428c5875c Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Tue, 24 Jan 2023 14:06:46 +0100 Subject: [PATCH 12/24] chore: separate target for each test binary --- Makefile | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 5795f7c..cf67ffa 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,8 @@ OBJS_TEST := $(SRCS_TEST:%=$(BUILD_DIR)/%.o) DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d) BINS_TEST := $(OBJS_TEST:%.c.o=%) +TARGETS_TEST := $(BINS_TEST:%=test-%) +TARGETS_MEM_TEST := $(BINS_TEST:%=test-mem-%) INC_FLAGS := $(addprefix -I,$(INC_DIRS)) @@ -49,13 +51,18 @@ $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c # =====TESTING===== .PHONY: test -test: build-test - @ $(foreach bin,$(BINS_TEST),./$(bin);) +test: $(TARGETS_TEST) .PHONY: test-mem -test-mem: build-test - @ $(foreach bin,$(BINS_TEST),valgrind --tool=memcheck --error-exitcode=1 \ - --track-origins=yes --leak-check=full ./$(bin);) +test-mem: $(TARGETS_MEM_TEST) + +.PHONY: $(TARGETS_TEST) +$(TARGETS_TEST): test-%: % + ./$^ + +.PHONY: $(TARGETS_MEM_TEST) +$(TARGETS_MEM_TEST): test-mem-%: % + valgrind --tool=memcheck --error-exitcode=1 --track-origins=yes --leak-check=full ./$^ .PHONY: build-test build-test: $(BINS_TEST) From 09c488aa0f6fa7dd4d5b0f296f20665d77bdf511 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Tue, 24 Jan 2023 17:01:37 +0100 Subject: [PATCH 13/24] feat(heap): not quite working pop --- src/heap/vieter_heap.c | 10 ++++---- src/heap/vieter_heap_tree.c | 48 +++++++++++++++++++++++++++++++++++-- src/heap/vieter_heap_tree.h | 2 +- test/heap/test_heap.c | 3 ++- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/heap/vieter_heap.c b/src/heap/vieter_heap.c index 8e036b6..dee109c 100644 --- a/src/heap/vieter_heap.c +++ b/src/heap/vieter_heap.c @@ -57,13 +57,15 @@ vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { } if (heap->tree->order == 0) { - *out = heap->tree->root->data; - vieter_heap_tree_free(heap->tree); - heap->tree = NULL; + *out = heap->tree->root->data; + vieter_heap_tree_free(heap->tree); + heap->tree = NULL; - return vieter_heap_ok; + return vieter_heap_ok; } + vieter_heap_tree_pop(out, heap->tree); + return vieter_heap_ok; } diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c index 8dcc29b..9a170e3 100644 --- a/src/heap/vieter_heap_tree.c +++ b/src/heap/vieter_heap_tree.c @@ -123,7 +123,7 @@ vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, tree = next_tree; } else if (target->order > tree->order) { - next_tree = tree->next; + next_tree = tree->next; if (previous_target == NULL) { previous_target = tree; @@ -152,6 +152,50 @@ vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, return out; } -vieter_heap_tree *vieter_heap_tree_pop(vieter_heap_tree *tree) { +vieter_heap_tree *vieter_heap_tree_pop(void **out, vieter_heap_tree *tree) { + vieter_heap_tree *tree_before_smallest = NULL; + vieter_heap_tree *previous_tree = NULL; + vieter_heap_tree *tree_out = tree; + uint64_t smallest_key = tree->root->key; + + while (tree->next != NULL) { + previous_tree = tree; + tree = tree->next; + + if (tree->root->key < smallest_key) { + smallest_key = tree->root->key; + *out = tree->root->data; + tree_before_smallest = previous_tree; + } + } + + if (tree_before_smallest != NULL) { + previous_tree->next = tree->next; + } else { + tree_out = tree_out->next; + } + + if (tree->order == 0) { + vieter_heap_tree_free(tree); + + return NULL; + } + + uint8_t old_order = tree->order; + + vieter_heap_node *node = tree->root->largest_order; + free(tree->root); + + previous_tree = vieter_heap_tree_init(node, NULL, old_order - 1); + + uint8_t i = 2; + while (node->next_largest_order != NULL) { + node = node->next_largest_order; + previous_tree = vieter_heap_tree_init(node, previous_tree, old_order - i); + + i++; + } + + return vieter_heap_tree_merge(tree_out, previous_tree); } diff --git a/src/heap/vieter_heap_tree.h b/src/heap/vieter_heap_tree.h index 117e1a9..10c6256 100644 --- a/src/heap/vieter_heap_tree.h +++ b/src/heap/vieter_heap_tree.h @@ -33,6 +33,6 @@ vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, vieter_heap_t vieter_heap_tree *vieter_heap_tree_merge_same_order(vieter_heap_tree *tree_a, vieter_heap_tree *tree_b); -vieter_heap_tree *vieter_heap_tree_pop(vieter_heap_tree *tree); +vieter_heap_tree *vieter_heap_tree_pop(void **out, vieter_heap_tree *tree); #endif diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c index 38d9095..dbc26b8 100644 --- a/test/heap/test_heap.c +++ b/test/heap/test_heap.c @@ -59,7 +59,7 @@ void test_pop() { for (uint64_t i = 50; i > 0; i--) { vieter_heap_insert(heap, i, (void *)i); - TEST_SIZE(heap, (uint64_t)50 - i); + TEST_SIZE(heap, (uint64_t)51 - i); TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); TEST_CHECK(data == (void*)i); @@ -80,5 +80,6 @@ TEST_LIST = { {"init", test_init}, {"merge same order", test_merge_same_order}, {"insert", test_insert}, + {"pop", test_pop}, {NULL, NULL} }; From 95d8c9972b4fb939546b03fc8b205186e146bf01 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Tue, 24 Jan 2023 17:22:11 +0100 Subject: [PATCH 14/24] refactor(heap): combine tree and node into single struct --- src/heap/vieter_heap.c | 62 +++++------ src/heap/vieter_heap_tree.c | 212 ++++++++++++++++++------------------ src/heap/vieter_heap_tree.h | 24 ++-- test/heap/test_heap.c | 52 ++++----- 4 files changed, 171 insertions(+), 179 deletions(-) diff --git a/src/heap/vieter_heap.c b/src/heap/vieter_heap.c index dee109c..ae69805 100644 --- a/src/heap/vieter_heap.c +++ b/src/heap/vieter_heap.c @@ -4,30 +4,30 @@ #include struct vieter_heap { - vieter_heap_tree *tree; + vieter_heap_node *tree; }; vieter_heap *vieter_heap_init() { return calloc(1, sizeof(vieter_heap)); } uint64_t vieter_heap_size(vieter_heap *heap) { uint64_t size = 0; - vieter_heap_tree *tree = heap->tree; + vieter_heap_node *tree = heap->tree; while (tree != NULL) { size |= (uint64_t)1 << tree->order; - tree = tree->next; + tree = tree->ptr.next_tree; } return size; } void vieter_heap_free(vieter_heap *heap) { - vieter_heap_tree *tree = heap->tree; - vieter_heap_tree *next; + vieter_heap_node *tree = heap->tree; + vieter_heap_node *next; while (tree != NULL) { - next = tree->next; + next = tree->ptr.next_tree; vieter_heap_tree_free(tree); tree = next; } @@ -37,10 +37,10 @@ void vieter_heap_free(vieter_heap *heap) { vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, void *data) { - vieter_heap_node *new_node = vieter_heap_node_init(); - new_node->key = key; - new_node->data = data; - vieter_heap_tree *new_tree = vieter_heap_tree_init(new_node, NULL, 0); + vieter_heap_node *new_tree = vieter_heap_node_init(); + new_tree->key = key; + new_tree->data = data; + new_tree->order = 0; if (heap->tree == NULL) { heap->tree = new_tree; @@ -51,39 +51,39 @@ vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, return vieter_heap_ok; } -vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { - if (heap->tree == NULL) { - return vieter_heap_empty; - } +/* vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { */ +/* if (heap->tree == NULL) { */ +/* return vieter_heap_empty; */ +/* } */ - if (heap->tree->order == 0) { - *out = heap->tree->root->data; - vieter_heap_tree_free(heap->tree); - heap->tree = NULL; +/* if (heap->tree->order == 0) { */ +/* *out = heap->tree->root->data; */ +/* vieter_heap_tree_free(heap->tree); */ +/* heap->tree = NULL; */ - return vieter_heap_ok; - } +/* return vieter_heap_ok; */ +/* } */ - vieter_heap_tree_pop(out, heap->tree); +/* vieter_heap_tree_pop(out, heap->tree); */ - return vieter_heap_ok; -} +/* return vieter_heap_ok; */ +/* } */ vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap) { if (heap->tree == NULL) { return vieter_heap_empty; } - vieter_heap_tree *tree = heap->tree; - uint64_t smallest_key = tree->root->key; - *out = tree->root->data; + vieter_heap_node *tree = heap->tree; + uint64_t smallest_key = tree->key; + *out = tree->data; - while (tree->next != NULL) { - tree = tree->next; + while (tree->ptr.next_tree != NULL) { + tree = tree->ptr.next_tree; - if (tree->root->key < smallest_key) { - smallest_key = tree->root->key; - *out = tree->root->data; + if (tree->key < smallest_key) { + smallest_key = tree->key; + *out = tree->data; } } diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c index 9a170e3..584ab46 100644 --- a/src/heap/vieter_heap_tree.c +++ b/src/heap/vieter_heap_tree.c @@ -6,11 +6,15 @@ vieter_heap_node *vieter_heap_node_init() { void vieter_heap_node_free(vieter_heap_node *node) { free(node); } -void vieter_heap_tree_free(vieter_heap_tree *tree) { +void vieter_heap_tree_free(vieter_heap_node *root) { + if (root->order == 0) { + goto end; + } + uint64_t size = 1; vieter_heap_node **stack = - malloc(((uint64_t)1 << tree->order) * sizeof(vieter_heap_node *)); - stack[0] = tree->root; + malloc(((uint64_t)1 << root->order) * sizeof(vieter_heap_node *)); + stack[0] = root->largest_order; vieter_heap_node *node; @@ -23,8 +27,8 @@ void vieter_heap_tree_free(vieter_heap_tree *tree) { size++; } - if (node->next_largest_order != NULL) { - stack[size] = node->next_largest_order; + if (node->ptr.next_largest_order != NULL) { + stack[size] = node->ptr.next_largest_order; size++; } @@ -32,170 +36,160 @@ void vieter_heap_tree_free(vieter_heap_tree *tree) { } free(stack); - free(tree); + +end: + vieter_heap_node_free(root); } -vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, - vieter_heap_tree *next, uint8_t order) { - vieter_heap_tree *tree = malloc(sizeof(vieter_heap_tree)); +/* vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, */ +/* vieter_heap_tree *next, uint8_t + * order) { */ +/* vieter_heap_tree *tree = malloc(sizeof(vieter_heap_tree)); */ - tree->root = root; - tree->next = next; - tree->order = order; +/* tree->root = root; */ +/* tree->next = next; */ +/* tree->order = order; */ - return tree; -} +/* return tree; */ +/* } */ -void vieter_heap_tree_swap(vieter_heap_tree *t1, vieter_heap_tree *t2) { - vieter_heap_tree temp = { - .order = t1->order, .root = t1->root, .next = t1->next}; +vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, + vieter_heap_node *root_b) { + vieter_heap_node *new_root; - t1->order = t2->order; - t1->root = t2->root; - t1->next = t2->next; - - t2->order = temp.order; - t2->root = temp.root; - t2->next = temp.next; -} - -vieter_heap_tree *vieter_heap_tree_merge_same_order(vieter_heap_tree *tree_a, - vieter_heap_tree *tree_b) { - vieter_heap_tree *new_tree; - - if (tree_a->root->key <= tree_b->root->key) { - new_tree = tree_a; - tree_a->root->next_largest_order = tree_a->root->largest_order; - tree_a->root->largest_order = tree_b->root; - - free(tree_b); + if (root_a->key <= root_b->key) { + new_root = root_a; + root_b->ptr.next_largest_order = root_a->largest_order; + root_a->largest_order = root_b; } else { - new_tree = tree_b; - tree_b->root->next_largest_order = tree_b->root->largest_order; - tree_b->root->largest_order = tree_a->root; - - free(tree_a); + new_root = root_b; + root_a->ptr.next_largest_order = root_b->largest_order; + root_b->largest_order = root_a; } - new_tree->order++; + new_root->order++; - return new_tree; + return new_root; } -vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, - vieter_heap_tree *tree_b) { - vieter_heap_tree *tree, *target, *out; +vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *root_a, + vieter_heap_node *root_b) { + vieter_heap_node *root, *target, *out; - if (tree_a->order <= tree_b->order) { - target = tree_a; - tree = tree_b; + if (root_a->order <= root_b->order) { + target = root_a; + root = root_b; } else { - target = tree_b; - tree = tree_a; + target = root_b; + root = root_a; } - vieter_heap_tree *next_tree, *next_target; - vieter_heap_tree *previous_target = NULL; + vieter_heap_node *next_tree, *next_target; + vieter_heap_node *previous_target = NULL; - while (target != NULL && tree != NULL) { - if (target->order == tree->order) { - next_tree = tree->next; - next_target = target->next; + while (target != NULL && root != NULL) { + if (target->order == root->order) { + next_tree = root->ptr.next_tree; + next_target = target->ptr.next_tree; - target = vieter_heap_tree_merge_same_order(target, tree); + target = vieter_heap_tree_merge_same_order(target, root); - target->next = next_target; + target->ptr.next_tree = next_target; // If this merge produces a binomial tree whose size is already in // target, it will be the next target. Therefore, we can merge target's // trees until we no longer have a duplicate depth. - while (target->next != NULL && target->next->order == target->order) { - next_target = target->next->next; - target = vieter_heap_tree_merge_same_order(target, target->next); - target->next = next_target; + while (target->ptr.next_tree != NULL && + target->ptr.next_tree->order == target->order) { + next_target = target->ptr.next_tree->ptr.next_tree; + target = + vieter_heap_tree_merge_same_order(target, target->ptr.next_tree); + target->ptr.next_tree = next_target; } if (previous_target != NULL) { - previous_target->next = target; + previous_target->ptr.next_tree = target; } else { out = target; } - tree = next_tree; - } else if (target->order > tree->order) { - next_tree = tree->next; + root = next_tree; + } else if (target->order > root->order) { + next_tree = root->ptr.next_tree; if (previous_target == NULL) { - previous_target = tree; - out = tree; + previous_target = root; + out = root; } else { - previous_target->next = tree; + previous_target->ptr.next_tree = root; } - tree->next = target; - tree = next_tree; + root->ptr.next_tree = target; + root = next_tree; } else { if (previous_target == NULL) { out = target; } previous_target = target; - target = target->next; + target = target->ptr.next_tree; } } // Append final part of tree to target if (target == NULL) { - previous_target->next = tree; + previous_target->ptr.next_tree = root; } return out; } -vieter_heap_tree *vieter_heap_tree_pop(void **out, vieter_heap_tree *tree) { - vieter_heap_tree *tree_before_smallest = NULL; - vieter_heap_tree *previous_tree = NULL; - vieter_heap_tree *tree_out = tree; +/* vieter_heap_tree *vieter_heap_tree_pop(void **out, vieter_heap_tree *tree) { + */ +/* vieter_heap_tree *tree_before_smallest = NULL; */ +/* vieter_heap_tree *previous_tree = NULL; */ +/* vieter_heap_tree *tree_out = tree; */ - uint64_t smallest_key = tree->root->key; +/* uint64_t smallest_key = tree->root->key; */ - while (tree->next != NULL) { - previous_tree = tree; - tree = tree->next; +/* while (tree->next != NULL) { */ +/* previous_tree = tree; */ +/* tree = tree->next; */ - if (tree->root->key < smallest_key) { - smallest_key = tree->root->key; - *out = tree->root->data; - tree_before_smallest = previous_tree; - } - } +/* if (tree->root->key < smallest_key) { */ +/* smallest_key = tree->root->key; */ +/* *out = tree->root->data; */ +/* tree_before_smallest = previous_tree; */ +/* } */ +/* } */ - if (tree_before_smallest != NULL) { - previous_tree->next = tree->next; - } else { - tree_out = tree_out->next; - } +/* if (tree_before_smallest != NULL) { */ +/* previous_tree->next = tree->next; */ +/* } else { */ +/* tree_out = tree_out->next; */ +/* } */ - if (tree->order == 0) { - vieter_heap_tree_free(tree); +/* if (tree->order == 0) { */ +/* vieter_heap_tree_free(tree); */ - return NULL; - } +/* return NULL; */ +/* } */ - uint8_t old_order = tree->order; +/* uint8_t old_order = tree->order; */ - vieter_heap_node *node = tree->root->largest_order; - free(tree->root); +/* vieter_heap_node *node = tree->root->largest_order; */ +/* free(tree->root); */ - previous_tree = vieter_heap_tree_init(node, NULL, old_order - 1); +/* previous_tree = vieter_heap_tree_init(node, NULL, old_order - 1); */ - uint8_t i = 2; - while (node->next_largest_order != NULL) { - node = node->next_largest_order; - previous_tree = vieter_heap_tree_init(node, previous_tree, old_order - i); +/* uint8_t i = 2; */ +/* while (node->next_largest_order != NULL) { */ +/* node = node->next_largest_order; */ +/* previous_tree = vieter_heap_tree_init(node, previous_tree, old_order - + * i); */ - i++; - } +/* i++; */ +/* } */ - return vieter_heap_tree_merge(tree_out, previous_tree); -} +/* return vieter_heap_tree_merge(tree_out, previous_tree); */ +/* } */ diff --git a/src/heap/vieter_heap_tree.h b/src/heap/vieter_heap_tree.h index 10c6256..b7597f6 100644 --- a/src/heap/vieter_heap_tree.h +++ b/src/heap/vieter_heap_tree.h @@ -8,31 +8,29 @@ typedef struct vieter_heap_node { uint64_t key; void *data; struct vieter_heap_node *largest_order; - struct vieter_heap_node *next_largest_order; + union { + struct vieter_heap_node *next_tree; + struct vieter_heap_node *next_largest_order; + } ptr; + uint8_t order; } vieter_heap_node; vieter_heap_node *vieter_heap_node_init(); void vieter_heap_node_free(vieter_heap_node *node); -typedef struct vieter_heap_tree { - uint8_t order; - vieter_heap_node *root; - struct vieter_heap_tree *next; -} vieter_heap_tree; - -vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, vieter_heap_tree *next, uint8_t order); +/* vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, vieter_heap_tree *next, uint8_t order); */ /* * Deallocate a tree object, along with its underlying tree structure. */ -void vieter_heap_tree_free(vieter_heap_tree *tree); +void vieter_heap_tree_free(vieter_heap_node *root); -vieter_heap_tree *vieter_heap_tree_merge(vieter_heap_tree *tree_a, vieter_heap_tree *tree_b); +vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *root_a, vieter_heap_node *root_b); -vieter_heap_tree *vieter_heap_tree_merge_same_order(vieter_heap_tree *tree_a, - vieter_heap_tree *tree_b); +vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, + vieter_heap_node *root_b); -vieter_heap_tree *vieter_heap_tree_pop(void **out, vieter_heap_tree *tree); +vieter_heap_node *vieter_heap_tree_pop(void **out, vieter_heap_node *root); #endif diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c index dbc26b8..f795ebe 100644 --- a/test/heap/test_heap.c +++ b/test/heap/test_heap.c @@ -16,18 +16,18 @@ void test_init() { void test_merge_same_order() { vieter_heap_node *root_a = vieter_heap_node_init(); root_a->key = 1; - vieter_heap_tree *tree_a = vieter_heap_tree_init(root_a, NULL, 1); + root_a->order = 0; vieter_heap_node *root_b = vieter_heap_node_init(); root_b->key = 2; - vieter_heap_tree *tree_b = vieter_heap_tree_init(root_b, NULL, 1); + root_b->order = 0; - vieter_heap_tree *merged = vieter_heap_tree_merge_same_order(tree_a, tree_b); + vieter_heap_node *merged = vieter_heap_tree_merge_same_order(root_a, root_b); - TEST_CHECK(merged == tree_a); - TEST_CHECK(merged->root->key == 1); - TEST_CHECK(merged->root->largest_order == root_b); - TEST_CHECK(merged->root->next_largest_order == NULL); + TEST_CHECK(merged == root_a); + TEST_CHECK(merged->key == 1); + TEST_CHECK(merged->largest_order == root_b); + TEST_CHECK(merged->ptr.next_largest_order == NULL); vieter_heap_tree_free(merged); } @@ -51,35 +51,35 @@ void test_insert() { vieter_heap_free(heap); } -void test_pop() { - vieter_heap *heap = vieter_heap_init(); - TEST_SIZE(heap, 0); +/* void test_pop() { */ +/* vieter_heap *heap = vieter_heap_init(); */ +/* TEST_SIZE(heap, 0); */ - void *data; +/* void *data; */ - for (uint64_t i = 50; i > 0; i--) { - vieter_heap_insert(heap, i, (void *)i); - TEST_SIZE(heap, (uint64_t)51 - i); +/* for (uint64_t i = 50; i > 0; i--) { */ +/* vieter_heap_insert(heap, i, (void *)i); */ +/* TEST_SIZE(heap, (uint64_t)51 - i); */ - TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); - TEST_CHECK(data == (void*)i); - } +/* TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); */ +/* TEST_CHECK(data == (void*)i); */ +/* } */ - data = NULL; +/* data = NULL; */ - for (uint64_t i = 1; i <= 50; i++) { - TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); - TEST_CHECK(data == (void*)i); - TEST_SIZE(heap, (uint64_t)50 - i); - } +/* for (uint64_t i = 1; i <= 50; i++) { */ +/* TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); */ +/* TEST_CHECK(data == (void*)i); */ +/* TEST_SIZE(heap, (uint64_t)50 - i); */ +/* } */ - vieter_heap_free(heap); -} +/* vieter_heap_free(heap); */ +/* } */ TEST_LIST = { {"init", test_init}, {"merge same order", test_merge_same_order}, {"insert", test_insert}, - {"pop", test_pop}, + /* {"pop", test_pop}, */ {NULL, NULL} }; From 6845e67cb6af299fdfcce1172c1e132d6ea826a5 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Tue, 24 Jan 2023 19:45:01 +0100 Subject: [PATCH 15/24] feat(heap): possibly working pop --- src/heap/vieter_heap.c | 26 +++++----- src/heap/vieter_heap_tree.c | 95 ++++++++++++++++++------------------- test/heap/test_heap.c | 38 +++++++-------- 3 files changed, 78 insertions(+), 81 deletions(-) diff --git a/src/heap/vieter_heap.c b/src/heap/vieter_heap.c index ae69805..f6d3eab 100644 --- a/src/heap/vieter_heap.c +++ b/src/heap/vieter_heap.c @@ -51,23 +51,23 @@ vieter_heap_error vieter_heap_insert(vieter_heap *heap, uint64_t key, return vieter_heap_ok; } -/* vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { */ -/* if (heap->tree == NULL) { */ -/* return vieter_heap_empty; */ -/* } */ +vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { + if (heap->tree == NULL) { + return vieter_heap_empty; + } -/* if (heap->tree->order == 0) { */ -/* *out = heap->tree->root->data; */ -/* vieter_heap_tree_free(heap->tree); */ -/* heap->tree = NULL; */ + if (heap->tree->order == 0 && heap->tree->ptr.next_tree == NULL) { + *out = heap->tree->data; + vieter_heap_tree_free(heap->tree); + heap->tree = NULL; -/* return vieter_heap_ok; */ -/* } */ + return vieter_heap_ok; + } -/* vieter_heap_tree_pop(out, heap->tree); */ + heap->tree = vieter_heap_tree_pop(out, heap->tree); -/* return vieter_heap_ok; */ -/* } */ + return vieter_heap_ok; +} vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap) { if (heap->tree == NULL) { diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c index 584ab46..56907a8 100644 --- a/src/heap/vieter_heap_tree.c +++ b/src/heap/vieter_heap_tree.c @@ -41,18 +41,6 @@ end: vieter_heap_node_free(root); } -/* vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, */ -/* vieter_heap_tree *next, uint8_t - * order) { */ -/* vieter_heap_tree *tree = malloc(sizeof(vieter_heap_tree)); */ - -/* tree->root = root; */ -/* tree->next = next; */ -/* tree->order = order; */ - -/* return tree; */ -/* } */ - vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, vieter_heap_node *root_b) { vieter_heap_node *new_root; @@ -144,52 +132,61 @@ vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *root_a, return out; } -/* vieter_heap_tree *vieter_heap_tree_pop(void **out, vieter_heap_tree *tree) { - */ -/* vieter_heap_tree *tree_before_smallest = NULL; */ -/* vieter_heap_tree *previous_tree = NULL; */ -/* vieter_heap_tree *tree_out = tree; */ +vieter_heap_node *vieter_heap_tree_pop(void **out, vieter_heap_node *tree) { + vieter_heap_node *tree_before_smallest = NULL; + vieter_heap_node *previous_tree = NULL; + vieter_heap_node *original_root = tree; -/* uint64_t smallest_key = tree->root->key; */ + uint64_t smallest_key = tree->key; -/* while (tree->next != NULL) { */ -/* previous_tree = tree; */ -/* tree = tree->next; */ + while (tree->ptr.next_tree != NULL) { + previous_tree = tree; + tree = tree->ptr.next_tree; -/* if (tree->root->key < smallest_key) { */ -/* smallest_key = tree->root->key; */ -/* *out = tree->root->data; */ -/* tree_before_smallest = previous_tree; */ -/* } */ -/* } */ + if (tree->key < smallest_key) { + smallest_key = tree->key; + tree_before_smallest = previous_tree; + } + } -/* if (tree_before_smallest != NULL) { */ -/* previous_tree->next = tree->next; */ -/* } else { */ -/* tree_out = tree_out->next; */ -/* } */ + vieter_heap_node *tree_to_pop; -/* if (tree->order == 0) { */ -/* vieter_heap_tree_free(tree); */ + if (tree_before_smallest != NULL) { + tree_to_pop = tree_before_smallest->ptr.next_tree; + tree_before_smallest->ptr.next_tree = tree_to_pop->ptr.next_tree; + } else { + tree_to_pop = original_root; + original_root = original_root->ptr.next_tree; + } -/* return NULL; */ -/* } */ + *out = tree_to_pop->data; -/* uint8_t old_order = tree->order; */ + if (tree_to_pop->order == 0) { + vieter_heap_tree_free(tree_to_pop); -/* vieter_heap_node *node = tree->root->largest_order; */ -/* free(tree->root); */ + return original_root; + } -/* previous_tree = vieter_heap_tree_init(node, NULL, old_order - 1); */ + previous_tree = tree_to_pop->largest_order; + vieter_heap_node_free(tree_to_pop); -/* uint8_t i = 2; */ -/* while (node->next_largest_order != NULL) { */ -/* node = node->next_largest_order; */ -/* previous_tree = vieter_heap_tree_init(node, previous_tree, old_order - - * i); */ + tree = previous_tree->ptr.next_largest_order; + previous_tree->ptr.next_tree = NULL; -/* i++; */ -/* } */ + vieter_heap_node *next_tree; -/* return vieter_heap_tree_merge(tree_out, previous_tree); */ -/* } */ + while (tree != NULL) { + next_tree = tree->ptr.next_largest_order; + tree->ptr.next_tree = previous_tree; + + previous_tree = tree; + tree = next_tree; + } + + // original_root is zero if the heap only contained a single tree. + if (original_root != NULL) { + return vieter_heap_tree_merge(original_root, previous_tree); + } else { + return previous_tree; + } +} diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c index f795ebe..74736a3 100644 --- a/test/heap/test_heap.c +++ b/test/heap/test_heap.c @@ -51,35 +51,35 @@ void test_insert() { vieter_heap_free(heap); } -/* void test_pop() { */ -/* vieter_heap *heap = vieter_heap_init(); */ -/* TEST_SIZE(heap, 0); */ +void test_pop() { + vieter_heap *heap = vieter_heap_init(); + TEST_SIZE(heap, 0); -/* void *data; */ + void *data; -/* for (uint64_t i = 50; i > 0; i--) { */ -/* vieter_heap_insert(heap, i, (void *)i); */ -/* TEST_SIZE(heap, (uint64_t)51 - i); */ + for (uint64_t i = 50; i > 0; i--) { + vieter_heap_insert(heap, i, (void *)i); + TEST_SIZE(heap, (uint64_t)51 - i); -/* TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); */ -/* TEST_CHECK(data == (void*)i); */ -/* } */ + TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); + TEST_CHECK(data == (void*)i); + } -/* data = NULL; */ + data = NULL; -/* for (uint64_t i = 1; i <= 50; i++) { */ -/* TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); */ -/* TEST_CHECK(data == (void*)i); */ -/* TEST_SIZE(heap, (uint64_t)50 - i); */ -/* } */ + for (uint64_t i = 1; i <= 50; i++) { + TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); + TEST_CHECK(data == (void*)i); + TEST_SIZE(heap, (uint64_t)50 - i); + } -/* vieter_heap_free(heap); */ -/* } */ + vieter_heap_free(heap); +} TEST_LIST = { {"init", test_init}, {"merge same order", test_merge_same_order}, {"insert", test_insert}, - /* {"pop", test_pop}, */ + {"pop", test_pop}, {NULL, NULL} }; From 63100c5b99aeecb488854a05382229689e81dd3e Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Tue, 24 Jan 2023 21:19:08 +0100 Subject: [PATCH 16/24] test(heap): add random test that exposes some faults --- src/heap/vieter_heap_tree.h | 27 +++++++++++++++++---- test/heap/test_heap.c | 48 +++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/src/heap/vieter_heap_tree.h b/src/heap/vieter_heap_tree.h index b7597f6..c3bc55d 100644 --- a/src/heap/vieter_heap_tree.h +++ b/src/heap/vieter_heap_tree.h @@ -9,28 +9,45 @@ typedef struct vieter_heap_node { void *data; struct vieter_heap_node *largest_order; union { - struct vieter_heap_node *next_tree; - struct vieter_heap_node *next_largest_order; + // Roots point to next tree in the heap, other nodes point to their first + // neighbour. + struct vieter_heap_node *next_tree; + struct vieter_heap_node *next_largest_order; } ptr; uint8_t order; } vieter_heap_node; +/* + * Allocate an initialize a heap node object. + */ vieter_heap_node *vieter_heap_node_init(); +/* + * Deallocate a node object. + */ void vieter_heap_node_free(vieter_heap_node *node); -/* vieter_heap_tree *vieter_heap_tree_init(vieter_heap_node *root, vieter_heap_tree *next, uint8_t order); */ - /* - * Deallocate a tree object, along with its underlying tree structure. + * Deallocate a node's entire structure. */ void vieter_heap_tree_free(vieter_heap_node *root); +/* + * Given the roots of the smallest trees in two heaps, merge them into a single + * large heap. + */ vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *root_a, vieter_heap_node *root_b); +/* + * Given the roots of two trees of the same order, merge them into a heap of one + * order larger. + */ vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, vieter_heap_node *root_b); +/* + * Remove the smallest element from the given heap. + */ vieter_heap_node *vieter_heap_tree_pop(void **out, vieter_heap_node *root); #endif diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c index 74736a3..e136d87 100644 --- a/test/heap/test_heap.c +++ b/test/heap/test_heap.c @@ -1,6 +1,7 @@ #include "acutest.h" #include "vieter_heap.h" #include "vieter_heap_tree.h" +#include #define TEST_SIZE(heap, size) \ TEST_CHECK(vieter_heap_size(heap) == size); \ @@ -76,10 +77,57 @@ void test_pop() { vieter_heap_free(heap); } +int uint64_t_compare(const void *a, const void *b) { + if ((*(uint64_t *)a) < (*(uint64_t *)b)) { + return -1; + } else if ((*(uint64_t *)a) < (*(uint64_t *)b)) { + return 1; + } else { + return 0; + } +} + +void test_pop_random() { + srand(0); + + vieter_heap *heap = vieter_heap_init(); + + uint64_t *numbers = malloc(500 * sizeof(uint64_t)); + uint64_t num; + + for (uint64_t i = 0; i < 500; i++) { + num = rand(); + vieter_heap_insert(heap, num, (void *)num); + TEST_SIZE(heap, i + 1); + + numbers[i] = num; + } + + + qsort(numbers, 500, sizeof(uint64_t), uint64_t_compare); + + void *data = NULL; + + for (uint64_t i = 0; i < 500; i++) { + TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); + TEST_CHECK(data == (void *)numbers[i]); + + data = NULL; + + TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); + TEST_CHECK(data == (void *)numbers[i]); + TEST_SIZE(heap, (uint64_t)500 - i - 1); + } + + vieter_heap_free(heap); + free(numbers); +} + TEST_LIST = { {"init", test_init}, {"merge same order", test_merge_same_order}, {"insert", test_insert}, {"pop", test_pop}, + {"pop random", test_pop_random}, {NULL, NULL} }; From dc557f57ab9e9672e3b0aa332999188a8a928e3f Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Wed, 25 Jan 2023 20:49:18 +0100 Subject: [PATCH 17/24] test(heap): some more tests to expose flaws --- src/heap/vieter_heap.c | 8 ----- src/heap/vieter_heap_tree.c | 3 ++ test/heap/test_heap.c | 71 ++++++++++++++++++++++--------------- test/heap/test_merge.c | 28 +++++++++++++++ 4 files changed, 73 insertions(+), 37 deletions(-) create mode 100644 test/heap/test_merge.c diff --git a/src/heap/vieter_heap.c b/src/heap/vieter_heap.c index f6d3eab..45f9e01 100644 --- a/src/heap/vieter_heap.c +++ b/src/heap/vieter_heap.c @@ -56,14 +56,6 @@ vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap) { return vieter_heap_empty; } - if (heap->tree->order == 0 && heap->tree->ptr.next_tree == NULL) { - *out = heap->tree->data; - vieter_heap_tree_free(heap->tree); - heap->tree = NULL; - - return vieter_heap_ok; - } - heap->tree = vieter_heap_tree_pop(out, heap->tree); return vieter_heap_ok; diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c index 56907a8..ff1ee66 100644 --- a/src/heap/vieter_heap_tree.c +++ b/src/heap/vieter_heap_tree.c @@ -167,6 +167,9 @@ vieter_heap_node *vieter_heap_tree_pop(void **out, vieter_heap_node *tree) { return original_root; } + // Each child has a pointer to its sibling with the next largest order. If we + // want to convert this list of children into their own tree, these pointers + // have to be reversed. previous_tree = tree_to_pop->largest_order; vieter_heap_node_free(tree_to_pop); diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c index e136d87..ad73b8c 100644 --- a/test/heap/test_heap.c +++ b/test/heap/test_heap.c @@ -5,7 +5,7 @@ #define TEST_SIZE(heap, size) \ TEST_CHECK(vieter_heap_size(heap) == size); \ - TEST_MSG("Size: %zu", vieter_heap_size(heap)) + TEST_MSG("Size: %zu, expected: %lu", vieter_heap_size(heap), (uint64_t)size) void test_init() { vieter_heap *heap = vieter_heap_init(); @@ -14,25 +14,6 @@ void test_init() { vieter_heap_free(heap); } -void test_merge_same_order() { - vieter_heap_node *root_a = vieter_heap_node_init(); - root_a->key = 1; - root_a->order = 0; - - vieter_heap_node *root_b = vieter_heap_node_init(); - root_b->key = 2; - root_b->order = 0; - - vieter_heap_node *merged = vieter_heap_tree_merge_same_order(root_a, root_b); - - TEST_CHECK(merged == root_a); - TEST_CHECK(merged->key == 1); - TEST_CHECK(merged->largest_order == root_b); - TEST_CHECK(merged->ptr.next_largest_order == NULL); - - vieter_heap_tree_free(merged); -} - void test_insert() { vieter_heap *heap = vieter_heap_init(); TEST_SIZE(heap, 0); @@ -52,6 +33,36 @@ void test_insert() { vieter_heap_free(heap); } +void test_insert_random() { + srand(1); + + vieter_heap *heap = vieter_heap_init(); + TEST_SIZE(heap, 0); + + uint64_t num = rand(); + uint64_t smallest = num; + + void *data = NULL; + + for (uint64_t i = 0; i < 5000; i++) { + vieter_heap_insert(heap, num, (void *)num); + TEST_SIZE(heap, i + 1); + + if (num < smallest) { + smallest = num; + } + + TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); + TEST_CHECK(data == (void *)smallest); + + data = NULL; + + num = rand(); + } + + vieter_heap_free(heap); +} + void test_pop() { vieter_heap *heap = vieter_heap_init(); TEST_SIZE(heap, 0); @@ -80,7 +91,7 @@ void test_pop() { int uint64_t_compare(const void *a, const void *b) { if ((*(uint64_t *)a) < (*(uint64_t *)b)) { return -1; - } else if ((*(uint64_t *)a) < (*(uint64_t *)b)) { + } else if ((*(uint64_t *)a) > (*(uint64_t *)b)) { return 1; } else { return 0; @@ -88,14 +99,16 @@ int uint64_t_compare(const void *a, const void *b) { } void test_pop_random() { + const uint64_t n = 29; + srand(0); vieter_heap *heap = vieter_heap_init(); - uint64_t *numbers = malloc(500 * sizeof(uint64_t)); + uint64_t *numbers = malloc(n * sizeof(uint64_t)); uint64_t num; - for (uint64_t i = 0; i < 500; i++) { + for (uint64_t i = 0; i < n; i++) { num = rand(); vieter_heap_insert(heap, num, (void *)num); TEST_SIZE(heap, i + 1); @@ -104,19 +117,19 @@ void test_pop_random() { } - qsort(numbers, 500, sizeof(uint64_t), uint64_t_compare); + qsort(numbers, n, sizeof(uint64_t), uint64_t_compare); void *data = NULL; - for (uint64_t i = 0; i < 500; i++) { + for (uint64_t i = 0; i < n; i++) { TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); - TEST_CHECK(data == (void *)numbers[i]); + TEST_CHECK_(data == (void *)numbers[i], "peek %lx == %lx", (uint64_t)data, numbers[i]); data = NULL; TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); - TEST_CHECK(data == (void *)numbers[i]); - TEST_SIZE(heap, (uint64_t)500 - i - 1); + TEST_CHECK_(data == (void *)numbers[i], "pop %lx == %lx", (uint64_t)data, numbers[i]); + TEST_SIZE(heap, n - i - 1); } vieter_heap_free(heap); @@ -125,8 +138,8 @@ void test_pop_random() { TEST_LIST = { {"init", test_init}, - {"merge same order", test_merge_same_order}, {"insert", test_insert}, + {"insert random", test_insert_random}, {"pop", test_pop}, {"pop random", test_pop_random}, {NULL, NULL} diff --git a/test/heap/test_merge.c b/test/heap/test_merge.c new file mode 100644 index 0000000..10053f2 --- /dev/null +++ b/test/heap/test_merge.c @@ -0,0 +1,28 @@ +#include "acutest.h" +#include "vieter_heap.h" +#include "vieter_heap_tree.h" +#include + +void test_merge_same_order() { + vieter_heap_node *root_a = vieter_heap_node_init(); + root_a->key = 1; + root_a->order = 0; + + vieter_heap_node *root_b = vieter_heap_node_init(); + root_b->key = 2; + root_b->order = 0; + + vieter_heap_node *merged = vieter_heap_tree_merge_same_order(root_a, root_b); + + TEST_CHECK(merged == root_a); + TEST_CHECK(merged->key == 1); + TEST_CHECK(merged->largest_order == root_b); + TEST_CHECK(merged->ptr.next_largest_order == NULL); + + vieter_heap_tree_free(merged); +} + +TEST_LIST = { + {"merge same order", test_merge_same_order}, + {NULL, NULL} +}; From 3ec2e76af9d3f1120cc85c50da3de446a334f790 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Wed, 25 Jan 2023 22:12:22 +0100 Subject: [PATCH 18/24] refactor(heap): some better variable names; some more tests --- src/heap/vieter_heap.c | 7 +-- src/heap/vieter_heap_internal.h | 6 +++ src/heap/vieter_heap_tree.c | 93 +++++++++++++++++---------------- test/heap/test_heap.c | 31 ++++++++++- 4 files changed, 83 insertions(+), 54 deletions(-) create mode 100644 src/heap/vieter_heap_internal.h diff --git a/src/heap/vieter_heap.c b/src/heap/vieter_heap.c index 45f9e01..9d4ef8e 100644 --- a/src/heap/vieter_heap.c +++ b/src/heap/vieter_heap.c @@ -1,12 +1,7 @@ -#include "vieter_heap.h" -#include "vieter_heap_tree.h" +#include "vieter_heap_internal.h" #include -struct vieter_heap { - vieter_heap_node *tree; -}; - vieter_heap *vieter_heap_init() { return calloc(1, sizeof(vieter_heap)); } uint64_t vieter_heap_size(vieter_heap *heap) { diff --git a/src/heap/vieter_heap_internal.h b/src/heap/vieter_heap_internal.h new file mode 100644 index 0000000..8aaed53 --- /dev/null +++ b/src/heap/vieter_heap_internal.h @@ -0,0 +1,6 @@ +#include "vieter_heap.h" +#include "vieter_heap_tree.h" + +struct vieter_heap { + vieter_heap_node *tree; +}; diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c index ff1ee66..7c7f8eb 100644 --- a/src/heap/vieter_heap_tree.c +++ b/src/heap/vieter_heap_tree.c @@ -43,90 +43,91 @@ end: vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, vieter_heap_node *root_b) { - vieter_heap_node *new_root; + vieter_heap_node *root, *child; if (root_a->key <= root_b->key) { - new_root = root_a; - root_b->ptr.next_largest_order = root_a->largest_order; - root_a->largest_order = root_b; + root = root_a; + child = root_b; } else { - new_root = root_b; - root_a->ptr.next_largest_order = root_b->largest_order; - root_b->largest_order = root_a; + root = root_b; + child = root_a; } - new_root->order++; + child->ptr.next_largest_order = root->largest_order; + root->largest_order = child; - return new_root; + root->order++; + + return root; } vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *root_a, vieter_heap_node *root_b) { - vieter_heap_node *root, *target, *out; + vieter_heap_node *other_tree, *target_tree, *out; if (root_a->order <= root_b->order) { - target = root_a; - root = root_b; + target_tree = root_a; + other_tree = root_b; } else { - target = root_b; - root = root_a; + target_tree = root_b; + other_tree = root_a; } - vieter_heap_node *next_tree, *next_target; - vieter_heap_node *previous_target = NULL; + vieter_heap_node *next_other_tree, *next_target_tree; + vieter_heap_node *previous_target_tree = NULL; - while (target != NULL && root != NULL) { - if (target->order == root->order) { - next_tree = root->ptr.next_tree; - next_target = target->ptr.next_tree; + while (target_tree != NULL && other_tree != NULL) { + if (target_tree->order == other_tree->order) { + next_other_tree = other_tree->ptr.next_tree; + next_target_tree = target_tree->ptr.next_tree; - target = vieter_heap_tree_merge_same_order(target, root); + target_tree = vieter_heap_tree_merge_same_order(target_tree, other_tree); - target->ptr.next_tree = next_target; + target_tree->ptr.next_tree = next_target_tree; // If this merge produces a binomial tree whose size is already in // target, it will be the next target. Therefore, we can merge target's // trees until we no longer have a duplicate depth. - while (target->ptr.next_tree != NULL && - target->ptr.next_tree->order == target->order) { - next_target = target->ptr.next_tree->ptr.next_tree; - target = - vieter_heap_tree_merge_same_order(target, target->ptr.next_tree); - target->ptr.next_tree = next_target; + while (next_target_tree != NULL && + next_target_tree->order == target_tree->order) { + next_target_tree = next_target_tree->ptr.next_tree; + target_tree = vieter_heap_tree_merge_same_order( + target_tree, target_tree->ptr.next_tree); + target_tree->ptr.next_tree = next_target_tree; } - if (previous_target != NULL) { - previous_target->ptr.next_tree = target; + if (previous_target_tree != NULL) { + previous_target_tree->ptr.next_tree = target_tree; } else { - out = target; + out = target_tree; } - root = next_tree; - } else if (target->order > root->order) { - next_tree = root->ptr.next_tree; + other_tree = next_other_tree; + } else if (target_tree->order > other_tree->order) { + next_other_tree = other_tree->ptr.next_tree; - if (previous_target == NULL) { - previous_target = root; - out = root; + if (previous_target_tree == NULL) { + previous_target_tree = other_tree; + out = other_tree; } else { - previous_target->ptr.next_tree = root; + previous_target_tree->ptr.next_tree = other_tree; } - root->ptr.next_tree = target; - root = next_tree; + other_tree->ptr.next_tree = target_tree; + other_tree = next_other_tree; } else { - if (previous_target == NULL) { - out = target; + if (previous_target_tree == NULL) { + out = target_tree; } - previous_target = target; - target = target->ptr.next_tree; + previous_target_tree = target_tree; + target_tree = target_tree->ptr.next_tree; } } // Append final part of tree to target - if (target == NULL) { - previous_target->ptr.next_tree = root; + if (target_tree == NULL) { + previous_target_tree->ptr.next_tree = other_tree; } return out; diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c index ad73b8c..1460dcc 100644 --- a/test/heap/test_heap.c +++ b/test/heap/test_heap.c @@ -1,6 +1,5 @@ #include "acutest.h" -#include "vieter_heap.h" -#include "vieter_heap_tree.h" +#include "vieter_heap_internal.h" #include #define TEST_SIZE(heap, size) \ @@ -14,6 +13,29 @@ void test_init() { vieter_heap_free(heap); } +void count_nodes(uint64_t *counter, vieter_heap_node *root) { + (*counter)++; + + if (root->largest_order != NULL) { + count_nodes(counter, root->largest_order); + } + + // This will also traverse the various trees + if (root->ptr.next_largest_order != NULL) { + count_nodes(counter, root->ptr.next_largest_order); + } +} + +uint64_t count_nodes_heap(vieter_heap *heap) { + uint64_t counter = 0; + + if (heap->tree != NULL) { + count_nodes(&counter, heap->tree); + } + + return counter; +} + void test_insert() { vieter_heap *heap = vieter_heap_init(); TEST_SIZE(heap, 0); @@ -23,6 +45,7 @@ void test_insert() { for (uint64_t i = 50; i > 0; i--) { vieter_heap_insert(heap, i, (void *)i); TEST_SIZE(heap, (uint64_t)51 - i); + TEST_CHECK(count_nodes_heap(heap) == (uint64_t)51 - i); data = 0; @@ -47,6 +70,7 @@ void test_insert_random() { for (uint64_t i = 0; i < 5000; i++) { vieter_heap_insert(heap, num, (void *)num); TEST_SIZE(heap, i + 1); + TEST_CHECK(count_nodes_heap(heap) == (uint64_t)i + 1); if (num < smallest) { smallest = num; @@ -72,6 +96,7 @@ void test_pop() { for (uint64_t i = 50; i > 0; i--) { vieter_heap_insert(heap, i, (void *)i); TEST_SIZE(heap, (uint64_t)51 - i); + TEST_CHECK(count_nodes_heap(heap) == (uint64_t)51 - i); TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); TEST_CHECK(data == (void*)i); @@ -112,6 +137,7 @@ void test_pop_random() { num = rand(); vieter_heap_insert(heap, num, (void *)num); TEST_SIZE(heap, i + 1); + TEST_CHECK(count_nodes_heap(heap) == i + 1); numbers[i] = num; } @@ -130,6 +156,7 @@ void test_pop_random() { TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); TEST_CHECK_(data == (void *)numbers[i], "pop %lx == %lx", (uint64_t)data, numbers[i]); TEST_SIZE(heap, n - i - 1); + TEST_CHECK(count_nodes_heap(heap) == n - i - 1); } vieter_heap_free(heap); From 05b96d1fd683fcf6dc98a0ace049020778d01c96 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Thu, 26 Jan 2023 10:21:30 +0100 Subject: [PATCH 19/24] fix(heap): finally fixed that pop bug --- src/heap/vieter_heap_tree.c | 25 +++++++++++++++---------- test/heap/test_heap.c | 14 ++++++++------ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c index 7c7f8eb..bdfe091 100644 --- a/src/heap/vieter_heap_tree.c +++ b/src/heap/vieter_heap_tree.c @@ -61,17 +61,18 @@ vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, return root; } -vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *root_a, - vieter_heap_node *root_b) { - vieter_heap_node *other_tree, *target_tree, *out; +vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *target_tree, + vieter_heap_node *other_tree) { + /* vieter_heap_node *other_tree, *target_tree, *out; */ + vieter_heap_node *out; - if (root_a->order <= root_b->order) { - target_tree = root_a; - other_tree = root_b; - } else { - target_tree = root_b; - other_tree = root_a; - } + /* if (root_a->order <= root_b->order) { */ + /* target_tree = root_a; */ + /* other_tree = root_b; */ + /* } else { */ + /* target_tree = root_b; */ + /* other_tree = root_a; */ + /* } */ vieter_heap_node *next_other_tree, *next_target_tree; vieter_heap_node *previous_target_tree = NULL; @@ -111,6 +112,10 @@ vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *root_a, out = other_tree; } else { previous_target_tree->ptr.next_tree = other_tree; + + // This single missing line right here broke this entire function for + // nearly a week. + previous_target_tree = other_tree; } other_tree->ptr.next_tree = target_tree; diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c index 1460dcc..84f24c1 100644 --- a/test/heap/test_heap.c +++ b/test/heap/test_heap.c @@ -88,15 +88,17 @@ void test_insert_random() { } void test_pop() { + const uint64_t n = 500; + vieter_heap *heap = vieter_heap_init(); TEST_SIZE(heap, 0); void *data; - for (uint64_t i = 50; i > 0; i--) { + for (uint64_t i = n; i > 0; i--) { vieter_heap_insert(heap, i, (void *)i); - TEST_SIZE(heap, (uint64_t)51 - i); - TEST_CHECK(count_nodes_heap(heap) == (uint64_t)51 - i); + TEST_SIZE(heap, (uint64_t)n + 1 - i); + TEST_CHECK(count_nodes_heap(heap) == (uint64_t)n + 1 - i); TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok); TEST_CHECK(data == (void*)i); @@ -104,10 +106,10 @@ void test_pop() { data = NULL; - for (uint64_t i = 1; i <= 50; i++) { + for (uint64_t i = 1; i <= n; i++) { TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); TEST_CHECK(data == (void*)i); - TEST_SIZE(heap, (uint64_t)50 - i); + TEST_SIZE(heap, (uint64_t)n - i); } vieter_heap_free(heap); @@ -124,7 +126,7 @@ int uint64_t_compare(const void *a, const void *b) { } void test_pop_random() { - const uint64_t n = 29; + const uint64_t n = 500; srand(0); From a6bdd397763e1b28038926b631ff68b18891f1b1 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Thu, 26 Jan 2023 11:03:41 +0100 Subject: [PATCH 20/24] docs(heap): add readme --- src/heap/README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/heap/README.md diff --git a/src/heap/README.md b/src/heap/README.md new file mode 100644 index 0000000..8942763 --- /dev/null +++ b/src/heap/README.md @@ -0,0 +1,33 @@ +This min-heap implementation is a pretty standard binomial heap. + +## Representation in memory + +A heap consists of one or more binomial trees, each with a different order `k` +and `2^k` total nodes. This heap can contain `2^64 - 1` elements at most, which +is far more than your memory can contain, but it's still fun to mention. + +A tree does not have its own memory structure; a node that's the root of a +binomial tree is simply called the tree. + +Each node has the following layout: + +```c +typedef struct vieter_heap_node { + uint64_t key; + void *data; + struct vieter_heap_node *largest_order; + union { + struct vieter_heap_node *next_tree; + struct vieter_heap_node *next_largest_order; + } ptr; + uint8_t order; +} vieter_heap_node; +``` + +Each node has a pointer to its child with the largest order (if the node's +order is `0`, this pointer will be NULL). Each non-root node has a pointer to +its sibling with the next-highest order. These pointers allow the children of a +binomial tree to be recombined into a new tree, once their root has been +pop'ed. + +Roots point to the binomial tree in the heap with the next largest order. From ab418e57b3d3ae989e7196056487b7f6634a677b Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Thu, 26 Jan 2023 12:24:34 +0100 Subject: [PATCH 21/24] test: also test with -O3 which can produce extra errors --- .woodpecker/test-mem.yml | 2 ++ .woodpecker/test.yml | 13 +++---------- src/heap/vieter_heap_tree.c | 11 +---------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/.woodpecker/test-mem.yml b/.woodpecker/test-mem.yml index a9ce77b..48896a5 100644 --- a/.woodpecker/test-mem.yml +++ b/.woodpecker/test-mem.yml @@ -14,5 +14,7 @@ pipeline: pull: true commands: - make test-mem + - make clean + - make test-mem CFLAGS='-O3 -Werror -Wall' when: event: [push, pull_request] diff --git a/.woodpecker/test.yml b/.woodpecker/test.yml index 6f636ca..a603923 100644 --- a/.woodpecker/test.yml +++ b/.woodpecker/test.yml @@ -11,19 +11,12 @@ branches: platform: ${PLATFORM} pipeline: - build: + build-and-test: image: *image pull: true - commands: - - make - - make clean - - CFLAGS='-O3' make - when: - event: [push, pull_request] - - test: - image: *image commands: - make test + - make clean + - make test CFLAGS='-O3 -Werror -Wall' when: event: [push, pull_request] diff --git a/src/heap/vieter_heap_tree.c b/src/heap/vieter_heap_tree.c index bdfe091..10c5437 100644 --- a/src/heap/vieter_heap_tree.c +++ b/src/heap/vieter_heap_tree.c @@ -63,16 +63,7 @@ vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a, vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *target_tree, vieter_heap_node *other_tree) { - /* vieter_heap_node *other_tree, *target_tree, *out; */ - vieter_heap_node *out; - - /* if (root_a->order <= root_b->order) { */ - /* target_tree = root_a; */ - /* other_tree = root_b; */ - /* } else { */ - /* target_tree = root_b; */ - /* other_tree = root_a; */ - /* } */ + vieter_heap_node *out = target_tree; vieter_heap_node *next_other_tree, *next_target_tree; vieter_heap_node *previous_target_tree = NULL; From d77b3e4fee699c3c6f6ee8adb7a04536d251354b Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Fri, 27 Jan 2023 20:48:51 +0100 Subject: [PATCH 22/24] chore: some nitpicking --- Makefile | 11 +++++++---- README.md | 4 ++-- include/vieter_cron.h | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index cf67ffa..04c6dd3 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' @@ -40,7 +42,7 @@ all: vieter objs: $(OBJS) .PHONY: vieter -vieter: $(BUILD_DIR)/$(LIB_FILENAME) +vieter: $(LIB) $(BUILD_DIR)/$(LIB_FILENAME): $(OBJS) ar -rcs $@ $(OBJS) @@ -69,10 +71,11 @@ 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) \ + $^ -o $@ -# Allow with the include directory, each test includes $(TEST_DIR) (which +# Along with the include directory, each test includes $(TEST_DIR) (which # contains the acutest.h header file), and the src directory of the module it's # testing. This allows tests to access internal methods, which aren't publicly # exposed. diff --git a/README.md b/README.md index 0c33ab0..d1fef1a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ be implemented in C (or just parts I want to implement in C because it's fun). The goal of this library is to be as self-contained as possible; data structures should be implemented manually if possible. -See the [source code](/src) for the list of modules. +See the [source code](src) for the list of modules. ## Development @@ -43,7 +43,7 @@ only used in a .c file, the import should be placed in the .c file instead. This library uses [Acutest](https://github.com/mity/acutest) for its tests. Tests should be placed in the `test` subdirectory, further divided into -directories that correspond those in `src`. Test files should begin with +directories that correspond to those in `src`. Test files should begin with `test_`, and their format should follow the expected format for Acutest. Each `test_` is compiled separately into a binary, linked with libvieter. A diff --git a/include/vieter_cron.h b/include/vieter_cron.h index 81a2630..459a49f 100644 --- a/include/vieter_cron.h +++ b/include/vieter_cron.h @@ -37,7 +37,7 @@ typedef struct vieter_cron_simple_time { /* * Allocate and initialize a new empty cron expression. */ -vieter_cron_expression *ce_init(); +vieter_cron_expression *vieter_cron_expr_init(); /* * Deallocate a cron expression. From 5b2ce6acaa6cb9cc559eb9618be1eabda2e12356 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Fri, 27 Jan 2023 20:59:06 +0100 Subject: [PATCH 23/24] feat(heap): thread-safety features --- include/vieter_heap.h | 20 +++++++++++++++++++- src/heap/vieter_heap.c | 20 +++++++++++++++++++- src/heap/vieter_heap_internal.h | 2 ++ src/heap/vieter_heap_tree.h | 2 +- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/include/vieter_heap.h b/include/vieter_heap.h index 5d08ebc..ce6f3a4 100644 --- a/include/vieter_heap.h +++ b/include/vieter_heap.h @@ -11,7 +11,7 @@ typedef enum vieter_heap_error { } vieter_heap_error; /* - * Allocate and initalize an empty heap. + * Allocate and initialize an empty heap. */ vieter_heap *vieter_heap_init(); @@ -41,4 +41,22 @@ vieter_heap_error vieter_heap_pop(void **out, vieter_heap *heap); */ vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap); +/* + * Acquire a read lock on the heap. Return value is the result of + * pthread_rwlock_rdlock. + */ +int vieter_heap_rlock(vieter_heap *heap); + +/* + * Acquire a write lock on the heap. Return value is the result of + * pthread_rwlock_wrlock. + */ +int vieter_heap_wlock(vieter_heap *heap); + +/* + * Unlock the lock after having acquired it. Return value is the result of + * pthread_rwlock_unlock. + */ +int vieter_heap_unlock(vieter_heap *heap); + #endif diff --git a/src/heap/vieter_heap.c b/src/heap/vieter_heap.c index 9d4ef8e..b10d94c 100644 --- a/src/heap/vieter_heap.c +++ b/src/heap/vieter_heap.c @@ -2,7 +2,13 @@ #include -vieter_heap *vieter_heap_init() { return calloc(1, sizeof(vieter_heap)); } +vieter_heap *vieter_heap_init() { + vieter_heap *heap = calloc(1, sizeof(vieter_heap)); + + pthread_rwlock_init(&heap->lock, NULL); + + return heap; +} uint64_t vieter_heap_size(vieter_heap *heap) { uint64_t size = 0; @@ -76,3 +82,15 @@ vieter_heap_error vieter_heap_peek(void **out, vieter_heap *heap) { return vieter_heap_ok; } + +int vieter_heap_rlock(vieter_heap *heap) { + return pthread_rwlock_rdlock(&heap->lock); +} + +int vieter_heap_wlock(vieter_heap *heap) { + return pthread_rwlock_wrlock(&heap->lock); +} + +int vieter_heap_unlock(vieter_heap *heap) { + return pthread_rwlock_unlock(&heap->lock); +} diff --git a/src/heap/vieter_heap_internal.h b/src/heap/vieter_heap_internal.h index 8aaed53..41850e9 100644 --- a/src/heap/vieter_heap_internal.h +++ b/src/heap/vieter_heap_internal.h @@ -1,6 +1,8 @@ #include "vieter_heap.h" #include "vieter_heap_tree.h" +#include struct vieter_heap { vieter_heap_node *tree; + pthread_rwlock_t lock; }; diff --git a/src/heap/vieter_heap_tree.h b/src/heap/vieter_heap_tree.h index c3bc55d..0a299db 100644 --- a/src/heap/vieter_heap_tree.h +++ b/src/heap/vieter_heap_tree.h @@ -18,7 +18,7 @@ typedef struct vieter_heap_node { } vieter_heap_node; /* - * Allocate an initialize a heap node object. + * Allocate and initialize a heap node object. */ vieter_heap_node *vieter_heap_node_init(); From 167611e6faf27bd1bc5ef84b6bf44f76ef3f896c Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Fri, 27 Jan 2023 21:59:09 +0100 Subject: [PATCH 24/24] test(heap): test insert after every pop just in case --- test/heap/test_heap.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/heap/test_heap.c b/test/heap/test_heap.c index 84f24c1..f77b0dc 100644 --- a/test/heap/test_heap.c +++ b/test/heap/test_heap.c @@ -159,6 +159,18 @@ void test_pop_random() { TEST_CHECK_(data == (void *)numbers[i], "pop %lx == %lx", (uint64_t)data, numbers[i]); TEST_SIZE(heap, n - i - 1); TEST_CHECK(count_nodes_heap(heap) == n - i - 1); + + // Assure each size is also a valid heap after inserting + vieter_heap_insert(heap, numbers[i], (void *)numbers[i]); + TEST_SIZE(heap, n - i); + TEST_CHECK(count_nodes_heap(heap) == n - i); + + data = NULL; + + TEST_CHECK(vieter_heap_pop(&data, heap) == vieter_heap_ok); + TEST_CHECK_(data == (void *)numbers[i], "pop %lx == %lx", (uint64_t)data, numbers[i]); + TEST_SIZE(heap, n - i - 1); + TEST_CHECK(count_nodes_heap(heap) == n - i - 1); } vieter_heap_free(heap);