You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

685 lines
16 KiB

/* Tests for C99 gheap, galgorithm and gpriority_queue */
#include "galgorithm.h"
#include "gheap.h"
#include "gpriority_queue.h"
#include <assert.h>
#include <stdint.h> /* for uintptr_t, SIZE_MAX */
#include <stdio.h> /* for printf() */
#include <stdlib.h> /* for srand(), rand(), malloc(), free() */
static int less_comparer(const void *const ctx, const void *const a,
const void *const b)
{
uintptr_t must_invert = (uintptr_t)ctx;
return (must_invert ? (*(int *)b < *(int *)a): (*(int *)a < *(int *)b));
}
static void item_mover(void *const dst, const void *const src)
{
*((int *)dst) = *((int *)src);
}
static void test_parent_child(const struct gheap_ctx *const ctx,
const size_t start_index, const size_t n)
{
assert(start_index > 0);
assert(start_index <= SIZE_MAX - n);
printf(" test_parent_child(start_index=%zu, n=%zu) ", start_index, n);
const size_t fanout = ctx->fanout;
for (size_t i = 0; i < n; ++i) {
const size_t u = start_index + i;
size_t v = gheap_get_child_index(ctx, u);
if (v < SIZE_MAX) {
assert(v > u);
v = gheap_get_parent_index(ctx, v);
assert(v == u);
}
v = gheap_get_parent_index(ctx, u);
assert(v < u);
v = gheap_get_child_index(ctx, v);
assert(v <= u && u - v < fanout);
}
printf("OK\n");
}
static void test_is_heap(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
assert(n > 0);
printf(" test_is_heap(n=%zu) ", n);
/* Verify that ascending sorted array creates one-item heap. */
for (size_t i = 0; i < n; ++i) {
a[i] = i;
}
assert(gheap_is_heap_until(ctx, a, n) == 1);
assert(gheap_is_heap(ctx, a, 1));
if (n > 1) {
assert(!gheap_is_heap(ctx, a, n));
}
/* Verify that descending sorted array creates valid heap. */
for (size_t i = 0; i < n; ++i) {
a[i] = n - i;
}
assert(gheap_is_heap_until(ctx, a, n) == n);
assert(gheap_is_heap(ctx, a, n));
/* Verify that array containing identical items creates valid heap. */
for (size_t i = 0; i < n; ++i) {
a[i] = n;
}
assert(gheap_is_heap_until(ctx, a, n) == n);
assert(gheap_is_heap(ctx, a, n));
printf("OK\n");
}
static void init_array(int *const a, const size_t n)
{
for (size_t i = 0; i < n; ++i) {
a[i] = rand();
}
}
static void assert_sorted(const struct gheap_ctx *const ctx,
const int *const base, const size_t n)
{
const gheap_less_comparer_t less_comparer = ctx->less_comparer;
const void *const less_comparer_ctx = ctx->less_comparer_ctx;
for (size_t i = 1; i < n; ++i) {
assert(!less_comparer(less_comparer_ctx, &base[i], &base[i - 1]));
}
}
static void test_make_heap(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_make_heap(n=%zu) ", n);
init_array(a, n);
gheap_make_heap(ctx, a, n);
assert(gheap_is_heap(ctx, a, n));
printf("OK\n");
}
static void test_sort_heap(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_sort_heap(n=%zu) ", n);
/* Verify ascending sorting. */
init_array(a, n);
gheap_make_heap(ctx, a, n);
gheap_sort_heap(ctx, a, n);
assert_sorted(ctx, a, n);
/* Verify descending sorting. */
const struct gheap_ctx ctx_desc = {
.fanout = ctx->fanout,
.page_chunks = ctx->page_chunks,
.item_size = ctx->item_size,
.less_comparer = &less_comparer,
.less_comparer_ctx = (void *)1,
.item_mover = ctx->item_mover,
};
init_array(a, n);
gheap_make_heap(&ctx_desc, a, n);
gheap_sort_heap(&ctx_desc, a, n);
assert_sorted(&ctx_desc, a, n);
printf("OK\n");
}
static void test_push_heap(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_push_heap(n=%zu) ", n);
init_array(a, n);
for (size_t i = 0; i < n; ++i) {
gheap_push_heap(ctx, a, i + 1);
}
assert(gheap_is_heap(ctx, a, n));
printf("OK\n");
}
static void test_pop_heap(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_pop_heap(n=%zu) ", n);
init_array(a, n);
gheap_make_heap(ctx, a, n);
for (size_t i = 0; i < n; ++i) {
const int item = a[0];
gheap_pop_heap(ctx, a, n - i);
assert(item == a[n - i - 1]);
}
assert_sorted(ctx, a, n);
printf("OK\n");
}
static void test_swap_max_item(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_swap_max_item(n=%zu) ", n);
init_array(a, n);
const size_t m = n / 2;
if (m > 0) {
gheap_make_heap(ctx, a, m);
for (size_t i = m; i < n; ++i) {
const int max_item = a[0];
gheap_swap_max_item(ctx, a, m, &a[i]);
assert(max_item == a[i]);
assert(gheap_is_heap(ctx, a, m));
}
}
printf("OK\n");
}
static void test_restore_heap_after_item_increase(
const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_restore_heap_after_item_increase(n=%zu) ", n);
init_array(a, n);
gheap_make_heap(ctx, a, n);
for (size_t i = 0; i < n; ++i) {
const size_t item_index = rand() % n;
const int old_item = a[item_index];
/* Don't allow integer overflow. */
size_t fade = SIZE_MAX;
do {
/* Division by zero is impossible here. */
a[item_index] = old_item + rand() % fade;
fade /= 2;
} while (a[item_index] < old_item);
gheap_restore_heap_after_item_increase(ctx, a, n, item_index);
assert(gheap_is_heap(ctx, a, n));
}
printf("OK\n");
}
static void test_restore_heap_after_item_decrease(
const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_restore_heap_after_item_decrease(n=%zu) ", n);
init_array(a, n);
gheap_make_heap(ctx, a, n);
for (size_t i = 0; i < n; ++i) {
const size_t item_index = rand() % n;
const int old_item = a[item_index];
/* Don't allow integer underflow. */
size_t fade = SIZE_MAX;
do {
/* Division by zero is impossible here. */
a[item_index] = old_item - rand() % fade;
fade /= 2;
} while (a[item_index] > old_item);
gheap_restore_heap_after_item_decrease(ctx, a, n, item_index);
assert(gheap_is_heap(ctx, a, n));
}
printf("OK\n");
}
static void test_remove_from_heap(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_remove_from_heap(n=%zu) ", n);
init_array(a, n);
gheap_make_heap(ctx, a, n);
for (size_t i = 0; i < n; ++i) {
const size_t item_index = rand() % (n - i);
const int item = a[item_index];
gheap_remove_from_heap(ctx, a, n - i, item_index);
assert(gheap_is_heap(ctx, a, n - i - 1));
assert(item == a[n - i - 1]);
}
printf("OK\n");
}
static const int *min_element(const int *const a, const size_t n)
{
assert(n > 0);
const int *min_ptr = a;
for (size_t i = 1; i < n; ++i) {
if (a[i] < *min_ptr) {
min_ptr = a + i;
}
}
return min_ptr;
}
static void test_heapsort(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_heapsort(n=%zu) ", n);
/* Verify ascending sorting. */
init_array(a, n);
galgorithm_heapsort(ctx, a, n);
assert_sorted(ctx, a, n);
/* Verify descending sorting. */
const struct gheap_ctx ctx_desc = {
.fanout = ctx->fanout,
.page_chunks = ctx->page_chunks,
.item_size = ctx->item_size,
.less_comparer = &less_comparer,
.less_comparer_ctx = (void *)1,
.item_mover = ctx->item_mover,
};
init_array(a, n);
galgorithm_heapsort(&ctx_desc, a, n);
assert_sorted(&ctx_desc, a, n);
printf("OK\n");
}
static void test_partial_sort(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_partial_sort(n=%zu) ", n);
// Check 0-items partial sort.
init_array(a, n);
galgorithm_partial_sort(ctx, a, n, 0);
// Check 1-item partial sort.
if (n > 0) {
init_array(a, n);
galgorithm_partial_sort(ctx, a, n, 1);
assert(min_element(a, n) == a);
}
// Check 2-items partial sort.
if (n > 1) {
init_array(a, n);
galgorithm_partial_sort(ctx, a, n, 2);
assert_sorted(ctx, a, 2);
assert(min_element(a + 1, n - 1) == a + 1);
}
// Check n-items partial sort.
init_array(a, n);
galgorithm_partial_sort(ctx, a, n, n);
assert_sorted(ctx, a, n);
// Check (n-1)-items partial sort.
if (n > 0) {
init_array(a, n);
galgorithm_partial_sort(ctx, a, n, n - 1);
assert_sorted(ctx, a, n);
}
// Check (n-2)-items partial sort.
if (n > 2) {
init_array(a, n);
galgorithm_partial_sort(ctx, a, n, n - 2);
assert_sorted(ctx, a, n - 2);
assert(min_element(a + n - 3, 3) == a + n - 3);
}
printf("OK\n");
}
struct nway_merge_input_ctx
{
int *next;
int *end;
};
static int nway_merge_input_next(void *const ctx)
{
struct nway_merge_input_ctx *const c = ctx;
assert(c->next <= c->end);
if (c->next < c->end) {
++(c->next);
}
return (c->next < c->end);
}
static const void *nway_merge_input_get(const void *const ctx)
{
const struct nway_merge_input_ctx *const c = ctx;
assert(c->next < c->end);
return c->next;
}
static void nway_ctx_mover(void *const dst, const void *const src)
{
*(struct nway_merge_input_ctx *)dst = *(struct nway_merge_input_ctx *)src;
}
static const struct galgorithm_nway_merge_input_vtable
nway_merge_input_vtable = {
.next = &nway_merge_input_next,
.get = &nway_merge_input_get,
};
struct nway_merge_output_ctx
{
int *next;
};
static void nway_merge_output_put(void *const ctx, const void *const data)
{
struct nway_merge_output_ctx *const c = ctx;
item_mover(c->next, data);
++(c->next);
}
static const struct galgorithm_nway_merge_output_vtable
nway_merge_output_vtable = {
.put = &nway_merge_output_put,
};
static void small_range_sorter(const void *const ctx,
void *const a, const size_t n)
{
galgorithm_heapsort(ctx, a, n);
}
static void test_nway_mergesort(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_nway_mergesort(n=%zu) ", n);
int *const items_tmp_buf = malloc(sizeof(a[0]) * n);
// Verify 2-way mergesort with small_range_size = 1.
init_array(a, n);
galgorithm_nway_mergesort(ctx, a, n, &small_range_sorter, ctx, 1, 2,
items_tmp_buf);
assert_sorted(ctx, a, n);
// Verify 3-way mergesort with small_range_size = 2.
init_array(a, n);
galgorithm_nway_mergesort(ctx, a, n, &small_range_sorter, ctx, 2, 3,
items_tmp_buf);
assert_sorted(ctx, a, n);
// Verify 10-way mergesort with small_range_size = 9.
init_array(a, n);
galgorithm_nway_mergesort(ctx, a, n, &small_range_sorter, ctx, 10, 9,
items_tmp_buf);
assert_sorted(ctx, a, n);
free(items_tmp_buf);
printf("OK\n");
}
static void test_nway_merge(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_nway_merge(n=%zu) ", n);
int *const b = malloc(sizeof(*b) * n);
struct galgorithm_nway_merge_input input = {
.vtable = &nway_merge_input_vtable,
.ctxs = NULL,
.ctxs_count = 0,
.ctx_size = sizeof(struct nway_merge_input_ctx),
.ctx_mover = &nway_ctx_mover,
};
struct nway_merge_output_ctx out_ctx;
const struct galgorithm_nway_merge_output output = {
.vtable = &nway_merge_output_vtable,
.ctx = &out_ctx,
};
// Check 1-way merge.
init_array(a, n);
galgorithm_heapsort(ctx, a, n);
struct nway_merge_input_ctx one_way_input_ctxs[1] = {
{
.next = a,
.end = a + n,
},
};
input.ctxs = one_way_input_ctxs;
input.ctxs_count = 1;
out_ctx.next = b;
galgorithm_nway_merge(ctx, &input, &output);
assert_sorted(ctx, b, n);
// Check 2-way merge.
if (n > 1) {
init_array(a, n);
galgorithm_heapsort(ctx, a, n / 2);
galgorithm_heapsort(ctx, a + n / 2, n - n / 2);
struct nway_merge_input_ctx two_way_input_ctxs[2] = {
{
.next = a,
.end = a + n / 2,
},
{
.next = a + n / 2,
.end = a + n,
},
};
input.ctxs = two_way_input_ctxs;
input.ctxs_count = 2;
out_ctx.next = b;
galgorithm_nway_merge(ctx, &input, &output);
assert_sorted(ctx, b, n);
}
// Check n-way merge with n sorted lists each containing exactly one item.
init_array(a, n);
struct nway_merge_input_ctx *const nway_merge_input_ctxs =
malloc(sizeof(nway_merge_input_ctxs[0]) * n);
for (size_t i = 0; i < n; ++i) {
struct nway_merge_input_ctx *const input_ctx = &nway_merge_input_ctxs[i];
input_ctx->next = a + i;
input_ctx->end = a + (i + 1);
}
input.ctxs = nway_merge_input_ctxs;
input.ctxs_count = n;
out_ctx.next = b;
galgorithm_nway_merge(ctx, &input, &output);
assert_sorted(ctx, b, n);
free(nway_merge_input_ctxs);
free(b);
printf("OK\n");
}
static void item_deleter(void *item)
{
/* do nothing */
(void)item;
}
static void test_priority_queue(const struct gheap_ctx *const ctx,
const size_t n, int *const a)
{
printf(" test_priority_queue(n=%zu) ", n);
// Verify emptry priority queue.
struct gpriority_queue *const q_empty = gpriority_queue_create(ctx,
&item_deleter);
assert(gpriority_queue_empty(q_empty));
assert(gpriority_queue_size(q_empty) == 0);
// Verify non-empty priority queue.
init_array(a, n);
struct gpriority_queue *const q = gpriority_queue_create_from_array(ctx,
&item_deleter, a, n);
assert(!gpriority_queue_empty(q));
assert(gpriority_queue_size(q) == n);
// Pop all items from the priority queue.
int max_item = *(int *)gpriority_queue_top(q);
for (size_t i = 1; i < n; ++i) {
gpriority_queue_pop(q);
assert(gpriority_queue_size(q) == n - i);
assert(*(int *)gpriority_queue_top(q) <= max_item);
max_item = *(int *)gpriority_queue_top(q);
}
assert(*(int *)gpriority_queue_top(q) <= max_item);
gpriority_queue_pop(q);
assert(gpriority_queue_empty(q));
// Push items to priority queue.
for (size_t i = 0; i < n; ++i) {
const int tmp = rand();
gpriority_queue_push(q, &tmp);
assert(gpriority_queue_size(q) == i + 1);
}
// Interleave pushing and popping items in priority queue.
max_item = *(int *)gpriority_queue_top(q);
for (size_t i = 1; i < n; ++i) {
gpriority_queue_pop(q);
assert(*(int*)gpriority_queue_top(q) <= max_item);
const int tmp = rand();
if (tmp > max_item) {
max_item = tmp;
}
gpriority_queue_push(q, &tmp);
}
assert(gpriority_queue_size(q) == n);
printf("OK\n");
}
static void run_all(const struct gheap_ctx *const ctx,
void (*func)(const struct gheap_ctx *, size_t, int *))
{
int *const a = malloc(1001 * sizeof(*a));
for (size_t i = 1; i < 12; ++i) {
func(ctx, i, a);
}
func(ctx, 1001, a);
free(a);
}
static void test_all(const size_t fanout, const size_t page_chunks)
{
printf(" test_all(fanout=%zu, page_chunks=%zu) start\n",
fanout, page_chunks);
const struct gheap_ctx ctx_v = {
.fanout = fanout,
.page_chunks = page_chunks,
.item_size = sizeof(int),
.less_comparer = &less_comparer,
.less_comparer_ctx = (void *)0,
.item_mover = &item_mover,
};
const struct gheap_ctx *const ctx = &ctx_v;
/* Verify parent-child calculations for indexes close to zero and
* indexes close to SIZE_MAX.
*/
static const size_t n = 1000000;
test_parent_child(ctx, 1, n);
test_parent_child(ctx, SIZE_MAX - n, n);
run_all(ctx, test_is_heap);
run_all(ctx, test_make_heap);
run_all(ctx, test_sort_heap);
run_all(ctx, test_push_heap);
run_all(ctx, test_pop_heap);
run_all(ctx, test_swap_max_item);
run_all(ctx, test_restore_heap_after_item_increase);
run_all(ctx, test_restore_heap_after_item_decrease);
run_all(ctx, test_remove_from_heap);
run_all(ctx, test_heapsort);
run_all(ctx, test_partial_sort);
run_all(ctx, test_nway_merge);
run_all(ctx, test_nway_mergesort);
run_all(ctx, test_priority_queue);
printf(" test_all(fanout=%zu, page_chunks=%zu) OK\n", fanout, page_chunks);
}
static void main_test(void)
{
printf("main_test() start\n");
test_all(1, 1);
test_all(2, 1);
test_all(3, 1);
test_all(4, 1);
test_all(101, 1);
test_all(1, 2);
test_all(2, 2);
test_all(3, 2);
test_all(4, 2);
test_all(101, 2);
test_all(1, 3);
test_all(2, 3);
test_all(3, 3);
test_all(4, 3);
test_all(101, 3);
test_all(1, 4);
test_all(2, 4);
test_all(3, 4);
test_all(4, 4);
test_all(101, 4);
test_all(1, 101);
test_all(2, 101);
test_all(3, 101);
test_all(4, 101);
test_all(101, 101);
printf("main_test() OK\n");
}
int main(void)
{
srand(0);
main_test();
return 0;
}