Add priority_queue

This commit is contained in:
Bailey Thompson
2018-01-01 18:06:13 -05:00
committed by GitHub
parent a8cfa76db4
commit bcfa6bb4ce
7 changed files with 417 additions and 6 deletions

226
src/priority_queue.c Normal file
View File

@@ -0,0 +1,226 @@
/*
* Copyright (c) 2017-2018 Bailey Thompson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include "vector.h"
#include "priority_queue.h"
/*
* Must exactly match the declaration in vector.c
*/
struct _vector {
size_t data_size;
int offset;
int space;
void *storage;
};
struct _priority_queue {
vector data;
size_t data_size;
int (*comparator)(const void *const one, const void *const two);
};
/**
* Initializes a priority queue, which adapts a container to provide priority
* queue. Adapts the vector container.
*
* @param data_size The size of the data in the priority queue.
* @param comparator The priority comparator function.
*
* @return The newly-initialized priority queue, or NULL if memory allocation
* error.
*/
priority_queue priority_queue_init(const size_t data_size,
int (*comparator)(const void *const,
const void *const))
{
struct _priority_queue *const init = malloc(sizeof(struct _priority_queue));
if (init == NULL) {
return NULL;
}
init->data_size = data_size;
init->data = vector_init(data_size);
if (init->data == NULL) {
free(init);
return NULL;
}
init->comparator = comparator;
return init;
}
/**
* Gets the size of the priority queue.
*
* @param me The priority queue to check.
*
* @return The size of the priority queue.
*/
int priority_queue_size(priority_queue me)
{
return vector_size(me->data);
}
/**
* Determines whether or not the priority queue is empty.
*
* @param me The priority queue to check.
*
* @return If the priority queue is empty.
*/
bool priority_queue_is_empty(priority_queue me)
{
return vector_is_empty(me->data);
}
/**
* Adds an element to the priority queue.
*
* @param me The priority queue to add an element to.
* @param data The data to add to the queue.
*
* @return 0 No error.
* -ENOMEM Out of memory.
*/
int priority_queue_push(priority_queue me, void *const data)
{
void *const temp = malloc(me->data_size);
if (temp == NULL) {
return -ENOMEM;
}
const int rc = vector_add_last(me->data, data);
if (rc != 0) {
free(temp);
return rc;
}
void *const vector_storage = me->data->storage;
int index = vector_size(me->data) - 1;
int parent_index = (index - 1) / 2;
void *data_index = vector_storage + index * me->data_size;
void *data_parent_index = vector_storage + parent_index * me->data_size;
while (index > 0 && me->comparator(data_index, data_parent_index) > 0) {
memcpy(temp, data_parent_index, me->data_size);
memcpy(data_parent_index, data_index, me->data_size);
memcpy(data_index, temp, me->data_size);
index = parent_index;
parent_index = (index - 1) / 2;
data_index = vector_storage + index * me->data_size;
data_parent_index = vector_storage + parent_index * me->data_size;
}
free(temp);
return 0;
}
/**
* Removes the highest priority element from the priority queue.
*
* @param data The data to have copied from the priority queue.
* @param me The priority queue to pop the next element from.
*
* @return If the priority queue contained elements.
*/
bool priority_queue_pop(void *const data, priority_queue me)
{
const int rc = vector_get_first(data, me->data);
if (rc != 0) {
return false;
}
void *const vector_storage = me->data->storage;
const int size = vector_size(me->data) - 1;
void *const temp = vector_storage + size * me->data_size;
memcpy(vector_storage, temp, me->data_size);
int index = 0;
int left_index = 1;
int right_index = 2;
void *data_index = vector_storage;
void *data_left_index = vector_storage + left_index * me->data_size;
void *data_right_index = vector_storage + right_index * me->data_size;
while (true) {
if (right_index < size &&
me->comparator(data_right_index, data_left_index) > 0 &&
me->comparator(data_right_index, data_index) > 0) {
// Swap parent and right child then continue down right child.
memcpy(temp, data_index, me->data_size);
memcpy(data_index, data_right_index, me->data_size);
memcpy(data_right_index, temp, me->data_size);
index = right_index;
} else if (left_index < size &&
me->comparator(data_left_index, data_index) > 0) {
// Swap parent and left child then continue down left child.
memcpy(temp, data_index, me->data_size);
memcpy(data_index, data_left_index, me->data_size);
memcpy(data_left_index, temp, me->data_size);
index = left_index;
} else {
break;
}
left_index = 2 * index + 1;
right_index = 2 * index + 2;
data_index = vector_storage + index * me->data_size;
data_left_index = vector_storage + left_index * me->data_size;
data_right_index = vector_storage + right_index * me->data_size;
}
vector_remove_last(me->data);
return true;
}
/**
* Gets the highest priority element in the priority queue.
*
* @param data Out copy of the highest priority element in the priority queue.
* @param me The priority queue to copy from.
*
* @return If the priority queue contained elements.
*/
bool priority_queue_front(void *const data, priority_queue me)
{
return vector_get_first(data, me->data) == 0;
}
/**
* Clears the elements from the priority queue.
*
* @param me The priority queue to clear.
*
* @return 0 No error.
* -ENOMEM Out of memory.
*/
int priority_queue_clear(priority_queue me)
{
return vector_clear(me->data);
}
/**
* Frees the priority queue memory.
*
* @param me The priority queue to free from memory.
*
* @return NULL
*/
priority_queue priority_queue_destroy(priority_queue me)
{
vector_destroy(me->data);
free(me);
return NULL;
}

