#ifndef _heap_h__
#define _heap_h__

/** Heap **
 * 
 * Binary Max-Heap using a dynamically allocated array and ordering elements according
 * to a given comparison function.
 * 
 * Can be used as a priority queue.
 * 
 * Ordering of elements is established using a comparison_fn (see `comparison` module).
 * This is a Max-Heap, meaning that the greatest element is at the top.
 * 
 * This structure distinguishes between capacity, i.e. the length of its underlying array,
 * and size, i.e. the used portion of capacity. It's always true that size <= capacity.
 * 
 * To use a Heap on the heap (no pun intended) :
 * 
 *   Heap *h = heap_new(COMPARATOR);
 *   heap_grow(h, CAPACITY);  // optional
 *   ...
 *   heap_delete(h);
 * 
 * To use a Heap on the stack:
 * 
 *   Heap h;
 *   heap_init(&h, COMPARATOR);
 *   heap_grow(&h, CAPACITY);  // optional
 *   ...
 *   heap_clear(&h);
 * 
 * Iteration is made convenient by macros:
 * 
 *   // Heap *h;
 *   HEAP_FOR(h) {
 *      MyType *element = HEAP_CUR(MyType);
 *   }
 * 
 */

#include <stddef.h>

#include "comparison.h"

#ifdef __cplusplus
extern "C" {
#endif


/**
 * Default Heap capacity (maximum size)
 */
#define HEAP_DEFAULT_CAPACITY 16

/**
 * Binary max-heap using a dynamically allocated array and ordering elements according
 * to a given comparison function.
 *
 * Struct members:
 * 
 *   elements: heap-allocated array of elements (each element is a pointer)
 *   capacity: length of array
 *   size:     number of elements
 *   compare:  comparison function imposing an order on elements
 */
typedef struct Heap {

    void **elements;
    size_t capacity;
    size_t size;
    comparison_fn compare;

} Heap;

/**
 * Create a new Heap on the heap (no pun intended).
 * It must be deleted later with heap_delete().
 * 
 * The newly created Heap is empty, i.e. both capacity and size are zero.
 * Use heap_grow() to increase capacity.
 * 
 * Return: pointer to the newly allocated Heap
 */
Heap *heap_new(comparison_fn compare);

/**
 * Delete a Heap previously created with heap_new().
 */
void heap_delete(Heap *h);

/**
 * Initialize a Heap declared on the stack. It must be cleared later with heap_clear().
 * 
 * After initialization, the Heap is empty, i.e. both capacity and size are zero.
 * Use heap_grow() to increase capacity.
 */
void heap_init(Heap *h, comparison_fn compare);

/**
 * Clear a Heap, i.e. set capacity and size to zero, and free the underlying array.
 * 
 * This function is intended to be called when the Heap is no longer needed, since it
 * frees the internal array.
 * 
 * The Heap itself is not freed can be re-used immediately. It is not necessary to call
 * heap_init() after heap_clear().
 * 
 * To remove all elements from the Heap, it is more efficient to call heap_remove_all()
 * instead, as it will keep the existing array.
 * 
 * It is not necessary to call heap_clear() before heap_delete().
 */
void heap_clear(Heap *h);

/**
 * Copy the given array into a Heap. The source array is untouched, but in the Heap itself,
 * elements are re-ordered appropriately.
 * 
 * The Heap must have been properly initialized, either with heap_new() or heap_init(),
 * before this function can be called.
 * 
 * Any existing elements in the Heap are discarded before copying.
 * 
 * After calling this function, size == n.
 */
void heap_build(Heap *h, const void **array, size_t n);

/**
 * Return the size of a Heap.
 */
#define heap_size(h)  ((h)->size)

/**
 * Set the capacity of a Heap to n bytes.
 * The internal array is (re-)allocated accordingly.
 * 
 * If n == 0, HEAP_DEFAULT_CAPACITY is used instead.
 */
void heap_grow(Heap *h, size_t n);

/**
 * Insert an element into the Heap. If the Heap is full, it grows automatically.
 * 
 * The element must be compatible with the Heap's comparison function.
 * 
 * Complexity: O(log(size)), or O(size) if growth is performed.
 */
void heap_insert(Heap *h, void *element);

/**
 * Return the root element of the Heap, or NULL if the Heap is empty.
 * The Heap is not modified.
 * 
 * Complexity: O(1)
 */
#define heap_peek(h)  ((h)->size ? elements[0] : NULL)

/**
 * Remove the root element of the Heap and return it, or return NULL if the Heap is empty.
 * 
 * Complexity: O(log(size))
 */
void *heap_pop(Heap *h);

/**
 * Update the position of an arbitrary element within the Heap.
 * 
 * This function should be called whenever the element changes in a way that its rank would
 * be affected. For example, if elements are of a struct type, and the comparison function
 * operates on members of that struct type, then modifying the members of an element would
 * require updating the Heap, since the element's rank might now be different.
 * 
 * If the elements of the Heap aren't structs, or if they otherwise remain constant, it is
 * never necessary to call this function.
 * 
 * This function makes sense when the Heap is being used as a priority queue containing
 * elements with variable priority.
 * 
 * To update the root element, use the more efficient heap_update_root() instead.
 * 
 * Complexity: O(n)
 */
void heap_update(Heap *h, void *element);

/**
 * Update the position of the root.
 * 
 * This function should be called whenever the root element changes in a way that its rank
 * would be affected. For example, if elements are of a struct type, and the comparison
 * function operates on members of that struct type, then modifying the members of the root
 * element would require updating the Heap, since the element's rank might now be different,
 * and might not be the root anymore.
 * 
 * If the elements of the Heap aren't structs, or if they otherwise remain constant, it is
 * never necessary to call this function.
 * 
 * This function makes sense when the Heap is being used as a priority queue containing
 * elements with variable priority.
 * 
 * To update an arbitrary element that isn't the root, use heap_update() instead.
 * 
 * Complexity: O(log(size))
 * 
 */
void heap_update_root(Heap *h);

/**
 * Remove all elements from the Heap. Capacity is retained.
 * 
 * Complexity: O(1)
 */
#define heap_remove_all(h)  { (h)->size = 0; }

/**
 * Macro for convenient iteration in a for-loop way.
 * 
 *   // Heap *h;
 *   HEAP_FOR(h) {
 *      MyType *element = HEAP_CUR(MyType);
 *   }
 */
#define HEAP_FOR(h) for (void **__a = (h)->elements; __a < (h)->elements + (h)->size; __a++)

/**
 * Macro for convenient iteration in a for-loop way.
 * 
 *   // Heap *h;
 *   HEAP_FOR(h) {
 *      MyType *element = HEAP_CUR(MyType);
 *   }
 */
#define HEAP_CUR(type) ((type *)(*__a))

/**
 * Sort an array in-place using a given comparison function.
 * Elements are re-arranged in ascending order, i.e. from smallest to largest.
 * 
 * This is the well-known Heapsort algorithm.
 * 
 * Arguments:
 * 
 *   array   : array of elements (each element is a pointer)
 *   n       : length of the array
 *   compare : comparison function imposing an order on elements
 * 
 * Complexity: O(n*log(n)) assuming the comparison function has O(1) complexity
 */
void heap_sort(void **array, size_t n, comparison_fn compare);


#ifdef __cplusplus
}
#endif

#endif
