diff --git a/rmutil/Makefile b/rmutil/Makefile index f8ddb63..b3bb712 100644 --- a/rmutil/Makefile +++ b/rmutil/Makefile @@ -6,7 +6,7 @@ endif CFLAGS = -g -fPIC -lc -lm -O3 -std=gnu99 -I$(RM_INCLUDE_DIR) -Wall -Wno-unused-function CC=gcc -OBJS=util.o strings.o sds.o vector.o +OBJS=util.o strings.o sds.o vector.o heap.o priority_queue.o all: librmutil.a @@ -20,4 +20,10 @@ test_vector: test_vector.o vector.o $(CC) -Wall -o test_vector vector.o test_vector.o -lc -O0 @(sh -c ./test_vector) - +test_heap: test_heap.o heap.o vector.o + $(CC) -Wall -o test_heap heap.o vector.o test_heap.o -lc -O0 + @(sh -c ./test_heap) + +test_priority_queue: test_priority_queue.o priority_queue.o heap.o vector.o + $(CC) -Wall -o test_priority_queue priority_queue.o heap.o vector.o test_priority_queue.o -lc -O0 + @(sh -c ./test_heap) diff --git a/rmutil/heap.c b/rmutil/heap.c new file mode 100644 index 0000000..fd1fffb --- /dev/null +++ b/rmutil/heap.c @@ -0,0 +1,107 @@ +#include "heap.h" + +/* Byte-wise swap two items of size SIZE. */ +#define SWAP(a, b, size) \ + do \ + { \ + register size_t __size = (size); \ + register char *__a = (a), *__b = (b); \ + do \ + { \ + char __tmp = *__a; \ + *__a++ = *__b; \ + *__b++ = __tmp; \ + } while (--__size > 0); \ + } while (0) + +inline char *__vector_GetPtr(Vector *v, size_t pos) { + return v->data + (pos * v->elemSize); +} + +void __sift_up(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)) { + size_t len = last - first; + if (len > 1) { + len = (len - 2) / 2; + size_t ptr = first + len; + if (cmp(__vector_GetPtr(v, ptr), __vector_GetPtr(v, --last)) < 0) { + char t[v->elemSize]; + memcpy(t, __vector_GetPtr(v, last), v->elemSize); + do { + memcpy(__vector_GetPtr(v, last), __vector_GetPtr(v, ptr), v->elemSize); + last = ptr; + if (len == 0) + break; + len = (len - 1) / 2; + ptr = first + len; + } while (cmp(__vector_GetPtr(v, ptr), t) < 0); + memcpy(__vector_GetPtr(v, last), t, v->elemSize); + } + } +} + +void __sift_down(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *), size_t start) { + // left-child of __start is at 2 * __start + 1 + // right-child of __start is at 2 * __start + 2 + size_t len = last - first; + size_t child = start - first; + + if (len < 2 || (len - 2) / 2 < child) + return; + + child = 2 * child + 1; + + if ((child + 1) < len && cmp(__vector_GetPtr(v, first + child), __vector_GetPtr(v, first + child + 1)) < 0) { + // right-child exists and is greater than left-child + ++child; + } + + // check if we are in heap-order + if (cmp(__vector_GetPtr(v, first + child), __vector_GetPtr(v, start)) < 0) + // we are, __start is larger than it's largest child + return; + + char top[v->elemSize]; + memcpy(top, __vector_GetPtr(v, start), v->elemSize); + do { + // we are not in heap-order, swap the parent with it's largest child + memcpy(__vector_GetPtr(v, start), __vector_GetPtr(v, first + child), v->elemSize); + start = first + child; + + if ((len - 2) / 2 < child) + break; + + // recompute the child based off of the updated parent + child = 2 * child + 1; + + if ((child + 1) < len && cmp(__vector_GetPtr(v, first + child), __vector_GetPtr(v, first + child + 1)) < 0) { + // right-child exists and is greater than left-child + ++child; + } + + // check if we are in heap-order + } while (cmp(__vector_GetPtr(v, first + child), top) >= 0); + memcpy(__vector_GetPtr(v, start), top, v->elemSize); +} + + +void Make_Heap(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)) { + if (last - first > 1) { + // start from the first parent, there is no need to consider children + for (int start = (last - first - 2) / 2; start >= 0; --start) { + __sift_down(v, first, last, cmp, first + start); + } + } +} + + +inline void Heap_Push(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)) { + __sift_up(v, first, last, cmp); +} + + +inline void Heap_Pop(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)) { + if (last - first > 1) { + SWAP(__vector_GetPtr(v, first), __vector_GetPtr(v, --last), v->elemSize); + __sift_down(v, first, last, cmp, first); + } +} diff --git a/rmutil/heap.h b/rmutil/heap.h new file mode 100644 index 0000000..b49efce --- /dev/null +++ b/rmutil/heap.h @@ -0,0 +1,38 @@ +#ifndef __HEAP_H__ +#define __HEAP_H__ + +#include "vector.h" + + +/* Make heap from range + * Rearranges the elements in the range [first,last) in such a way that they form a heap. + * A heap is a way to organize the elements of a range that allows for fast retrieval of the element with the highest + * value at any moment (with pop_heap), even repeatedly, while allowing for fast insertion of new elements (with + * push_heap). + * The element with the highest value is always pointed by first. The order of the other elements depends on the + * particular implementation, but it is consistent throughout all heap-related functions of this header. + * The elements are compared using cmp. + */ +void Make_Heap(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)); + + +/* Push element into heap range + * Given a heap in the range [first,last-1), this function extends the range considered a heap to [first,last) by + * placing the value in (last-1) into its corresponding location within it. + * A range can be organized into a heap by calling make_heap. After that, its heap properties are preserved if elements + * are added and removed from it using push_heap and pop_heap, respectively. + */ +void Heap_Push(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)); + + +/* Pop element from heap range + * Rearranges the elements in the heap range [first,last) in such a way that the part considered a heap is shortened + * by one: The element with the highest value is moved to (last-1). + * While the element with the highest value is moved from first to (last-1) (which now is out of the heap), the other + * elements are reorganized in such a way that the range [first,last-1) preserves the properties of a heap. + * A range can be organized into a heap by calling make_heap. After that, its heap properties are preserved if elements + * are added and removed from it using push_heap and pop_heap, respectively. + */ +void Heap_Pop(Vector *v, size_t first, size_t last, int (*cmp)(void *, void *)); + +#endif //__HEAP_H__ diff --git a/rmutil/priority_queue.c b/rmutil/priority_queue.c new file mode 100644 index 0000000..5c16c08 --- /dev/null +++ b/rmutil/priority_queue.c @@ -0,0 +1,36 @@ +#include "priority_queue.h" +#include "heap.h" + +PriorityQueue *__newPriorityQueueSize(size_t elemSize, size_t cap, int (*cmp)(void *, void *)) { + PriorityQueue *pq = malloc(sizeof(PriorityQueue)); + pq->v = __newVectorSize(elemSize, cap); + pq->cmp = cmp; + return pq; +} + +inline size_t Priority_Queue_Size(PriorityQueue *pq) { + return Vector_Size(pq->v); +} + +inline int Priority_Queue_Top(PriorityQueue *pq, void *ptr) { + return Vector_Get(pq->v, 0, ptr); +} + +inline size_t __priority_Queue_PushPtr(PriorityQueue *pq, void *elem) { + size_t top = __vector_PushPtr(pq->v, elem); + Heap_Push(pq->v, 0, top, pq->cmp); + return top; +} + +inline void Priority_Queue_Pop(PriorityQueue *pq) { + if (pq->v->top == 0) { + return; + } + Heap_Pop(pq->v, 0, pq->v->top, pq->cmp); + pq->v->top--; +} + +void Priority_Queue_Free(PriorityQueue *pq) { + Vector_Free(pq->v); + free(pq); +} diff --git a/rmutil/priority_queue.h b/rmutil/priority_queue.h new file mode 100644 index 0000000..89f8c73 --- /dev/null +++ b/rmutil/priority_queue.h @@ -0,0 +1,55 @@ +#ifndef __PRIORITY_QUEUE_H__ +#define __PRIORITY_QUEUE_H__ + +#include "vector.h" + +/* Priority queue + * Priority queues are designed such that its first element is always the greatest of the elements it contains. + * This context is similar to a heap, where elements can be inserted at any moment, and only the max heap element can be + * retrieved (the one at the top in the priority queue). + * Priority queues are implemented as Vectors. Elements are popped from the "back" of Vector, which is known as the top + * of the priority queue. + */ +typedef struct { + Vector *v; + + int (*cmp)(void *, void *); +} PriorityQueue; + +/* Construct priority queue + * Constructs a priority_queue container adaptor object. + */ +PriorityQueue *__newPriorityQueueSize(size_t elemSize, size_t cap, int (*cmp)(void *, void *)); + +#define NewPriorityQueue(type, cap, cmp) __newPriorityQueueSize(sizeof(type), cap, cmp) + +/* Return size + * Returns the number of elements in the priority_queue. + */ +size_t Priority_Queue_Size(PriorityQueue *pq); + +/* Access top element + * Copy the top element in the priority_queue to ptr. + * The top element is the element that compares higher in the priority_queue. + */ +int Priority_Queue_Top(PriorityQueue *pq, void *ptr); + +/* Insert element + * Inserts a new element in the priority_queue. + */ +size_t __priority_Queue_PushPtr(PriorityQueue *pq, void *elem); + +#define Priority_Queue_Push(pq, elem) __priority_Queue_PushPtr(pq, &(typeof(elem)){elem}) + +/* Remove top element + * Removes the element on top of the priority_queue, effectively reducing its size by one. The element removed is the + * one with the highest value. + * The value of this element can be retrieved before being popped by calling Priority_Queue_Top. + */ +void Priority_Queue_Pop(PriorityQueue *pq); + +/* free the priority queue and the underlying data. Does not release its elements if + * they are pointers */ +void Priority_Queue_Free(PriorityQueue *pq); + +#endif //__PRIORITY_QUEUE_H__ diff --git a/rmutil/test_heap.c b/rmutil/test_heap.c new file mode 100644 index 0000000..515400f --- /dev/null +++ b/rmutil/test_heap.c @@ -0,0 +1,38 @@ +#include +#include "heap.h" +#include "assert.h" + +int cmp(void *a, void *b) { + int *__a = (int *) a; + int *__b = (int *) b; + return *__a - *__b; +} + +int main(int argc, char **argv) { + int myints[] = {10, 20, 30, 5, 15}; + Vector *v = NewVector(int, 5); + for (int i = 0; i < 5; i++) { + Vector_Push(v, myints[i]); + } + + Make_Heap(v, 0, v->top, cmp); + + int n; + Vector_Get(v, 0, &n); + assert(30 == n); + + Heap_Pop(v, 0, v->top, cmp); + v->top = 4; + Vector_Get(v, 0, &n); + assert(20 == n); + + Vector_Push(v, 99); + Heap_Push(v, 0, v->top, cmp); + Vector_Get(v, 0, &n); + assert(99 == n); + + Vector_Free(v); + printf("PASS!"); + return 0; +} + diff --git a/rmutil/test_priority_queue.c b/rmutil/test_priority_queue.c new file mode 100644 index 0000000..ce8faa7 --- /dev/null +++ b/rmutil/test_priority_queue.c @@ -0,0 +1,37 @@ +#include +#include "assert.h" +#include "priority_queue.h" + +int cmp(void* i1, void* i2) { + int *__i1 = (int*) i1; + int *__i2 = (int*) i2; + return *__i1 - *__i2; +} + +int main(int argc, char **argv) { + PriorityQueue *pq = NewPriorityQueue(int, 10, cmp); + assert(0 == Priority_Queue_Size(pq)); + + for (int i = 0; i < 5; i++) { + Priority_Queue_Push(pq, i); + } + assert(5 == Priority_Queue_Size(pq)); + + Priority_Queue_Pop(pq); + assert(4 == Priority_Queue_Size(pq)); + + Priority_Queue_Push(pq, 10); + Priority_Queue_Push(pq, 20); + Priority_Queue_Push(pq, 15); + int n; + Priority_Queue_Top(pq, &n); + assert(20 == n); + + Priority_Queue_Pop(pq); + Priority_Queue_Top(pq, &n); + assert(15 == n); + + Priority_Queue_Free(pq); + printf("PASS!"); + return 0; +} \ No newline at end of file