diff --git a/include/QF/Makemodule.am b/include/QF/Makemodule.am index e0f9ebb07..8f8379c01 100644 --- a/include/QF/Makemodule.am +++ b/include/QF/Makemodule.am @@ -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 \ diff --git a/include/QF/fbsearch.h b/include/QF/fbsearch.h index b6463969a..19611f3c5 100644 --- a/include/QF/fbsearch.h +++ b/include/QF/fbsearch.h @@ -25,8 +25,8 @@ */ -#ifndef __QF_bsearch_h -#define __QF_bsearch_h +#ifndef __QF_fbsearch_h +#define __QF_fbsearch_h #include @@ -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 diff --git a/include/QF/heapsort.h b/include/QF/heapsort.h new file mode 100644 index 000000000..153c4faee --- /dev/null +++ b/include/QF/heapsort.h @@ -0,0 +1,61 @@ +/* + heapsort.h + + Priority heap related functions + + Copyright (C) 2021 Bill Currie + + 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 + +#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 diff --git a/libs/util/Makemodule.am b/libs/util/Makemodule.am index 827696908..6e0dbbb23 100644 --- a/libs/util/Makemodule.am +++ b/libs/util/Makemodule.am @@ -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 \ diff --git a/libs/util/heapsort.c b/libs/util/heapsort.c new file mode 100644 index 000000000..aca74e574 --- /dev/null +++ b/libs/util/heapsort.c @@ -0,0 +1,146 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#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); + } +} diff --git a/libs/util/test/Makemodule.am b/libs/util/test/Makemodule.am index 4a0e2734c..3b1510082 100644 --- a/libs/util/test/Makemodule.am +++ b/libs/util/test/Makemodule.am @@ -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 diff --git a/libs/util/test/test-heapsort.c b/libs/util/test/test-heapsort.c new file mode 100644 index 000000000..d3841cc2c --- /dev/null +++ b/libs/util/test/test-heapsort.c @@ -0,0 +1,175 @@ +/* + test-heapsort.c + + Priority heap related tests + + Copyright (C) 2021 Bill Currie + + 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 +#include + +#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; +}