Impl. priority heap, minor tweaks/fixes
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 3s
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 3s
Implemented a priority heap. Added tests for the priority heap. Add a "default" lcg generator function. Tweak hash_map to use the default lcg generator function. `clang-format`.
This commit is contained in:
parent
ea919f3eb3
commit
fbe62ba5b2
9 changed files with 342 additions and 8 deletions
|
@ -8,6 +8,7 @@ set(SimpleArchiver_SOURCES
|
||||||
src/parser.c
|
src/parser.c
|
||||||
src/data_structures/linked_list.c
|
src/data_structures/linked_list.c
|
||||||
src/data_structures/hash_map.c
|
src/data_structures/hash_map.c
|
||||||
|
src/data_structures/priority_heap.c
|
||||||
src/algorithms/linear_congruential_gen.c
|
src/algorithms/linear_congruential_gen.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,5 +30,6 @@ add_executable(test_datastructures
|
||||||
src/data_structures/test.c
|
src/data_structures/test.c
|
||||||
src/data_structures/linked_list.c
|
src/data_structures/linked_list.c
|
||||||
src/data_structures/hash_map.c
|
src/data_structures/hash_map.c
|
||||||
|
src/data_structures/priority_heap.c
|
||||||
src/algorithms/linear_congruential_gen.c
|
src/algorithms/linear_congruential_gen.c
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,13 +8,15 @@ SOURCES = \
|
||||||
../src/parser.c \
|
../src/parser.c \
|
||||||
../src/algorithms/linear_congruential_gen.c \
|
../src/algorithms/linear_congruential_gen.c \
|
||||||
../src/data_structures/linked_list.c \
|
../src/data_structures/linked_list.c \
|
||||||
../src/data_structures/hash_map.c
|
../src/data_structures/hash_map.c \
|
||||||
|
../src/data_structures/priority_heap.c
|
||||||
|
|
||||||
HEADERS = \
|
HEADERS = \
|
||||||
../src/parser.h \
|
../src/parser.h \
|
||||||
../src/algorithms/linear_congruential_gen.h \
|
../src/algorithms/linear_congruential_gen.h \
|
||||||
../src/data_structures/linked_list.h \
|
../src/data_structures/linked_list.h \
|
||||||
../src/data_structures/hash_map.h \
|
../src/data_structures/hash_map.h \
|
||||||
|
../src/data_structures/priority_heap.h \
|
||||||
../src/platforms.h
|
../src/platforms.h
|
||||||
|
|
||||||
OBJECTS = $(addprefix ${OBJDIR}/,$(subst ..,PREVDIR,$(patsubst %.c,%.c.o,${SOURCES})))
|
OBJECTS = $(addprefix ${OBJDIR}/,$(subst ..,PREVDIR,$(patsubst %.c,%.c.o,${SOURCES})))
|
||||||
|
|
|
@ -25,3 +25,8 @@ unsigned long long simple_archiver_algo_lcg(unsigned long long seed,
|
||||||
// "m" is implicity 2^64.
|
// "m" is implicity 2^64.
|
||||||
return seed * a + c;
|
return seed * a + c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long long simple_archiver_algo_lcg_defaults(unsigned long long seed) {
|
||||||
|
// "m" is implicity 2^64.
|
||||||
|
return seed * SC_ALGO_LCG_DEFAULT_A + SC_ALGO_LCG_DEFAULT_C;
|
||||||
|
}
|
||||||
|
|
|
@ -27,4 +27,6 @@ unsigned long long simple_archiver_algo_lcg(unsigned long long seed,
|
||||||
unsigned long long a,
|
unsigned long long a,
|
||||||
unsigned long long c);
|
unsigned long long c);
|
||||||
|
|
||||||
|
unsigned long long simple_archiver_algo_lcg_defaults(unsigned long long seed);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -86,8 +86,7 @@ unsigned long long simple_archiver_hash_map_internal_key_to_hash(
|
||||||
seed += temp;
|
seed += temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
return simple_archiver_algo_lcg(seed, SC_ALGO_LCG_DEFAULT_A,
|
return simple_archiver_algo_lcg_defaults(seed);
|
||||||
SC_ALGO_LCG_DEFAULT_C);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns 0 on success.
|
/// Returns 0 on success.
|
||||||
|
|
159
src/data_structures/priority_heap.c
Normal file
159
src/data_structures/priority_heap.c
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Stephen Seo
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* `priority_heap.c` is the source for a priority heap implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "priority_heap.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void simple_archiver_priority_heap_internal_realloc(
|
||||||
|
SDArchiverPHeap **priority_heap) {
|
||||||
|
SDArchiverPHeap *new_priority_heap = malloc(sizeof(SDArchiverPHeap));
|
||||||
|
|
||||||
|
new_priority_heap->capacity = (*priority_heap)->capacity * 2;
|
||||||
|
new_priority_heap->size = 0;
|
||||||
|
|
||||||
|
new_priority_heap->nodes =
|
||||||
|
calloc(new_priority_heap->capacity, sizeof(SDArchiverPHNode));
|
||||||
|
|
||||||
|
for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
|
||||||
|
if ((*priority_heap)->nodes[idx].is_valid != 0) {
|
||||||
|
simple_archiver_priority_heap_insert(
|
||||||
|
&new_priority_heap, (*priority_heap)->nodes[idx].priority,
|
||||||
|
(*priority_heap)->nodes[idx].data,
|
||||||
|
(*priority_heap)->nodes[idx].data_cleanup_fn);
|
||||||
|
(*priority_heap)->nodes[idx].is_valid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simple_archiver_priority_heap_free(priority_heap);
|
||||||
|
|
||||||
|
*priority_heap = new_priority_heap;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDArchiverPHeap *simple_archiver_priority_heap_init(void) {
|
||||||
|
SDArchiverPHeap *priority_heap = malloc(sizeof(SDArchiverPHeap));
|
||||||
|
|
||||||
|
priority_heap->capacity = SC_SA_DS_PRIORITY_HEAP_START_SIZE;
|
||||||
|
priority_heap->size = 0;
|
||||||
|
|
||||||
|
priority_heap->nodes =
|
||||||
|
calloc(priority_heap->capacity, sizeof(SDArchiverPHNode));
|
||||||
|
|
||||||
|
return priority_heap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap) {
|
||||||
|
if (priority_heap && *priority_heap) {
|
||||||
|
for (unsigned int idx = 1; idx < (*priority_heap)->size + 1; ++idx) {
|
||||||
|
if ((*priority_heap)->nodes[idx].is_valid != 0) {
|
||||||
|
if ((*priority_heap)->nodes[idx].data_cleanup_fn) {
|
||||||
|
(*priority_heap)
|
||||||
|
->nodes[idx]
|
||||||
|
.data_cleanup_fn((*priority_heap)->nodes[idx].data);
|
||||||
|
} else {
|
||||||
|
free((*priority_heap)->nodes[idx].data);
|
||||||
|
}
|
||||||
|
(*priority_heap)->nodes[idx].is_valid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free((*priority_heap)->nodes);
|
||||||
|
free(*priority_heap);
|
||||||
|
*priority_heap = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void simple_archiver_priority_heap_insert(SDArchiverPHeap **priority_heap,
|
||||||
|
long long priority, void *data,
|
||||||
|
void (*data_cleanup_fn)(void *)) {
|
||||||
|
if (!priority_heap || !*priority_heap) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*priority_heap)->size + 1 >= (*priority_heap)->capacity) {
|
||||||
|
simple_archiver_priority_heap_internal_realloc(priority_heap);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int hole = (*priority_heap)->size + 1;
|
||||||
|
|
||||||
|
while (hole > 1 && priority < (*priority_heap)->nodes[hole / 2].priority) {
|
||||||
|
(*priority_heap)->nodes[hole] = (*priority_heap)->nodes[hole / 2];
|
||||||
|
hole /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*priority_heap)->nodes[hole].priority = priority;
|
||||||
|
(*priority_heap)->nodes[hole].data = data;
|
||||||
|
(*priority_heap)->nodes[hole].data_cleanup_fn = data_cleanup_fn;
|
||||||
|
(*priority_heap)->nodes[hole].is_valid = 1;
|
||||||
|
|
||||||
|
++(*priority_heap)->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *simple_archiver_priority_heap_top(SDArchiverPHeap *priority_heap) {
|
||||||
|
if (priority_heap && priority_heap->size != 0) {
|
||||||
|
return priority_heap->nodes[1].data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *simple_archiver_priority_heap_pop(SDArchiverPHeap *priority_heap) {
|
||||||
|
if (!priority_heap || priority_heap->size == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *data = priority_heap->nodes[1].data;
|
||||||
|
priority_heap->nodes[1].is_valid = 0;
|
||||||
|
|
||||||
|
SDArchiverPHNode end = priority_heap->nodes[priority_heap->size];
|
||||||
|
priority_heap->nodes[priority_heap->size].is_valid = 0;
|
||||||
|
|
||||||
|
unsigned int hole = 1;
|
||||||
|
while (hole * 2 + 1 <= priority_heap->size) {
|
||||||
|
if (priority_heap->nodes[hole * 2].is_valid != 0 &&
|
||||||
|
priority_heap->nodes[hole * 2 + 1].is_valid != 0) {
|
||||||
|
if (end.priority < priority_heap->nodes[hole * 2].priority &&
|
||||||
|
end.priority < priority_heap->nodes[hole * 2 + 1].priority) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (priority_heap->nodes[hole * 2].priority <
|
||||||
|
priority_heap->nodes[hole * 2 + 1].priority) {
|
||||||
|
priority_heap->nodes[hole] = priority_heap->nodes[hole * 2];
|
||||||
|
hole = hole * 2;
|
||||||
|
} else {
|
||||||
|
priority_heap->nodes[hole] = priority_heap->nodes[hole * 2 + 1];
|
||||||
|
hole = hole * 2 + 1;
|
||||||
|
}
|
||||||
|
} else if (priority_heap->nodes[hole * 2].is_valid != 0) {
|
||||||
|
if (end.priority < priority_heap->nodes[hole * 2].priority) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
priority_heap->nodes[hole] = priority_heap->nodes[hole * 2];
|
||||||
|
hole = hole * 2;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
priority_heap->nodes[hole] = end;
|
||||||
|
|
||||||
|
--priority_heap->size;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
55
src/data_structures/priority_heap.h
Normal file
55
src/data_structures/priority_heap.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Stephen Seo
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* `priority_heap.h` is the header for a priority heap implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_
|
||||||
|
#define SEODISPARATE_COM_SIMPLE_ARCHIVER_DATA_STRUCTURE_PRIORITY_HEAP_H_
|
||||||
|
|
||||||
|
#define SC_SA_DS_PRIORITY_HEAP_START_SIZE 32
|
||||||
|
|
||||||
|
typedef struct SDArchiverPHNode {
|
||||||
|
long long priority;
|
||||||
|
void *data;
|
||||||
|
void (*data_cleanup_fn)(void *);
|
||||||
|
/// Is non-zero if valid.
|
||||||
|
int is_valid;
|
||||||
|
} SDArchiverPHNode;
|
||||||
|
|
||||||
|
typedef struct SDArchiverPHeap {
|
||||||
|
SDArchiverPHNode *nodes;
|
||||||
|
unsigned long long capacity;
|
||||||
|
unsigned long long size;
|
||||||
|
} SDArchiverPHeap;
|
||||||
|
|
||||||
|
SDArchiverPHeap *simple_archiver_priority_heap_init(void);
|
||||||
|
void simple_archiver_priority_heap_free(SDArchiverPHeap **priority_heap);
|
||||||
|
|
||||||
|
/// If data_cleanup_fn is NULL, then "free()" is used on data when popped or
|
||||||
|
/// freed.
|
||||||
|
void simple_archiver_priority_heap_insert(SDArchiverPHeap **priority_heap,
|
||||||
|
long long priority, void *data,
|
||||||
|
void (*data_cleanup_fn)(void *));
|
||||||
|
|
||||||
|
/// Returns NULL if empty or if priority_heap is NULL.
|
||||||
|
void *simple_archiver_priority_heap_top(SDArchiverPHeap *priority_heap);
|
||||||
|
|
||||||
|
/// Returns NULL if empty or if priority_heap is NULL.
|
||||||
|
/// When data is popped, the data_cleanup_fn is ignored and the user must take
|
||||||
|
/// ownership of the returned data pointer.
|
||||||
|
void *simple_archiver_priority_heap_pop(SDArchiverPHeap *priority_heap);
|
||||||
|
|
||||||
|
#endif
|
|
@ -20,8 +20,10 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../algorithms/linear_congruential_gen.h"
|
||||||
#include "hash_map.h"
|
#include "hash_map.h"
|
||||||
#include "linked_list.h"
|
#include "linked_list.h"
|
||||||
|
#include "priority_heap.h"
|
||||||
|
|
||||||
static int checks_checked = 0;
|
static int checks_checked = 0;
|
||||||
static int checks_passed = 0;
|
static int checks_passed = 0;
|
||||||
|
@ -151,6 +153,114 @@ int main(void) {
|
||||||
simple_archiver_hash_map_free(&hash_map);
|
simple_archiver_hash_map_free(&hash_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test PriorityHeap.
|
||||||
|
{
|
||||||
|
SDArchiverPHeap *priority_heap = simple_archiver_priority_heap_init();
|
||||||
|
simple_archiver_priority_heap_free(&priority_heap);
|
||||||
|
|
||||||
|
priority_heap = simple_archiver_priority_heap_init();
|
||||||
|
|
||||||
|
// Just 3 elements.
|
||||||
|
for (unsigned int idx = 0; idx < 3; ++idx) {
|
||||||
|
unsigned int *data = malloc(sizeof(unsigned int));
|
||||||
|
*data = idx;
|
||||||
|
simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
|
||||||
|
}
|
||||||
|
for (unsigned int idx = 0; idx < 3; ++idx) {
|
||||||
|
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
|
||||||
|
CHECK_TRUE(*data == idx);
|
||||||
|
if (*data != idx) {
|
||||||
|
printf("idx is %u, data is %u\n", idx, *data);
|
||||||
|
}
|
||||||
|
data = simple_archiver_priority_heap_pop(priority_heap);
|
||||||
|
CHECK_TRUE(*data == idx);
|
||||||
|
if (*data != idx) {
|
||||||
|
printf("idx is %u, data is %u\n", idx, *data);
|
||||||
|
}
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 100 elements.
|
||||||
|
unsigned int max = 100;
|
||||||
|
|
||||||
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
|
unsigned int *data = malloc(sizeof(unsigned int));
|
||||||
|
*data = idx;
|
||||||
|
simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
|
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
|
||||||
|
CHECK_TRUE(*data == idx);
|
||||||
|
data = simple_archiver_priority_heap_pop(priority_heap);
|
||||||
|
CHECK_TRUE(*data == idx);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert in reverse order.
|
||||||
|
for (unsigned int idx = max; idx-- > 0;) {
|
||||||
|
unsigned int *data = malloc(sizeof(unsigned int));
|
||||||
|
*data = idx;
|
||||||
|
simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
|
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
|
||||||
|
CHECK_TRUE(*data == idx);
|
||||||
|
data = simple_archiver_priority_heap_pop(priority_heap);
|
||||||
|
CHECK_TRUE(*data == idx);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert in random order.
|
||||||
|
unsigned int *array = malloc(sizeof(unsigned int) * max);
|
||||||
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
|
array[idx] = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic randomization.
|
||||||
|
for (unsigned int idx = max - 1; idx-- > 0;) {
|
||||||
|
unsigned int other_idx = simple_archiver_algo_lcg_defaults(idx) %
|
||||||
|
(unsigned long long)(idx + 1);
|
||||||
|
if (idx != other_idx) {
|
||||||
|
unsigned int temp = array[max - 1];
|
||||||
|
array[max - 1] = array[other_idx];
|
||||||
|
array[other_idx] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the deterministically randomized array.
|
||||||
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
|
simple_archiver_priority_heap_insert(&priority_heap, array[idx],
|
||||||
|
array + idx, no_free_fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
|
unsigned int *data = simple_archiver_priority_heap_top(priority_heap);
|
||||||
|
CHECK_TRUE(*data == idx);
|
||||||
|
if (*data != idx) {
|
||||||
|
printf("idx is %u, data is %u\n", idx, *data);
|
||||||
|
}
|
||||||
|
data = simple_archiver_priority_heap_pop(priority_heap);
|
||||||
|
CHECK_TRUE(*data == idx);
|
||||||
|
if (*data != idx) {
|
||||||
|
printf("idx is %u, data is %u\n", idx, *data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(array);
|
||||||
|
|
||||||
|
simple_archiver_priority_heap_free(&priority_heap);
|
||||||
|
|
||||||
|
// Insert, don't pop, do free, for memcheck.
|
||||||
|
priority_heap = simple_archiver_priority_heap_init();
|
||||||
|
for (unsigned int idx = 0; idx < max; ++idx) {
|
||||||
|
unsigned int *data = malloc(sizeof(unsigned int));
|
||||||
|
*data = idx;
|
||||||
|
simple_archiver_priority_heap_insert(&priority_heap, idx, data, NULL);
|
||||||
|
}
|
||||||
|
simple_archiver_priority_heap_free(&priority_heap);
|
||||||
|
}
|
||||||
|
|
||||||
printf("Checks checked: %u\n", checks_checked);
|
printf("Checks checked: %u\n", checks_checked);
|
||||||
printf("Checks passed: %u\n", checks_passed);
|
printf("Checks passed: %u\n", checks_passed);
|
||||||
return checks_passed == checks_checked ? 0 : 1;
|
return checks_passed == checks_checked ? 0 : 1;
|
||||||
|
|
Loading…
Reference in a new issue