mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-03-21 18:01:15 +00:00
[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:
parent
4f2113bc05
commit
f76964b86b
4 changed files with 384 additions and 0 deletions
|
@ -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
124
include/QF/pqueue.h
Normal 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
|
|
@ -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
|
||||
|
|
254
libs/util/test/test-pqueue.c
Normal file
254
libs/util/test/test-pqueue.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue