From f76964b86bb6fd83a3996f74e06050b7c050e662 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Mon, 2 Aug 2021 13:29:55 +0900 Subject: [PATCH] [util] Add an priority queue implementation Done via macros (like darray and ringbuffer). Might prove useful for qfvis and maybe dynamic lights. --- include/QF/Makemodule.am | 1 + include/QF/pqueue.h | 124 +++++++++++++++++ libs/util/test/Makemodule.am | 5 + libs/util/test/test-pqueue.c | 254 +++++++++++++++++++++++++++++++++++ 4 files changed, 384 insertions(+) create mode 100644 include/QF/pqueue.h create mode 100644 libs/util/test/test-pqueue.c diff --git a/include/QF/Makemodule.am b/include/QF/Makemodule.am index 8f8379c01..af3c99982 100644 --- a/include/QF/Makemodule.am +++ b/include/QF/Makemodule.am @@ -42,6 +42,7 @@ include_qf = \ include/QF/png.h \ include/QF/plist.h \ include/QF/plugin.h \ + include/QF/pqueue.h \ include/QF/pr_comp.h \ include/QF/pr_debug.h \ include/QF/pr_obj.h \ diff --git a/include/QF/pqueue.h b/include/QF/pqueue.h new file mode 100644 index 000000000..a9428ae48 --- /dev/null +++ b/include/QF/pqueue.h @@ -0,0 +1,124 @@ +/* + pqueue.h + + Priority Queues + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/08/02 + + 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_pqueue_h +#define __QF_pqueue_h + +#include + +#include "QF/heapsort.h" +#include "QF/sys.h" + +/** \defgroup pqueue Priority Queues + \ingroup utils + + Priority queue management +*/ +///@{ + +/** The structure def for a priority queue with elements of the given type. + + This is just the def of a struct delcaration: it is useless on its own. + The intended usage is something like: + + typedef struct priority_queue_s PQUEUE_TYPE (int) priority_queue_t; + + This allows full flexibility in just how the actual type is used. + + The \a size field is the number of elements currently in the queue, and + the \a maxSize field is the number of elements the queue can hold. The + priority queue functions will never resize the array. However, with a + little glue logic, priority queues can be used with dynamic arrays. + + \param T The type to use for the element array, which is accessed + by the \a a field. +*/ +#define PQUEUE_TYPE(T) \ + { \ + size_t size; \ + size_t maxSize; \ + T *a; \ + int (*compare) (const T *a, const T *b, void *data); \ + void *data; \ + } + +#define PQUEUE_IS_EMPTY(queue) ((queue)->size == 0) + +#define PQUEUE_IS_FULL(queue) \ + ({ \ + __auto_type q = (queue); \ + q->size == q->maxSize; \ + }) + +#define PQUEUE_PEEK(queue) ((queue)->a[0]) + +#define PQUEUE_INSERT(queue, value) \ + do { \ + __auto_type q = (queue); \ + if (PQUEUE_IS_FULL (q)) { \ + Sys_Error ("Priority queue full"); \ + } \ + q->a[q->size++] = (value); \ + heap_swim_r (q->a, q->size - 1, q->size, sizeof (q->a[0]), \ + (__compar_d_fn_t) q->compare, q->data); \ + } while (0) + +#define PQUEUE_REMOVE(queue) \ + ({ \ + __auto_type q = (queue); \ + if (PQUEUE_IS_EMPTY (q)) { \ + Sys_Error ("Priority queue empty"); \ + } \ + __auto_type value = q->a[0]; \ + q->a[0] = q->a[--q->size]; \ + heap_sink_r (q->a, 0, q->size, sizeof (q->a[0]), \ + (__compar_d_fn_t) q->compare, q->data); \ + value; \ + }) + +#define PQUEUE_ADJUST(queue, index) \ + do { \ + __auto_type q = (queue); \ + size_t ind = (index); \ + if (ind >= q->size) { \ + Sys_Error ("Priority queue index error"); \ + } \ + if (ind>0 && q->compare(q->a + ind, q->a + (ind-1)/2, q->data)>0) { \ + heap_swim_r (q->a, ind, q->size, sizeof (q->a[0]), \ + (__compar_d_fn_t) q->compare, q->data); \ + } else { \ + heap_sink_r (q->a, ind, q->size, sizeof (q->a[0]), \ + (__compar_d_fn_t) q->compare, q->data); \ + } \ + } while (0) + +///@} + +#endif//__QF_pqueue_h diff --git a/libs/util/test/Makemodule.am b/libs/util/test/Makemodule.am index 3b1510082..ed3b0bb99 100644 --- a/libs/util/test/Makemodule.am +++ b/libs/util/test/Makemodule.am @@ -13,6 +13,7 @@ libs_util_tests = \ libs/util/test/test-mat3 \ libs/util/test/test-mat4 \ libs/util/test/test-plist \ + libs/util/test/test-pqueue \ libs/util/test/test-qfs \ libs/util/test/test-quat \ libs/util/test/test-seb \ @@ -82,6 +83,10 @@ libs_util_test_test_plist_SOURCES=libs/util/test/test-plist.c libs_util_test_test_plist_LDADD=libs/util/libQFutil.la libs_util_test_test_plist_DEPENDENCIES=libs/util/libQFutil.la +libs_util_test_test_pqueue_SOURCES=libs/util/test/test-pqueue.c +libs_util_test_test_pqueue_LDADD=libs/util/libQFutil.la +libs_util_test_test_pqueue_DEPENDENCIES=libs/util/libQFutil.la + libs_util_test_test_qfs_SOURCES=libs/util/test/test-qfs.c libs_util_test_test_qfs_LDADD=libs/util/libQFutil.la libs_util_test_test_qfs_DEPENDENCIES=libs/util/libQFutil.la diff --git a/libs/util/test/test-pqueue.c b/libs/util/test/test-pqueue.c new file mode 100644 index 000000000..4e877c569 --- /dev/null +++ b/libs/util/test/test-pqueue.c @@ -0,0 +1,254 @@ +/* + test-pqueue.c + + Priority queue 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/pqueue.h" + +#define SIZEOF(x) (sizeof(x) / sizeof(x[0])) + +static int +compare (const int *a, const int *b, void *arg) +{ + //printf ("%d %d\n", *a, *b); + return *a - *b; +} + +static int +rev (const int *a, const int *b, void *arg) +{ + return *b - *a; +} + +static int queue_array[7]; +static struct PQUEUE_TYPE (int) pqueue = { + .maxSize = SIZEOF (queue_array), + .compare = compare, + .a = queue_array, + .data = 0, +}; + +static int +pq_is_empty (void) +{ + return PQUEUE_IS_EMPTY (&pqueue); +} + +static int +pq_is_full (void) +{ + return PQUEUE_IS_FULL (&pqueue); +} + +static int +pq_peek (void) +{ + return PQUEUE_PEEK (&pqueue); +} + +static void +pq_insert (int value) +{ + PQUEUE_INSERT (&pqueue, value); +} + +static int +pq_remove (void) +{ + return PQUEUE_REMOVE (&pqueue); +} + +static void +pq_adjust (int index) +{ + PQUEUE_ADJUST (&pqueue, index); +} + +static void +dump_array (const int *arr, size_t n) +{ + while (n-- > 0) { + printf (" %d", *arr++); + } + printf ("\n"); +} + + +static int +test_insert (const int *data, size_t count) +{ + while (count-- > 0) { + pq_insert (*data++); + } + return 1; +} + +static int +test_remove (const int *data, size_t count) +{ + while (count-- > 0) { + if (pq_remove () != *data++) { + return 0; + } + } + return 1; +} + +static int +test_adjust (const int *data, size_t count) +{ + //printf ("adjust start\n"); + pqueue.a[count] = *data; + pq_adjust (count); + //printf ("adjust end\n"); + return 1; +} + +static int +test_peek (const int *data, size_t count) +{ + return pq_peek () == *data; +} + +static int +test_size (const int *data, size_t count) +{ + return pqueue.size == count; +} + +static int +test_is_empty (const int *data, size_t count) +{ + return pq_is_empty () == *data; +} + +static int +test_is_full (const int *data, size_t count) +{ + return pq_is_full () == *data; +} + +static int +dump_queue (const int *data, size_t count) +{ + if (0) { + dump_array (pqueue.a, pqueue.size); + } + return 1; +} + +static int true = 1; +static int false = 0; + +static int data[] = { 3, 2, 5, 6, 8, 1, 9 }; +static int data1[] = { 3, 7, 5, 6, 8, 1, 9 }; +static int data2[] = { 3, 7, 5, 6, 8, 1, 4 }; +static int data3[] = { 3, 7, 0, 6, 8, 1, 4 }; +static int sort[SIZEOF(data)]; +static int sort1[SIZEOF(data1)]; +static int sort2[SIZEOF(data2)]; +static int sort3[SIZEOF(data3)]; + +typedef struct { + const int *data; + size_t nele; + int (*test) (const int *data, size_t nele); +} test_t; + +static test_t tests[] = { + { &true, 1, test_is_empty }, + { &false, 1, test_is_full }, + { 0, 0, test_size }, + + { data + 0, 1, test_insert }, + { 0, 1, test_size }, + { &false, 1, test_is_empty }, + { &false, 1, test_is_full }, + { data + 0, 1, test_peek }, + { data + 0, 1, test_remove }, + { &true, 1, test_is_empty }, + { &false, 1, test_is_full }, + { 0, 0, test_size }, + + { data + 0, 3, test_insert }, + { 0, 3, test_size }, + { data + 2, 1, test_peek }, + { data + 2, 1, test_remove }, + { data + 0, 1, test_remove }, + { data + 1, 1, test_remove }, + { &true, 1, test_is_empty }, + + { data + 0, SIZEOF(data), test_insert }, + { 0, SIZEOF(data), test_size }, + { &false, 1, test_is_empty }, + { &true, 1, test_is_full }, + { 0, 0, dump_queue }, + { sort1 + 2, 3, test_adjust }, + { 0, 0, dump_queue }, + { sort2 + 4, 0, test_adjust }, + { 0, 0, dump_queue }, + { sort3 + 7, 4, test_adjust }, + { 0, 0, dump_queue }, + { sort3 + 0, SIZEOF(sort), test_remove }, +}; +#define num_tests ((int) SIZEOF (tests)) + +static int +run_test (const test_t *test) +{ + return test->test (test->data, test->nele); +} + +int +main(int argc, const char **argv) +{ + int ret = 0; + + memcpy (sort, data, sizeof (sort)); + memcpy (sort1, data1, sizeof (sort1)); + memcpy (sort2, data2, sizeof (sort2)); + memcpy (sort3, data3, sizeof (sort3)); + heapsort_r (sort, SIZEOF(sort), sizeof(int), (__compar_d_fn_t)rev, 0); + heapsort_r (sort1, SIZEOF(sort1), sizeof(int), (__compar_d_fn_t)rev, 0); + heapsort_r (sort2, SIZEOF(sort2), sizeof(int), (__compar_d_fn_t)rev, 0); + heapsort_r (sort3, SIZEOF(sort3), sizeof(int), (__compar_d_fn_t)rev, 0); + //dump_array (sort, SIZEOF(sort)); + //dump_array (sort1, SIZEOF(sort1)); + //dump_array (sort2, SIZEOF(sort2)); + //dump_array (sort3, SIZEOF(sort3)); + for (int i = 0; i < num_tests; i++) { + if (!run_test (&tests[i])) { + printf ("test %d failed\n", i); + ret = 1; + } + } + return ret; +}