mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-17 01:11:45 +00:00
[util] Add functions for binary heaps
Sink, swim, build and sort, both "simple" and with a data parameter for the compare function.
This commit is contained in:
parent
f514345d77
commit
e4984aad17
7 changed files with 392 additions and 3 deletions
|
@ -18,6 +18,7 @@ include_qf = \
|
|||
include/QF/fbsearch.h \
|
||||
include/QF/gib.h \
|
||||
include/QF/hash.h \
|
||||
include/QF/heapsort.h \
|
||||
include/QF/idparse.h \
|
||||
include/QF/image.h \
|
||||
include/QF/in_event.h \
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
|
||||
*/
|
||||
|
||||
#ifndef __QF_bsearch_h
|
||||
#define __QF_bsearch_h
|
||||
#ifndef __QF_fbsearch_h
|
||||
#define __QF_fbsearch_h
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
@ -46,4 +46,4 @@ void *fbsearch (const void *key, const void *base, size_t nmemb, size_t size,
|
|||
void *fbsearch_r (const void *key, const void *base, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg);
|
||||
|
||||
#endif//__QF_bsearch_h
|
||||
#endif//__QF_fbsearch_h
|
||||
|
|
61
include/QF/heapsort.h
Normal file
61
include/QF/heapsort.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
heapsort.h
|
||||
|
||||
Priority heap related functions
|
||||
|
||||
Copyright (C) 2021 Bill Currie <bill@taniwha.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __QF_heapsort_h
|
||||
#define __QF_heapsort_h
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef __compar_fn_t_defined
|
||||
#define __compar_fn_t_defined
|
||||
typedef int (*__compar_fn_t)(const void *, const void *);
|
||||
#endif
|
||||
|
||||
#ifndef __compar_d_fn_t_defined
|
||||
#define __compar_d_fn_t_defined
|
||||
typedef int (*__compar_d_fn_t)(const void *, const void *, void *);
|
||||
#endif
|
||||
|
||||
void heap_sink (void *base, size_t ind, size_t nmemb, size_t size,
|
||||
__compar_fn_t cmp);
|
||||
void heap_sink_r (void *base, size_t ind, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg);
|
||||
|
||||
void heap_swim (void *base, size_t ind, size_t nmemb, size_t size,
|
||||
__compar_fn_t cmp);
|
||||
void heap_swim_r (void *base, size_t ind, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg);
|
||||
|
||||
void heap_build (void *base, size_t nmemb, size_t size, __compar_fn_t cmp);
|
||||
void heap_build_r (void *base, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg);
|
||||
|
||||
void heapsort (void *base, size_t nmemb, size_t size, __compar_fn_t cmp);
|
||||
void heapsort_r (void *base, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg);
|
||||
|
||||
#endif//__QF_heapsort_h
|
|
@ -56,6 +56,7 @@ libs_util_libQFutil_la_SOURCES= \
|
|||
libs/util/dstring.c \
|
||||
libs/util/fendian.c \
|
||||
libs/util/hash.c \
|
||||
libs/util/heapsort.c \
|
||||
libs/util/idparse.c \
|
||||
libs/util/info.c \
|
||||
libs/util/link.c \
|
||||
|
|
146
libs/util/heapsort.c
Normal file
146
libs/util/heapsort.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "QF/heapsort.h"
|
||||
#include "QF/qtypes.h"
|
||||
|
||||
static inline void
|
||||
swap_elements (void *a, void *b, size_t size)
|
||||
{
|
||||
byte tmp[size];
|
||||
memcpy (tmp, a, size);
|
||||
memcpy (a, b, size);
|
||||
memcpy (b, tmp, size);
|
||||
}
|
||||
|
||||
VISIBLE void
|
||||
heap_sink (void *base, size_t ind, size_t nmemb, size_t size,
|
||||
__compar_fn_t cmp)
|
||||
{
|
||||
size_t left_ind = 2 * ind + 1;
|
||||
size_t right_ind = 2 * ind + 2;
|
||||
void *node = (byte *) base + ind * size;
|
||||
void *left = (byte *) base + left_ind * size;
|
||||
void *right = (byte *) base + right_ind * size;
|
||||
|
||||
size_t largest_ind = ind;
|
||||
void *largest = node;
|
||||
|
||||
if (left_ind < nmemb && cmp (left, node) > 0) {
|
||||
largest = left;
|
||||
largest_ind = left_ind;
|
||||
}
|
||||
if (right_ind < nmemb && cmp (right, largest) > 0) {
|
||||
largest = right;
|
||||
largest_ind = right_ind;
|
||||
}
|
||||
if (largest_ind != ind) {
|
||||
swap_elements (largest, node, size);
|
||||
heap_sink (base, largest_ind, nmemb, size, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
VISIBLE void
|
||||
heap_sink_r (void *base, size_t ind, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
size_t left_ind = 2 * ind + 1;
|
||||
size_t right_ind = 2 * ind + 2;
|
||||
void *node = (byte *) base + ind * size;
|
||||
void *left = (byte *) base + left_ind * size;
|
||||
void *right = (byte *) base + right_ind * size;
|
||||
|
||||
size_t largest_ind = ind;
|
||||
void *largest = node;
|
||||
|
||||
if (left_ind < nmemb && cmp (left, node, arg) > 0) {
|
||||
largest = left;
|
||||
largest_ind = left_ind;
|
||||
}
|
||||
if (right_ind < nmemb && cmp (right, largest, arg) > 0) {
|
||||
largest = right;
|
||||
largest_ind = right_ind;
|
||||
}
|
||||
if (largest_ind != ind) {
|
||||
swap_elements (largest, node, size);
|
||||
heap_sink_r (base, largest_ind, nmemb, size, cmp, arg);
|
||||
}
|
||||
}
|
||||
|
||||
VISIBLE void
|
||||
heap_swim (void *base, size_t ind, size_t nmemb, size_t size,
|
||||
__compar_fn_t cmp)
|
||||
{
|
||||
size_t parent_ind = (ind - 1) / 2;
|
||||
void *node = (byte *) base + ind * size;
|
||||
void *parent = (byte *) base + parent_ind * size;
|
||||
|
||||
if (ind > 0 && cmp (node, parent) > 0) {
|
||||
swap_elements (node, parent, size);
|
||||
heap_swim (base, parent_ind, nmemb, size, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
VISIBLE void
|
||||
heap_swim_r (void *base, size_t ind, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
size_t parent_ind = (ind - 1) / 2;
|
||||
void *node = (byte *) base + ind * size;
|
||||
void *parent = (byte *) base + parent_ind * size;
|
||||
|
||||
if (ind > 0 && cmp (node, parent, arg) > 0) {
|
||||
swap_elements (node, parent, size);
|
||||
heap_swim_r (base, parent_ind, nmemb, size, cmp, arg);
|
||||
}
|
||||
}
|
||||
|
||||
VISIBLE void
|
||||
heap_build (void *base, size_t nmemb, size_t size, __compar_fn_t cmp)
|
||||
{
|
||||
if (nmemb < 2) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = nmemb / 2; i-- > 0; ) {
|
||||
heap_sink (base, i, nmemb, size, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
VISIBLE void
|
||||
heap_build_r (void *base, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
if (nmemb < 2) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = nmemb / 2; i-- > 0; ) {
|
||||
heap_sink_r (base, i, nmemb, size, cmp, arg);
|
||||
}
|
||||
}
|
||||
|
||||
VISIBLE void
|
||||
heapsort (void *base, size_t nmemb, size_t size, __compar_fn_t cmp)
|
||||
{
|
||||
heap_build (base, nmemb, size, cmp);
|
||||
for (size_t i = nmemb; i-- > 1; ) {
|
||||
void *last = (byte *) base + i * size;
|
||||
swap_elements (base, last, size);
|
||||
heap_sink (base, 0, i, size, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
VISIBLE void
|
||||
heapsort_r (void *base, size_t nmemb, size_t size, __compar_d_fn_t cmp,
|
||||
void *arg)
|
||||
{
|
||||
heap_build_r (base, nmemb, size, cmp, arg);
|
||||
for (size_t i = nmemb; i-- > 1; ) {
|
||||
void *last = (byte *) base + i * size;
|
||||
swap_elements (base, last, size);
|
||||
heap_sink_r (base, 0, i, size, cmp, arg);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ libs_util_tests = \
|
|||
libs/util/test/test-darray \
|
||||
libs/util/test/test-dq \
|
||||
libs/util/test/test-half \
|
||||
libs/util/test/test-heapsort \
|
||||
libs/util/test/test-mat3 \
|
||||
libs/util/test/test-mat4 \
|
||||
libs/util/test/test-plist \
|
||||
|
@ -65,6 +66,10 @@ libs_util_test_test_half_SOURCES=libs/util/test/test-half.c
|
|||
libs_util_test_test_half_LDADD=libs/util/libQFutil.la
|
||||
libs_util_test_test_half_DEPENDENCIES=libs/util/libQFutil.la
|
||||
|
||||
libs_util_test_test_heapsort_SOURCES=libs/util/test/test-heapsort.c
|
||||
libs_util_test_test_heapsort_LDADD=libs/util/libQFutil.la
|
||||
libs_util_test_test_heapsort_DEPENDENCIES=libs/util/libQFutil.la
|
||||
|
||||
libs_util_test_test_mat3_SOURCES=libs/util/test/test-mat3.c
|
||||
libs_util_test_test_mat3_LDADD=libs/util/libQFutil.la
|
||||
libs_util_test_test_mat3_DEPENDENCIES=libs/util/libQFutil.la
|
||||
|
|
175
libs/util/test/test-heapsort.c
Normal file
175
libs/util/test/test-heapsort.c
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
test-heapsort.c
|
||||
|
||||
Priority heap related tests
|
||||
|
||||
Copyright (C) 2021 Bill Currie <bill@taniwha.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "QF/heapsort.h"
|
||||
|
||||
#define SIZEOF(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
static int sink_data[] = {
|
||||
4, 7, 8, 3, 2, 6, 5,
|
||||
};
|
||||
static int sink_expect[] = {
|
||||
8, 7, 6, 3, 2, 4, 5,
|
||||
};
|
||||
|
||||
static int swim_data[] = {
|
||||
10, 8, 9, 7, 4, 1, 3, 2,
|
||||
};
|
||||
static int swim_expect[] = {
|
||||
10, 8, 9, 7, 4, 1, 3, 2,
|
||||
};
|
||||
|
||||
static int swim_data2[] = {
|
||||
10, 8, 9, 7, 4, 1, 3, 0, 2, 5,
|
||||
};
|
||||
static int swim_expect2[] = {
|
||||
10, 8, 9, 7, 5, 1, 3, 0, 2, 4,
|
||||
};
|
||||
|
||||
static int build_data[] = {
|
||||
1, 4, 3, 7, 8, 9, 10,
|
||||
};
|
||||
static int build_expect1[] = {
|
||||
10, 8, 9, 7, 4, 1, 3,
|
||||
};
|
||||
static int build_expect2[] = {
|
||||
9, 8, 3, 7, 4, 1, 10,
|
||||
};
|
||||
static int build_expect3[] = {
|
||||
8, 7, 3, 1, 4, 9, 10,
|
||||
};
|
||||
|
||||
static int sort_data[] = {
|
||||
4, 3, 7, 1, 8, 5,
|
||||
};
|
||||
static int sort_expect[] = {
|
||||
1, 3, 4, 5, 7, 8,
|
||||
};
|
||||
|
||||
static int
|
||||
compare (const void *a, const void *b)
|
||||
{
|
||||
return *(const int *)a - *(const int *)b;
|
||||
}
|
||||
#if 0
|
||||
static int
|
||||
compared (const void *a, const void *b, void *d)
|
||||
{
|
||||
return *(const int *)a - *(const int *)b;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
test_sink (int *data, size_t nele)
|
||||
{
|
||||
heap_sink (data, 0, nele, sizeof (int), compare);
|
||||
}
|
||||
|
||||
static void
|
||||
test_swim (int *data, size_t nele)
|
||||
{
|
||||
heap_swim (data, nele - 1, nele, sizeof (int), compare);
|
||||
}
|
||||
|
||||
static void
|
||||
test_build (int *data, size_t nele)
|
||||
{
|
||||
heap_build (data, nele, sizeof (int), compare);
|
||||
}
|
||||
|
||||
static void
|
||||
test_sort (int *data, size_t nele)
|
||||
{
|
||||
heapsort (data, nele, sizeof (int), compare);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const int *data;
|
||||
const int *expect;
|
||||
size_t nele;
|
||||
void (*test) (int *data, size_t nele);
|
||||
size_t sub;
|
||||
} test_t;
|
||||
|
||||
static test_t tests[] = {
|
||||
{ sink_data, sink_expect, SIZEOF (sink_data), test_sink },
|
||||
{ swim_data, swim_expect, SIZEOF (swim_data), test_swim },
|
||||
{ swim_data2, swim_expect2, SIZEOF (swim_data2), test_swim },
|
||||
{ build_data, build_expect1, SIZEOF (build_data), test_build },
|
||||
{ build_data, build_expect2, SIZEOF (build_data), test_build, 1 },
|
||||
{ build_data, build_expect3, SIZEOF (build_data), test_build, 2 },
|
||||
{ sort_data, sort_expect, SIZEOF (sort_data), test_sort },
|
||||
};
|
||||
#define num_tests ((int) SIZEOF (tests))
|
||||
|
||||
static void
|
||||
dump_array (const int *arr, size_t n)
|
||||
{
|
||||
while (n-- > 0) {
|
||||
printf (" %d", *arr++);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
static int
|
||||
run_test (test_t *test)
|
||||
{
|
||||
int test_data[test->nele];
|
||||
|
||||
memcpy (test_data, test->data, test->nele * sizeof (int));
|
||||
printf ("start : ");
|
||||
dump_array (test_data, test->nele);
|
||||
test->test (test_data, test->nele - test->sub);
|
||||
printf ("got : ");
|
||||
dump_array (test_data, test->nele);
|
||||
printf ("expect: ");
|
||||
dump_array (test->expect, test->nele);
|
||||
if (memcmp (test_data, test->expect, test->nele * sizeof (int))) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
for (int i = 0; i < num_tests; i++) {
|
||||
if (!run_test (&tests[i])) {
|
||||
printf ("test %d failed\n", i);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
Loading…
Reference in a new issue