[util] Add an priority queue implementation

Done via macros (like darray and ringbuffer). Might prove useful for
qfvis and maybe dynamic lights.
This commit is contained in:
Bill Currie 2021-08-02 13:29:55 +09:00
parent 4f2113bc05
commit f76964b86b
4 changed files with 384 additions and 0 deletions

View file

@ -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 \

124
include/QF/pqueue.h Normal file
View file

@ -0,0 +1,124 @@
/*
pqueue.h
Priority Queues
Copyright (C) 2021 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
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 <stdlib.h>
#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

View file

@ -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

View file

@ -0,0 +1,254 @@
/*
test-pqueue.c
Priority queue 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/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;
}