diff --git a/src/priority_queue.c b/src/priority_queue.c new file mode 100644 index 0000000..a482f06 --- /dev/null +++ b/src/priority_queue.c @@ -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 +#include +#include +#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; +} diff --git a/src/priority_queue.h b/src/priority_queue.h new file mode 100644 index 0000000..c3b6ef3 --- /dev/null +++ b/src/priority_queue.h @@ -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 + +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 */ diff --git a/src/queue.c b/src/queue.c index 14410ae..71ae9c8 100644 --- a/src/queue.c +++ b/src/queue.c @@ -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. * diff --git a/src/stack.c b/src/stack.c index 36eea03..0a302cc 100644 --- a/src/stack.c +++ b/src/stack.c @@ -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. * diff --git a/tst/priority_queue.c b/tst/priority_queue.c new file mode 100644 index 0000000..d1c7bab --- /dev/null +++ b/tst/priority_queue.c @@ -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); +} diff --git a/tst/test.c b/tst/test.c index c0d7ecf..937469b 100644 --- a/tst/test.c +++ b/tst/test.c @@ -17,5 +17,6 @@ int main(void) test_unordered_multimap(); test_stack(); test_queue(); + test_priority_queue(); return 0; } diff --git a/tst/test.h b/tst/test.h index 99add38..dbef271 100644 --- a/tst/test.h +++ b/tst/test.h @@ -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 */