Binomial heap #4
			
				
			
		
		
		
	| 
						 | 
					@ -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]
 | 
				
			||||||
| 
						 | 
					@ -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]
 | 
				
			||||||
| 
						 | 
					@ -11,26 +11,12 @@ branches:
 | 
				
			||||||
platform: ${PLATFORM}
 | 
					platform: ${PLATFORM}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pipeline:
 | 
					pipeline:
 | 
				
			||||||
  lint:
 | 
					  build-and-test:
 | 
				
			||||||
    image: *image
 | 
					    image: *image
 | 
				
			||||||
    pull: true
 | 
					    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:
 | 
					    commands:
 | 
				
			||||||
      - make test
 | 
					      - make test
 | 
				
			||||||
 | 
					      - make clean
 | 
				
			||||||
 | 
					      - make test CFLAGS='-O3 -Werror -Wall'
 | 
				
			||||||
    when:
 | 
					    when:
 | 
				
			||||||
      event: [push, pull_request]
 | 
					      event: [push, pull_request]
 | 
				
			||||||
							
								
								
									
										45
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										45
									
								
								Makefile
								
								
								
								
							| 
						 | 
					@ -8,6 +8,8 @@ SRC_DIR ?= src
 | 
				
			||||||
TEST_DIR ?= test
 | 
					TEST_DIR ?= test
 | 
				
			||||||
INC_DIRS ?= include
 | 
					INC_DIRS ?= include
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LIB := $(BUILD_DIR)/$(LIB_FILENAME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SRCS != find '$(SRC_DIR)' -iname '*.c'
 | 
					SRCS != find '$(SRC_DIR)' -iname '*.c'
 | 
				
			||||||
SRCS_H != find $(INC_DIRS) -iname '*.h'
 | 
					SRCS_H != find $(INC_DIRS) -iname '*.h'
 | 
				
			||||||
SRCS_TEST != find '$(TEST_DIR)' -iname '*.c'
 | 
					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)
 | 
					DEPS := $(SRCS:%=$(BUILD_DIR)/%.d) $(SRCS_TEST:%=$(BUILD_DIR)/%.d)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BINS_TEST := $(OBJS_TEST:%.c.o=%)
 | 
					BINS_TEST := $(OBJS_TEST:%.c.o=%)
 | 
				
			||||||
 | 
					TARGETS_TEST := $(BINS_TEST:%=test-%)
 | 
				
			||||||
 | 
					TARGETS_MEM_TEST := $(BINS_TEST:%=test-mem-%)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
 | 
					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.
 | 
					#  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
 | 
					# -MP: generate a dummy target for every header file (according to the  docs it
 | 
				
			||||||
#  prevents some errors when removing header files)
 | 
					#  prevents some errors when removing header files)
 | 
				
			||||||
CFLAGS ?= -MMD -MP -Wall -Werror -Wextra
 | 
					CFLAGS ?= -MMD -MP -g
 | 
				
			||||||
CFLAGS += $(INC_FLAGS)
 | 
					VIETERCFLAGS := $(INC_FLAGS) $(CFLAGS) -Wall -Wextra
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: all
 | 
					.PHONY: all
 | 
				
			||||||
