#include "heap.h" #include "../core/memory.h" typedef struct BzHeapHead { i32 capacity; i32 size; i32 stride; i32 weightOffset; } BzHeapHead; #define HEAP_HEAD(heap) (((BzHeapHead *)heap) - 1) #define HEAP_LEFT(i) ((i32)(i * 2 + 1)) #define HEAP_RIGHT(i) ((i32)(i * 2 + 2)) #define HEAP_PARENT(i) ((i32) ((i - 1) / 2)) static void heapSiftUp(BzHeapHead *head, void *heap, i32 idx); static void heapSiftDown(BzHeapHead *head, void *heap, i32 idx); void *_bzHeapCreate(i32 startCapacity, i32 stride, i32 weightOffset) { i32 numBytes = sizeof(BzHeapHead) + (startCapacity + 1) * stride; BzHeapHead *heap = bzAlloc(numBytes); heap[0] = (BzHeapHead) { .capacity=startCapacity, .size=0, .stride=stride, .weightOffset=weightOffset }; return &heap[1]; } void _bzHeapDestroy(void *heap) { bzFree(HEAP_HEAD(heap)); } void _bzHeapClear(void *heap) { BzHeapHead *head = HEAP_HEAD(heap); head->size = 0; } i32 _bzHeapSize(void *heap) { return HEAP_HEAD(heap)->size; } bool _bzHeapIsEmpty(void *heap) { return HEAP_HEAD(heap)->size == 0; } i32 _bzHeapPop(void *heap) { BzHeapHead *head = HEAP_HEAD(heap); BZ_ASSERT(head->size > 0); // Move first to index capacity (for output) bzMemMove(((u8 *)heap) + head->capacity * head->stride, heap, head->stride); head->size--; if (head->size == 0) return 0; // Move last to index 0 bzMemMove(heap, ((u8 *) heap) + head->size * head->stride, head->stride); heapSiftDown(head, heap, 0); return head->capacity; } void _bzHeapPush(void *heap) { BzHeapHead *head = HEAP_HEAD(heap); BZ_ASSERT(head->size < head->capacity); void *item = ((u8 *)heap) + head->size * head->stride; bzMemMove(((u8 *)heap) + head->size * head->stride, item, head->stride); head->size++; heapSiftUp(head, heap, head->size - 1); } i32 _bzHeapPushIdx(void *heap) { BzHeapHead *head = HEAP_HEAD(heap); BZ_ASSERT(head->size < head->capacity); return head->size; } void _bzHeapUpdate(void *heap, i32 idx) { BzHeapHead *head = HEAP_HEAD(heap); BZ_ASSERT(idx >= 0 && idx < head->size); heapSiftUp(head, heap, idx); heapSiftDown(head, heap, idx); } static void heapSwap(BzHeapHead *head, void *heap, i32 aIdx, i32 bIdx) { u8 *aItem = ((u8 *)heap) + aIdx * head->stride; u8 *bItem = ((u8 *)heap) + bIdx * head->stride; u8 tmp[head->stride]; bzMemMove(tmp, aItem, head->stride); bzMemMove(aItem, bItem, head->stride); bzMemMove(bItem, tmp, head->stride); } static int heapCmp(BzHeapHead *head, void *heap, i32 lhs, i32 rhs) { int *aWeight = (i32 *) (((u8 *)heap) + lhs * head->stride + head->weightOffset); int *bWeight = (i32 *) (((u8 *)heap) + rhs * head->stride + head->weightOffset); return (*aWeight) - (*bWeight); } static void heapSiftUp(BzHeapHead *head, void *heap, i32 idx) { while (idx >= 0) { i32 parent = HEAP_PARENT(idx); if (heapCmp(head, heap, idx, parent) >= 0) break; heapSwap(head, heap, idx, parent); idx = parent; } } static void heapSiftDown(BzHeapHead *head, void *heap, i32 idx) { while (idx < head->size) { i32 l = HEAP_LEFT(idx); i32 r = HEAP_RIGHT(idx); i32 smallest = idx; if (l < head->size && heapCmp(head, heap, l, idx) < 0) smallest = l; if (r < head->size && heapCmp(head, heap, r, smallest) < 0) smallest = r; if (smallest == idx) break; heapSwap(head, heap, idx, smallest); idx = smallest; } }