52
src/priority_queue.h Normal file
View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2017-2018 Bailey Thompson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CONTAINERS_PRIORITY_QUEUE_H
#define CONTAINERS_PRIORITY_QUEUE_H
#include <stdbool.h>
typedef struct _priority_queue *priority_queue;
// Starting
priority_queue priority_queue_init(size_t data_size,
int (*comparator)(const void *const one,
const void *const two));
// Utility
int priority_queue_size(priority_queue me);
bool priority_queue_is_empty(priority_queue me);
// Adding
int priority_queue_push(priority_queue me, void *data);
// Removing
bool priority_queue_pop(void *data, priority_queue me);
// Getting
bool priority_queue_front(void *data, priority_queue me);
// Ending
int priority_queue_clear(priority_queue me);
priority_queue priority_queue_destroy(priority_queue me);
#endif /* CONTAINERS_PRIORITY_QUEUE_H */

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017 Bailey Thompson
* Copyright (c) 2017-2018 Bailey Thompson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,8 +32,8 @@ struct _queue {
};
/**
* Initializes a queue which is first-in first-out. Uses a deque as the
* underlying implementation.
* Initializes a queue, which adapts a container to provide queue
* (first-in first-out). Adapts the deque container.
*
* @param data_size The size of each element.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017 Bailey Thompson
* Copyright (c) 2017-2018 Bailey Thompson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -29,8 +29,8 @@ struct _stack {
};
/**
* Initializes a stack which is last-in first-out. Uses a deque as the
* underlying implementation.
* Initializes a stack, which adapts a container to provide stack
* (last-in first-out). Adapts the deque container.
*
* @param data_size The size of each data element in the stack.
*

131
tst/priority_queue.c Normal file
View File

@@ -0,0 +1,131 @@
#include "test.h"
#include "../src/vector.h"
#include "../src/priority_queue.h"
/*
* Include this for the stubs.
*/
struct _vector {
size_t data_size;
int offset;
int space;
void *storage;
};
/*
* Include this for the stubs.
*/
struct _priority_queue {
vector data;
size_t data_size;
int (*comparator)(const void *const one, const void *const two);
};
static void priority_queue_verify(priority_queue me)
{
void *const vector_storage = me->data->storage;
const int size = vector_size(me->data);
for (int i = 0; i < size; i++) {
const int val = *(int *) (vector_storage + i * me->data_size);
const int left_child = 2 * i + 1;
const int right_child = 2 * i + 2;
if (left_child < size) {
void *left_data = vector_storage + left_child * me->data_size;
const int left_val = *(int *) left_data;
assert(val >= left_val);
}
if (right_child < size) {
void *right_data = vector_storage + right_child * me->data_size;
const int right_val = *(int *) right_data;
assert(val >= right_val);
}
}
}
static int compare_int(const void *const one, const void *const two)
{
const int a = *(int *) one;
const int b = *(int *) two;
return a - b;
}
int stub_priority_queue_push(priority_queue me, void *const data)
{
const int ret = priority_queue_push(me, data);
priority_queue_verify(me);
return ret;
}
bool stub_priority_queue_pop(void *const data, priority_queue me)
{
const bool ret = priority_queue_pop(data, me);
priority_queue_verify(me);
return ret;
}
void test_priority_queue(void)
{
priority_queue a = priority_queue_init(sizeof(int), compare_int);
assert(a != NULL);
assert(priority_queue_size(a) == 0);
assert(priority_queue_is_empty(a));
int b = 5;
stub_priority_queue_push(a, &b);
b = 2;
stub_priority_queue_push(a, &b);
b = 7;
stub_priority_queue_push(a, &b);
b = 3;
stub_priority_queue_push(a, &b);
b = 4;
stub_priority_queue_push(a, &b);
b = 5;
stub_priority_queue_push(a, &b);
b = 9;
stub_priority_queue_push(a, &b);
b = 2;
stub_priority_queue_push(a, &b);
b = 3;
stub_priority_queue_push(a, &b);
b = -5;
stub_priority_queue_push(a, &b);
b = 7;
stub_priority_queue_push(a, &b);
b = 3;
stub_priority_queue_push(a, &b);
b = 4;
stub_priority_queue_push(a, &b);
b = 3;
stub_priority_queue_push(a, &b);
b = 11;
stub_priority_queue_push(a, &b);
b = 6;
stub_priority_queue_push(a, &b);
assert(priority_queue_size(a) == 16);
assert(!priority_queue_is_empty(a));
b = 0xdeadbeef;
priority_queue_front(&b, a);
assert(b == 11);
b = 0xdeadbeef;
stub_priority_queue_pop(&b, a);
assert(b == 11);
assert(priority_queue_size(a) == 15);
b = 0xdeadbeef;
priority_queue_front(&b, a);
assert(b == 9);
int c = b;
for (int i = 0; i < 15; i++) {
stub_priority_queue_pop(&b, a);
assert(b <= c);
c = b;
assert(priority_queue_size(a) == 15 - i - 1);
}
priority_queue_clear(a);
assert(priority_queue_is_empty(a));
b = 0xdeadbeef;
assert(!priority_queue_front(&b, a));
assert(b == 0xdeadbeef);
assert(priority_queue_is_empty(a));
a = priority_queue_destroy(a);
assert(a == NULL);
}

View File

@@ -17,5 +17,6 @@ int main(void)
test_unordered_multimap();
test_stack();
test_queue();
test_priority_queue();
return 0;
}

View File

@@ -20,5 +20,6 @@ void test_unordered_multiset(void);
void test_unordered_multimap(void);
void test_stack(void);
void test_queue(void);
void test_priority_queue(void);
#endif /* CONTAINERS_TEST_H */