all: vieter
 | 
					all: vieter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# =====COMPILATION=====
 | 
					# =====COMPILATION=====
 | 
				
			||||||
 | 
					# Utility used by the CI to lint
 | 
				
			||||||
 | 
					.PHONY: objs
 | 
				
			||||||
 | 
					objs: $(OBJS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: vieter
 | 
					.PHONY: vieter
 | 
				
			||||||
vieter: $(BUILD_DIR)/$(LIB_FILENAME)
 | 
					vieter: $(LIB)
 | 
				
			||||||
$(BUILD_DIR)/$(LIB_FILENAME): $(OBJS)
 | 
					$(BUILD_DIR)/$(LIB_FILENAME): $(OBJS)
 | 
				
			||||||
	ar -rcs $@ $(OBJS)
 | 
						ar -rcs $@ $(OBJS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c
 | 
					$(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c
 | 
				
			||||||
	mkdir -p $(dir $@)
 | 
						mkdir -p $(dir $@)
 | 
				
			||||||
	$(CC) $(CFLAGS) -c $< -o $@
 | 
						$(CC) $(VIETERCFLAGS) -c $< -o $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# =====TESTING=====
 | 
					# =====TESTING=====
 | 
				
			||||||
.PHONY: test
 | 
					.PHONY: test
 | 
				
			||||||
test: build-test
 | 
					test: $(TARGETS_TEST)
 | 
				
			||||||
	@ $(foreach bin,$(BINS_TEST),./$(bin);)
 | 
					
 | 
				
			||||||
 | 
					.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
 | 
					.PHONY: build-test
 | 
				
			||||||
build-test: $(BINS_TEST)
 | 
					build-test: $(BINS_TEST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# For simplicity, we link every object file to each of the test files. This
 | 
					# 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.
 | 
					# might be changed later if this starts to become too slow.
 | 
				
			||||||
$(BINS_TEST): %: %.c.o $(OBJS)
 | 
					$(BINS_TEST): %: %.c.o $(LIB)
 | 
				
			||||||
	$(CC) $^ -o $@
 | 
						$(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
 | 
					$(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c
 | 
				
			||||||
	mkdir -p $(dir $@)
 | 
						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=====
 | 
					# =====MAINTENANCE=====
 | 
				
			||||||
.PHONY: lint
 | 
					.PHONY: lint
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										41
									
								
								README.md
								
								
								
								
							
							
						
						
									
										41
									
								
								README.md
								
								
								
								
							| 
						 | 
					@ -1,15 +1,12 @@
 | 
				
			||||||
# libvieter
 | 
					# 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).
 | 
					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
 | 
					The goal of this library is to be as self-contained as possible; data
 | 
				
			||||||
required data structures have to be implemented as well. It can only depend on
 | 
					structures should be implemented manually if possible.
 | 
				
			||||||
the C standard libraries.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Currently it contains the following:
 | 
					See the [source code](src) for the list of modules.
 | 
				
			||||||
 | 
					 | 
				
			||||||
* Cron expression parser & next time calculator
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Development
 | 
					## Development
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,13 +15,41 @@ Currently it contains the following:
 | 
				
			||||||
Everything is handled by the provided Makefile. To compile the static library,
 | 
					Everything is handled by the provided Makefile. To compile the static library,
 | 
				
			||||||
simply run `make`.
 | 
					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
 | 
					### Testing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This library uses [Acutest](https://github.com/mity/acutest) for its tests.
 | 
					This library uses [Acutest](https://github.com/mity/acutest) for its tests.
 | 
				
			||||||
Tests should be placed in the `test` subdirectory, further divided into
 | 
					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.
 | 
					`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
 | 
					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`.
 | 
					test binary, you can find them in `build/test`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
#ifndef VIETER_CRON
 | 
					#ifndef VIETER_CRON
 | 
				
			||||||
#define VIETER_CRON
 | 
					#define VIETER_CRON
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdbool.h>
 | 
					 | 
				
			||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
| 
						 | 
					@ -35,18 +34,38 @@ typedef struct vieter_cron_simple_time {
 | 
				
			||||||
  int minute;
 | 
					  int minute;
 | 
				
			||||||
} vieter_cron_simple_time;
 | 
					} 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);
 | 
					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,
 | 
					void vieter_cron_expr_next(vieter_cron_simple_time *out,
 | 
				
			||||||
                           vieter_cron_expression *ce,
 | 
					                           vieter_cron_expression *ce,
 | 
				
			||||||
                           vieter_cron_simple_time *ref);
 | 
					                           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,
 | 
					void vieter_cron_expr_next_from_now(vieter_cron_simple_time *out,
 | 
				
			||||||
                                    vieter_cron_expression *ce);
 | 
					                                    vieter_cron_expression *ce);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum vieter_cron_parse_error vieter_cron_expr_parse(vieter_cron_expression *out,
 | 
					/*
 | 
				
			||||||
 | 
					 * 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);
 | 
					                                               const char *expression);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,8 @@
 | 
				
			||||||
#include "vieter_cron.h"
 | 
					#include "vieter_cron.h"
 | 
				
			||||||
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <time.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This prefix is needed to properly compile
 | 
					// This prefix is needed to properly compile
 | 
				
			||||||
const uint8_t parse_month_days[] = {31, 28, 31, 30, 31, 30,
 | 
					const uint8_t parse_month_days[] = {31, 28, 31, 30, 31, 30,
 | 
				
			||||||
| 
						 | 
					@ -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.
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					@ -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}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -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}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
		Loading…
	
		Reference in New Issue