/** @license 2020 Neil Edelman, distributed under the terms of the [MIT License](https://opensource.org/licenses/MIT). @abstract Source , depends on ; examples . @subtitle Priority-queue ![Example of heap.](../web/heap.png) A heap> is a binary heap, proposed by using terminology of . It can be used as an implementation of a priority queue; internally, it is a `<node>array` with implicit heap properties on priority> and an optional value> pointer value. @param[HEAP_NAME, HEAP_TYPE] `` that satisfies `C` naming conventions when mangled and an assignable type priority> associated therewith. `HEAP_NAME` is required; `HEAP_TYPE` defaults to `unsigned int`. `` is private, whose names are prefixed in a manner to avoid collisions. @param[HEAP_COMPARE] A function satisfying compare_fn>. Defaults to minimum-hash. Required if `HEAP_TYPE` is changed to an incomparable type. @param[HEAP_VALUE] Optional value value>, that is stored as a reference in heapnode>; declaring it is sufficient. If set, has no effect on the ranking, but affects value>, (otherwise, it's the same field as priority>.) @param[HEAP_EXPECT_TRAIT] Do not un-define certain variables for subsequent inclusion in a parameterized trait. @param[HEAP_TO_STRING_NAME, HEAP_TO_STRING] To string trait contained in ; an optional unique `` that satisfies `C` naming conventions when mangled and function implementing to_string_fn>. @depend [array](https://github.com/neil-edelman/array) @std C89 @fixme Add decrease priority. @fixme Add replace. @fixme `HEAP_VALUE` has to be a pointer; use `memcpy` instead. */ #ifndef HEAP_NAME #error Generic HEAP_NAME undefined. #endif #if defined(HEAP_TO_STRING_NAME) || defined(HEAP_TO_STRING) /* */ #define HEAP_TRAITS HEAP_TO_STRING_TRAIT #if HEAP_TRAITS > 1 #error Only one trait per include is allowed; use HEAP_EXPECT_TRAIT. #endif #if defined(HEAP_TO_STRING_NAME) && !defined(HEAP_TO_STRING) #error HEAP_TO_STRING_NAME requires HEAP_TO_STRING. #endif #ifndef HEAP_H /* */ #if HEAP_TRAITS == 0 /* */ /* Check that `HEAP_COMPARE` is a function implementing compare_fn>, if defined. */ static const PH_(compare_fn) PH_(compare) = (HEAP_COMPARE); #ifdef HEAP_VALUE /* */ /* This relies on `array.h` which must be in the same directory. */ #define ARRAY_NAME PH_(node) #define ARRAY_TYPE PH_(node) #include "array.h" /** Stores the heap as an implicit binary tree in an array called `a`. To initialize it to an idle state, see heap>, `HEAP_IDLE`, `{0}` (`C99`), or being `static`. ![States.](../web/states.png) */ struct H_(heap) { struct PH_(node_array) a; }; /** Extracts the priority> of `node`, which must not be null. */ static PH_(priority) PH_(get_priority)(const PH_(node) *const node) { #ifdef HEAP_VALUE return node->priority; #else return *node; #endif } /** Extracts the value> of `node`, which must not be null. */ static PH_(value) PH_(get_value)(const PH_(node) *const node) { #ifdef HEAP_VALUE /* <-- value */ return node->value; #else /* value --> */ } /** Copies `src` to `dest`. */ static void PH_(copy)(const PH_(node) *const src, PH_(node) *const dest) { #ifdef HEAP_VALUE /* */ } /** Find the spot in `heap` where `node` goes and put it there. @param[heap] At least one entry; the last entry will be replaced by `node`. @order \O(log `size`) */ static void PH_(sift_up)(struct H_(heap) *const heap, PH_(node) *const node) { PH_(node) *const n0 = heap->a.data; PH_(priority) p = PH_(get_priority)(node); size_t i = heap->a.size - 1; assert(heap && heap->a.size && node); if(i) { size_t i_up; do { /* Note: don't change the `<=`; it's a queue. */ i_up = (i - 1) >> 1; if(PH_(compare)(PH_(get_priority)(n0 + i_up), p) <= 0) break; PH_(copy)(n0 + i_up, n0 + i); } while((i = i_up)); } PH_(copy)(node, n0 + i); } /** Pop the head of `heap` and restore the heap by sifting down the last element. @param[heap] At least one entry. The head is popped, and the size will be one less. */ static void PH_(sift_down)(struct H_(heap) *const heap) { const size_t size = (assert(heap && heap->a.size), --heap->a.size), half = size >> 1; size_t i = 0, c; PH_(node) *const n0 = heap->a.data, *const down = n0 + size /* Put it at the top. */, *child; const PH_(priority) down_p = PH_(get_priority)(down); while(i < half) { c = (i << 1) + 1; if(c + 1 < size && PH_(compare)(PH_(get_priority)(n0 + c), PH_(get_priority)(n0 + c + 1)) > 0) c++; child = n0 + c; if(PH_(compare)(down_p, PH_(get_priority)(child)) <= 0) break; PH_(copy)(child, n0 + i); i = c; } PH_(copy)(down, n0 + i); } /** Restore the `heap` by permuting the elements so `i` is in the proper place. This reads from the an arbitrary leaf-node into a temporary value, so is slightly more complex than sift_down>, but the same thing. @param[heap] At least `i + 1` entries. */ static void PH_(sift_down_i)(struct H_(heap) *const heap, size_t i) { const size_t size = (assert(heap && i < heap->a.size), heap->a.size), half = size >> 1; size_t c; PH_(node) *const n0 = heap->a.data, *child, temp; int temp_valid = 0; while(i < half) { c = (i << 1) + 1; if(c + 1 < size && PH_(compare)(PH_(get_priority)(n0 + c), PH_(get_priority)(n0 + c + 1)) > 0) c++; child = n0 + c; if(temp_valid) { if(PH_(compare)(PH_(get_priority)(&temp), PH_(get_priority)(child)) <= 0) break; } else { /* Only happens on the first compare when `i` is in it's original position. */ if(PH_(compare)(PH_(get_priority)(n0 + i), PH_(get_priority)(child)) <= 0) break; PH_(copy)(n0 + i, &temp), temp_valid = 1; } PH_(copy)(child, n0 + i); i = c; } if(temp_valid) PH_(copy)(&temp, n0 + i); } /** Create a `heap` from an array. @order \O(`heap.size`) */ static void PH_(heapify)(struct H_(heap) *const heap) { size_t i; assert(heap); if(heap->a.size > 1) for(i = heap->a.size / 2 - 1; (PH_(sift_down_i)(heap, i), i); i--); } /** Removes from `heap`. Must have a non-zero size. */ static PH_(node) PH_(remove)(struct H_(heap) *const heap) { const PH_(node) result = *heap->a.data; assert(heap); if(heap->a.size > 1) { PH_(sift_down)(heap); } else { assert(heap->a.size == 1); heap->a.size = 0; } return result; } /** Initializes `heap` to be idle. @order \Theta(1) @allow */ static void H_(heap)(struct H_(heap) *const heap) { assert(heap), PH_(node_array)(&heap->a); } /** Returns `heap` to the idle state where it takes no dynamic memory. @order \Theta(1) @allow */ static void H_(heap_)(struct H_(heap) *const heap) { assert(heap), PH_(node_array_)(&heap->a); } /** Sets `heap` to be empty. That is, the size of `heap` will be zero, but if it was previously in an active non-idle state, it continues to be. @param[heap] If null, does nothing. @order \Theta(1) @allow */ static void H_(heap_clear)(struct H_(heap) *const heap) { assert(heap), PH_(node_array_clear)(&heap->a); } /** If the `heap` requires differentiation between empty and zero. (One may access it directly at `!heap.a.size`.) @return If the heap is empty. @allow */ static int H_(heap_is_empty)(const struct H_(heap) *const heap) { return assert(heap), !heap->a.size; } /** Copies `node` into `heap`. @return Success. @throws[ERANGE, realloc] @order \O(log `heap.size`) @allow */ static int H_(heap_add)(struct H_(heap) *const heap, PH_(node) node) { assert(heap); return PH_(node_array_new)(&heap->a) && (PH_(sift_up)(heap, &node), 1); } /** @return The lowest element in `heap` according to `HEAP_COMPARE` or null/zero if the heap is empty. On some heaps, one may have to call heap_is_empty> in order to differentiate. @order \O(1) @allow */ static PH_(value) H_(heap_peek)(const struct H_(heap) *const heap) { return assert(heap), heap->a.size ? PH_(get_value)(heap->a.data) : 0; } /** Remove the lowest element in `heap` according to `HEAP_COMPARE`. @return The same as heap_peek>. @order \O(log `size`) @allow */ static PH_(value) H_(heap_pop)(struct H_(heap) *const heap) { PH_(node) n; return assert(heap), heap->a.size ? (n = PH_(remove)(heap), PH_(get_value)(&n)) : 0; } /** The capacity of `heap` will be increased to at least `n` elements beyond the size. Invalidates pointers in `heap.a`. All the elements in `heap.a.size` are part of the heap, but `heap.a.size` <= `index` < `heap.a.capacity` can be used to construct new elements without immediately making them part of the heap, then heap_append>. @return The start of the buffered space. If `a` is idle and `buffer` is zero, a null pointer is returned, otherwise null indicates an error. @throws[realloc, ERANGE] @allow */ static PH_(node) *H_(heap_buffer)(struct H_(heap) *const heap, const size_t n) { return PH_(node_array_buffer)(&heap->a, n); } /** Adds and heapifies `n` elements to `heap`. Uses to sift-down all the internal nodes of heap. The heap elements must exist, see heap_buffer>. @param[n] If zero, returns true without heapifying. @return Success. @order \O(`heap.size` + `n`) @allow */ static void H_(heap_append)(struct H_(heap) *const heap, const size_t n) { PH_(node) *more; /* In practice, pushing uninitialized elements onto the heap does not make sense, so we assert that the elements exist first. */ assert(heap && n <= heap->a.capacity - heap->a.size); more = PH_(node_array_append)(&heap->a, n), assert(more); if(n) PH_(heapify)(heap); } /** Shallow-copies and heapifies `master` into `heap`. @param[master] If null, does nothing. @return Success. @order \O(`heap.size` + `copy.size`) @throws[ERANGE, realloc] @allow */ static int H_(heap_affix)(struct H_(heap) *const heap, const struct H_(heap) *const master) { PH_(node) *n; assert(heap); if(!master || !master->a.size) return 1; assert(master->a.data); if(!(n = PH_(node_array_buffer)(&heap->a, master->a.size))) return 0; memcpy(n, master->a.data, sizeof *n * master->a.size); n = PH_(node_array_append)(&heap->a, master->a.size), assert(n); PH_(heapify)(heap); return 1; } /* */ /* Define these for traits. */ #define BOX_ PH_ #define BOX_CONTAINER struct H_(heap) #define BOX_CONTENTS PH_(node) #ifdef HEAP_TEST /* */ static void PH_(unused_base_coda)(void); static void PH_(unused_base)(void) { PH_(node) unused; memset(&unused, 0, sizeof unused); H_(heap)(0); H_(heap_)(0); H_(heap_clear)(0); H_(heap_is_empty)(0); H_(heap_add)(0, unused); H_(heap_peek)(0); H_(heap_pop)(0); H_(heap_buffer)(0, 0); H_(heap_append)(0, 0); H_(heap_affix)(0, 0); PH_(begin)(0, 0); PH_(next)(0); PH_(unused_base_coda)(); } static void PH_(unused_base_coda)(void) { PH_(unused_base)(); } #elif defined(HEAP_TO_STRING) /* base code --> */ #define TSZ_(n) HEAP_CAT(heap_sz, SZ_(n)) #ifdef HEAP_VALUE /* */ #include "to_string.h" /** \include */ #ifdef HEAP_TEST /* */ #undef TSZ_ #undef SZ_ #undef HEAP_TO_STRING #ifdef HEAP_TO_STRING_NAME #undef HEAP_TO_STRING_NAME #endif static void PH_(unused_to_string_coda)(void); static void PH_(unused_to_string)(void) { H_(heap_to_string)(0); PH_(unused_to_string_coda)(); } static void PH_(unused_to_string_coda)(void) { PH_(unused_to_string)(); } #endif /* traits --> */ #ifdef HEAP_EXPECT_TRAIT /* */ #endif /* !trait --> */ #undef HEAP_TO_STRING_TRAIT #undef HEAP_TRAITS