Merge pull request 'Binomial heap' (#4) from Chewing_Bever/libvieter:min-heap into dev

Reviewed-on: vieter-v/libvieter#4
dev
Jef Roosens 2023-01-27 22:32:02 +01:00
commit 379a05a7b6
16 changed files with 792 additions and 40 deletions

View File

@ -0,0 +1,16 @@
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
- make objs CFLAGS='-Werror -fsyntax-only'
when:
event: [push, pull_request]

View File

@ -0,0 +1,20 @@
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
- make clean
- make test-mem CFLAGS='-O3 -Werror -Wall'
when:
event: [push, pull_request]

View File

@ -11,26 +11,12 @@ branches:
platform: ${PLATFORM}
pipeline:
lint:
build-and-test:
image: *image
pull: true
commands:
- make lint
when:
event: [push, pull_request]
build:
image: *image
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]

View File

@ -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'
@ -17,6 +19,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))
@ -25,42 +29,61 @@ 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 += $(INC_FLAGS)
CFLAGS ?= -MMD -MP -g
VIETERCFLAGS := $(INC_FLAGS) $(CFLAGS) -Wall -Wextra
.PHONY: all
all: vieter
# =====COMPILATION=====
# Utility used by the CI to lint
.PHONY: objs
objs: $(OBJS)
.PHONY: vieter
vieter: $(BUILD_DIR)/$(LIB_FILENAME)
vieter: $(LIB)
$(BUILD_DIR)/$(LIB_FILENAME): $(OBJS)
ar -rcs $@ $(OBJS)
$(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(CC) $(VIETERCFLAGS) -c $< -o $@
# =====TESTING=====
.PHONY: test
test: build-test
@ $(foreach bin,$(BINS_TEST),./$(bin);)
test: $(TARGETS_TEST)
.PHONY: test-mem
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)
# 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 $@
# Each test includes the test directory, which contains the acutest header file
# 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.
$(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -I$(TEST_DIR) -c $< -o $@
$(CC) $(VIETERCFLAGS) -I$(TEST_DIR) \
-I$(dir $(@:$(BUILD_DIR)/$(TEST_DIR)/%=$(SRC_DIR)/%)) \
-c $< -o $@
# =====MAINTENANCE=====
.PHONY: lint

View File

@ -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,13 +15,41 @@ 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.
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.
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
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`.

View File

@ -1,7 +1,6 @@
#ifndef VIETER_CRON
#define VIETER_CRON
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@ -35,18 +34,38 @@ typedef struct vieter_cron_simple_time {
int minute;
} vieter_cron_simple_time;
vieter_cron_expression *ce_init();
/*
* Allocate and initialize a new empty cron expression.
*/
vieter_cron_expression *vieter_cron_expr_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

View File

@ -0,0 +1,62 @@
#ifndef VIETER_HEAP
#define VIETER_HEAP
#include <stdint.h>
typedef struct vieter_heap vieter_heap;
typedef enum vieter_heap_error {
vieter_heap_ok = 0,
vieter_heap_empty = 1
} vieter_heap_error;
/*
* Allocate and initialize 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);
/*
* 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

View File

@ -1,4 +1,8 @@
#include "vieter_cron.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
// This prefix is needed to properly compile
const uint8_t parse_month_days[] = {31, 28, 31, 30, 31, 30,

33
src/heap/README.md 100644
View File

@ -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.

View File

@ -0,0 +1,96 @@
#include "vieter_heap_internal.h"
#include <stdlib.h>
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;
vieter_heap_node *tree = heap->tree;
while (tree != NULL) {
size |= (uint64_t)1 << tree->order;
tree = tree->ptr.next_tree;
}
return size;
}
void vieter_heap_free(vieter_heap *heap) {
vieter_heap_node *tree = heap->tree;
vieter_heap_node *next;
while (tree != NULL) {
next = tree->ptr.next_tree;
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_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;
} 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) {
if (heap->tree == NULL) {
return vieter_heap_empty;
}
heap->tree = vieter_heap_tree_pop(out, heap->tree);
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_node *tree = heap->tree;
uint64_t smallest_key = tree->key;
*out = tree->data;
while (tree->ptr.next_tree != NULL) {
tree = tree->ptr.next_tree;
if (tree->key < smallest_key) {
smallest_key = tree->key;
*out = tree->data;
}
}
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);
}

View File

@ -0,0 +1,8 @@
#include "vieter_heap.h"
#include "vieter_heap_tree.h"
#include <pthread.h>
struct vieter_heap {
vieter_heap_node *tree;
pthread_rwlock_t lock;
};

View File

@ -0,0 +1,192 @@
#include "vieter_heap_tree.h"
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_node *root) {
if (root->order == 0) {
goto end;
}
uint64_t size = 1;
vieter_heap_node **stack =
malloc(((uint64_t)1 << root->order) * sizeof(vieter_heap_node *));
stack[0] = root->largest_order;
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->ptr.next_largest_order != NULL) {
stack[size] = node->ptr.next_largest_order;
size++;
}
vieter_heap_node_free(node);
}
free(stack);
end:
vieter_heap_node_free(root);
}
vieter_heap_node *vieter_heap_tree_merge_same_order(vieter_heap_node *root_a,
vieter_heap_node *root_b) {
vieter_heap_node *root, *child;
if (root_a->key <= root_b->key) {
root = root_a;
child = root_b;
} else {
root = root_b;
child = root_a;
}
child->ptr.next_largest_order = root->largest_order;
root->largest_order = child;
root->order++;
return root;
}
vieter_heap_node *vieter_heap_tree_merge(vieter_heap_node *target_tree,
vieter_heap_node *other_tree) {
vieter_heap_node *out = target_tree;
vieter_heap_node *next_other_tree, *next_target_tree;
vieter_heap_node *previous_target_tree = NULL;
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_tree = vieter_heap_tree_merge_same_order(target_tree, other_tree);
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 (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_tree != NULL) {
previous_target_tree->ptr.next_tree = target_tree;
} else {
out = target_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_tree == NULL) {
previous_target_tree = other_tree;
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;
other_tree = next_other_tree;
} else {
if (previous_target_tree == NULL) {
out = target_tree;
}
previous_target_tree = target_tree;
target_tree = target_tree->ptr.next_tree;
}
}
// Append final part of tree to target
if (target_tree == NULL) {
previous_target_tree->ptr.next_tree = other_tree;
}
return out;
}
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->key;
while (tree->ptr.next_tree != NULL) {
previous_tree = tree;
tree = tree->ptr.next_tree;
if (tree->key < smallest_key) {
smallest_key = tree->key;
tree_before_smallest = previous_tree;
}
}
vieter_heap_node *tree_to_pop;
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;
}
*out = tree_to_pop->data;
if (tree_to_pop->order == 0) {
vieter_heap_tree_free(tree_to_pop);
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);
tree = previous_tree->ptr.next_largest_order;
previous_tree->ptr.next_tree = NULL;
vieter_heap_node *next_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;
}
}

View File

@ -0,0 +1,53 @@
#ifndef VIETER_HEAP_TREE
#define VIETER_HEAP_TREE
#include <stdint.h>
#include <stdlib.h>
typedef struct vieter_heap_node {
uint64_t key;
void *data;
struct vieter_heap_node *largest_order;
union {
// 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 and 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);
/*
* 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

View File

@ -0,0 +1,187 @@
#include "acutest.h"
#include "vieter_heap_internal.h"
#include <stdlib.h>
#define TEST_SIZE(heap, size) \
TEST_CHECK(vieter_heap_size(heap) == size); \
TEST_MSG("Size: %zu, expected: %lu", vieter_heap_size(heap), (uint64_t)size)
void test_init() {
vieter_heap *heap = vieter_heap_init();
TEST_CHECK(heap != NULL);
TEST_SIZE(heap, 0);
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);
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(count_nodes_heap(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_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);
TEST_CHECK(count_nodes_heap(heap) == (uint64_t)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() {
const uint64_t n = 500;
vieter_heap *heap = vieter_heap_init();
TEST_SIZE(heap, 0);
void *data;
for (uint64_t i = n; i > 0; i--) {
vieter_heap_insert(heap, i, (void *)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);
}
data = NULL;
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)n - i);
}
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() {
const uint64_t n = 500;
srand(0);
vieter_heap *heap = vieter_heap_init();
uint64_t *numbers = malloc(n * sizeof(uint64_t));
uint64_t num;
for (uint64_t i = 0; i < n; i++) {
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;
}
qsort(numbers, n, sizeof(uint64_t), uint64_t_compare);
void *data = NULL;
for (uint64_t i = 0; i < n; i++) {
TEST_CHECK(vieter_heap_peek(&data, heap) == vieter_heap_ok);
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], "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);
free(numbers);
}
TEST_LIST = {
{"init", test_init},
{"insert", test_insert},
{"insert random", test_insert_random},
{"pop", test_pop},
{"pop random", test_pop_random},
{NULL, NULL}
};

View File

@ -0,0 +1,28 @@
#include "acutest.h"
#include "vieter_heap.h"
#include "vieter_heap_tree.h"
#include <stdlib.h>
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}
};