/*
 * Copyright (c) 2009-2015 ZIH, TU Dresden, Federal Republic of Germany. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *	- Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *	- Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * 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.
 *
 */

/*
 * Abstract:
 *	Declaration of a d-ary heap. The caller must provide
 *	key/context and must provide a callback function to update
 *	the index of heap elements. Additionally, the caller can
 *	provide a compare function in case that the minimum d-ary heap
 *	based on uint64_t keys is not sufficient enough. The
 *	heap allocates internal structures and can be resized,
 *	which will not relocate or change existing elements.
 */

#ifndef _CL_HEAP_H_
#define _CL_HEAP_H_

#include <complib/cl_types.h>

#ifdef __cplusplus
#define BEGIN_C_DECLS extern "C" {
#define END_C_DECLS   }
#else				/* !__cplusplus */
#define BEGIN_C_DECLS
#define END_C_DECLS
#endif				/* __cplusplus */

BEGIN_C_DECLS
/****h* Component Library/d-ary (Min-)Heap
* NAME
*	Heap
*
* DESCRIPTION
*	The d-ary heap is stored implicitly in an array and parent/child nodes
*	are accessed via index calculations rather than using allocated
*	elements and pointer structures. It has been proven that implicit 4-ary
*	heaps are in many use cases the most efficient implementation (e.g.,
*	see "A Back-to-Basics Empirical Study of Priority Queues" by
*	Larkin et al.).
*
*	The heap has to be initialized to a certain size by the caller,
*	however a function to resize the heap is available. The resize function
*	will not delete or relocate existing elements in the heap.
*
*	Typical heap operations, such as insert, extract_[min | max],
*	decrease_key, and delete, are provided and referencing an element,
*	e.g., for deletion is done via indices. The implication is, that
*	the caller needs to be informed about index changes, e.g., after
*	internal reordering through shift_[up | down], to always know the
*	index of each element in the heap. Therefore, the caller has to
*	provide a callback function, which forwards the context of an element
*	and the new index in the heap back to the caller. The caller is
*	responsible for updating its internal sturctures/indices accordingly.
*
*	An implementation of heapify is omitted, because the caller should not
*	be able to allocate and provide an unsorted array. All heapify
*	operations with shift_up and shift_down are done internally after the
*	caller manipulated the heap with the provided functions.
*
*	Heaps are used extensively in some routing functions, such as [DF]SSSP
*	routing. Therefore, the [DF]SSSP implementation can be used as a
*	prototype to adapt and use the d-ary heap in other parts of OpenSM.
*
*	The cl_heap_t structure should be treated as opaque and should be
*	manipulated only through the provided functions.
*
* SEE ALSO
*	Structures:
*		cl_heap_t
*
*	Callbacks:
*		cl_pfn_heap_apply_index_update_t, cl_pfn_heap_compare_keys_t
*
*	Initialization:
*		cl_heap_construct, cl_heap_init, cl_heap_destroy
*
*	Manipulation:
*		cl_heap_insert, cl_heap_delete, cl_heap_extract_root,
*		cl_heap_modify_key, cl_heap_resize
*
*	Attributes:
*		cl_heap_get_capacity, cl_heap_get_size, cl_heap_is_empty
*		cl_is_stored_in_heap, cl_is_heap_inited
*********/
/****d* Component Library: Heap/cl_pfn_heap_apply_index_update_t
* NAME
*	cl_pfn_heap_apply_index_update_t
*
* DESCRIPTION
*	The cl_pfn_heap_apply_index_update_t function type defines the prototype
*	to update the heap index, position of the element, in the user supplied
*	context. The index is the only information the user needs to store
*	somewhere in his/her structures and is essential for all munipulations
*	operations on the heap (except resize), since these will change the
*	position of elements through heap_up/heap_down.
*
* SYNOPSIS
*/
typedef void
 (*cl_pfn_heap_apply_index_update_t) (IN const void *context,
					IN const size_t new_index);
/*
* PARAMETERS
*	context
*		[in] Pointer to the user supplied context which is associated
*		with a heap element.
*
*	new_index
*		[in] The new index in the heap, i.e., position in the
*		element_array of cl_heap_t.
*
* RETURN VALUES
*	This callback function should not return any value.
*
* NOTES
*	The function is necessary to update the indices for the caller,
*	since the caller MUST keep track of the position of heap elements
*	to make changes, such as decrease_key or delete. For a working
*	reference implementation on how to define and handle this callback,
*	please refer to the [DF]SSSP routing in opensm/osm_ucast_dfsssp.c.
*
* SEE ALSO
*	Heap, cl_heap_modify_key, cl_heap_insert, cl_heap_delete,
*	cl_heap_extract_root
*********/

/****d* Component Library: Heap/cl_pfn_heap_compare_keys_t
* NAME
*	cl_pfn_heap_heap_compare_keys_t
*
* DESCRIPTION
*	The cl_pfn_heap_heap_compare_keys_t function type defines the prototype
*	to compare the keys of two heap elements.
*
* SYNOPSIS
*/
typedef int
 (*cl_pfn_heap_compare_keys_t) (IN const void *p_key_1, IN const void *p_key_2);
/*
* PARAMETERS
*	p_key_1
*		[in] Pointer to the first key.
*
*	p_key_2
*		[in] Pointer to the second key.
*
* RETURN VALUES
*	The function should return an integer less than, equal to, or greater
*	than zero indicating that key1 is "smaller", "equal" or "greater" than
*	key2.
*
* NOTES
*	If user does not provide a compare function, then the default behavior
*	is to assume all keys are uint64_t and the heap is a minimum d-ary
*	heap, i.e., the smallest key is stored at the root node.
*
* SEE ALSO
*	Heap
*********/

/****s* Component Library: Heap/cl_heap_t
* NAME
*	cl_heap_t
*
* DESCRIPTION
*	Heap structure.
*
*	The cl_heap_t structure should be treated as opaque and should be
*	manipulated only through the provided functions..
*
* SYNOPSIS
*
*/
struct _cl_heap_elem;
typedef struct _cl_heap {
	cl_state_t state;
	uint8_t branching_factor;
	struct _cl_heap_elem *element_array;
	size_t size;
	size_t capacity;
	cl_pfn_heap_apply_index_update_t pfn_index_update;
	cl_pfn_heap_compare_keys_t pfn_compare;
} cl_heap_t;
/*
* FIELDS
*	state
*		State of the heap.
*
*	branching_factor
*		Branching factor d for the d-ary heap, i.e., number of children
*		per node.
*
*	element_array
*		Array of elements for the heap. Each element consists of a key
*		and user supplied context pointer, i.e., usually very compact
*		storage with 16 bytes per element.
*
*	size
*		Number of elements successfully inserted into the heap.
*
*	capacity
*		Total number of elements allocated.
*
*	pfn_index_update
*		User supplied function to update position indicies for
*		elements in the heap.
*
*	pfn_compare
*		User supplied comparison of element keys.
*
* SEE ALSO
*	Heap, cl_pfn_heap_apply_index_update_t, cl_pfn_heap_compare_keys_t
*********/

/****f* Component Library: Heap/cl_heap_construct
* NAME
*	cl_heap_construct
*
* DESCRIPTION
*	The cl_heap_construct function constructs a d-ary heap.
*	The result is a valid, but uninitialized, heap. The function
*	cl_heap_construct does not allocate any memory.
*
* SYNOPSIS
*/
void cl_heap_construct(IN cl_heap_t * const p_heap);
/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure to construct.
*
* RETURN VALUE
*	This function does not return a value.
*
* NOTES
*	Allows calling cl_heap_destroy without first calling cl_heap_init.
*	Calling cl_heap_construct is a prerequisite to calling any other
*	heap function except cl_heap_init. The caller must allocate the
*	memory for the heap, i.e., p_heap==NULL results in an exception.
*
* SEE ALSO
*	Heap, cl_heap_init, cl_heap_destroy
*********/

/****f* Component Library: Heap/cl_heap_init
* NAME
*	cl_heap_init
*
* DESCRIPTION
*	The cl_heap_init function initializes a d-ary heap for use.
*
* SYNOPSIS
*/
cl_status_t
cl_heap_init(IN cl_heap_t * const p_heap,
	    IN const size_t max_size,
	    IN const uint8_t branching_factor,
	    IN cl_pfn_heap_apply_index_update_t pfn_index_update,
	    IN cl_pfn_heap_compare_keys_t pfn_compare OPTIONAL);
/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure to inititalize.
*
*	max_size
*		[in] Total number of elements the heap should be able to store.
*
*	branching_factor
*		[in] Branching factor d for the d-ary heap, i.e., number of
*		children. For example, using d=2 yields in a binary heap...
*
*	pfn_index_update
*		[in] User supplied callback to inform the user about index
*		changes of individual elements stored in the heap.
*
*	pfn_compare
*		[in] User supplied callback to compare two keys. This function
*		pointer is optional, i.e., if pfn_compare=NULL, then an internal
*		compare function is used to create a min-heap.
*
* RETURN VALUES
*	CL_SUCCESS if the heap was initialized successfully.
*
*       CL_INVALID_PARAMETER if max_size or branching_factor are less than or
*       equal to zero, or if pfn_index_update is NULL.
*
*	CL_INSUFFICIENT_MEMORY if the initialization failed.
*
* NOTES
*	Can be called without calling cl_heap_construct first. Calling
*	cl_heap_init on an already initialized heap will result in an internal
*	call to cl_heap_destroy prior to the memory allocation of the
*	element_array.
*
* SEE ALSO
*	Heap, cl_pfn_heap_apply_index_update_t, cl_pfn_heap_compare_keys_t,
*	cl_heap_construct
*********/

/****f* Component Library: Heap/cl_heap_destroy
* NAME
*	cl_heap_destroy
*
* DESCRIPTION
*	The cl_heap_destroy function destroys the heap. The heap is afterwards
*	in the CL_UNINITIALIZED state.
*
* SYNOPSIS
*/
void cl_heap_destroy(IN cl_heap_t * const p_heap);
/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure to destroy.
*
* RETURN VALUE
*	This function does not return a value.
*
* NOTES
*	cl_heap_destroy frees all memory allocated for the heap. The heap
*	is left valid, but uninitialized, with a zero capacity and size, and
*	must be re-initialized by calling cl_heap_init for further usage.
*
*	This function should only be called after a call to cl_heap_construct
*	or cl_heap_init.
*
* SEE ALSO
*	Heap, cl_heap_construct, cl_heap_init
*********/

/****f* Component Library: Heap/cl_heap_modify_key
* NAME
*	cl_heap_modify_key
*
* DESCRIPTION
*	The cl_heap_modify_key function changes the key of an element in the
*	heap identified by an index. The result could be invalidate the heap
*	property for the stored elements. Therefore, the cl_heap_modify_key
*	function calls shift_[up | down] internally to reconstruct a valid
*	heap.
*
* SYNOPSIS
*/
cl_status_t cl_heap_modify_key(IN cl_heap_t * const p_heap,
				IN const uint64_t key, IN const size_t index);
/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure to modify.
*
*	key
*		[in] The new key for an existing heap element.
*
*	index
*		[in] Index of the heap elemnt in element_array, which should
*		be modified.
*
* RETURN VALUE
*	CL_SUCCESS if the key of a heap element was modified successfully.
*
*	CL_INVALID_PARAMETER if the user supplied index is out of bounds.
*
* NOTES
*	This function is similar to the common decrease_key for minimum heaps,
*	however the naming scheme is more generic to support maximum heaps
*	as well.
*
*	This function should only be called after a call to cl_heap_init.
*
* SEE ALSO
*	Heap, cl_heap_init
*********/

/****f* Component Library: Heap/cl_heap_insert
* NAME
*	cl_heap_insert
*
* DESCRIPTION
*	The cl_heap_insert function adds a new element to the existing heap
*	and restores the heap property afterwards.
*
* SYNOPSIS
*/
cl_status_t cl_heap_insert(IN cl_heap_t * const p_heap, IN const uint64_t key,
			  IN const void *const context);
/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure to modify.
*
*	key
*		[in] Initial key value to compare heap elements and reorder
*		the element_array according to the heap property.
*
*	context
*		[in] User supplied pointer to a context which "represents"
*		the heap element for the user.
*
* RETURN VALUE
*	CL_SUCCESS if the new heap element was stored successfully.
*
*	CL_INVALID_PARAMETER if the user has no association to the heap
*	element, i.e., the supplied context is NULL.
*
*	CL_INSUFFICIENT_RESOURCES if the heap is already full and no further
*	elements can be stored.
*
* NOTES
*	The user supplied context will be returned to the user in case the
*	user decides to remove either the root of the heap or any element
*	in the heap with cl_heap_extract_root or cl_heap_delete.
*	Furthermore, the context will be forwarded to the user through
*	cl_pfn_heap_apply_index_update_t to indicate for which element an index
*	change in the heap has happened.
*
* SEE ALSO
*	Heap, cl_pfn_heap_apply_index_update_t, cl_heap_get_capacity,
*	cl_heap_delete, cl_heap_extract_root
*********/

/****f* Component Library: Heap/cl_heap_delete
* NAME
*	cl_heap_delete
*
* DESCRIPTION
*	The cl_heap_delete function removes an arbitrary element, referenced
*	by index, from the heap. Afterwards, the heap property is restored.
*
* SYNOPSIS
*/
void *cl_heap_delete(IN cl_heap_t * const p_heap, IN const size_t index);
/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure to modify.
*
*	index
*		[in] Index to an element in the element_array of the heap which
*		should be deleted.
*
* RETURN VALUE
*	The context pointer associated with the index (and key) in the heap,
*	(i.e., the context initially supplied by the user through
*	cl_heap_insert). If the index is invalid or the heap is empty, then
*	NULL is returned.
*
* NOTES
*	The function will move the element to a "out-of-bounds" position, i.e.,
*	relative to the size but not capacity of the heap, and update
*	the index for the caller via cl_pfn_heap_apply_index_update_t. This
*	ensures that later attempts to modify/delete this element can be
*	intercepted.
*
* SEE ALSO
*	Heap, cl_pfn_heap_apply_index_update_t, cl_heap_insert
*********/

/****f* Component Library: Heap/cl_heap_extract_root
* NAME
*	cl_heap_extract_root
*
* DESCRIPTION
*	The cl_heap_extract_root deletes the root of the heap and returns
*	the stored context to the user. The naming scheme is generalized
*	here to support minimum or maximum heaps with one function, e.g.,
*	the root refers to the element with the smallest key in a minimum
*	heap.
*
* SYNOPSIS
*/
void *cl_heap_extract_root(IN cl_heap_t * const p_heap);
/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure to modify.
*
* RETURN VALUE
*	The context pointer associated with the smallest or largest key
*	in a minimum or maximum heap, respectively.
*
* NOTES
*	Internally, the cl_heap_extract_root function calls cl_heap_delete for
*	the element with index zero. Therefore, refer to cl_heap_delete for
*	further explanation.
*
* SEE ALSO
*	Heap, cl_heap_delete
*********/

/****f* Component Library: Heap/cl_heap_resize
* NAME
*	cl_heap_resize
*
* DESCRIPTION
*	The cl_heap_resize function changes the capacity of an existing heap.
*	The function will not delete or relocate existing elements in the heap,
*	consequently changing the capacity to a value smaller than the current
*	number of elements in the heap will result in an error.
*
* SYNOPSIS
*/
cl_status_t cl_heap_resize(IN cl_heap_t * const p_heap,
			  IN const size_t new_size);
/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure to resize.
*
*	new_size
*		[in] Total number of elements allocated for the heap.
*
* RETURN VALUE
*	CL_SUCCESS if the heap has been changed in size.
*
*	CL_INVALID_PARAMETER if either new_size is less than or equal to zero,
*	or if the requested size is smaller than the number of currently
*	stored heap elements (to prevent loss of data).
*
*	CL_INSUFFICIENT_MEMORY if resizing element_array failed due to an
*	insufficient amount of memory. The data stored in the heap is not lost
*	and can still be retrieved or modified.
*
* NOTES
*	Resizing the heap to a zero capacity is not supported. Please, use the
*	cl_heap_destroy function to free the memory allocated in cl_heap_t.
*
* SEE ALSO
*	Heap, cl_heap_get_capacity, cl_heap_destroy
*********/

/****f* Component Library: Heap/cl_heap_get_capacity
* NAME
*	cl_heap_get_capacity
*
* DESCRIPTION
*	The cl_heap_get_capacity function returns the capacity of a heap.
*
* SYNOPSIS
*/
static inline size_t cl_heap_get_capacity(IN const cl_heap_t * const p_heap)
{
	CL_ASSERT(p_heap);
	CL_ASSERT(p_heap->state == CL_INITIALIZED);

	return (p_heap->capacity);
}

/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure whose capacity to return.
*
* RETURN VALUE
*	Total number of elements which can be stored in the heap.
*
* NOTES
*	The capacity is the number of elements that the heap can store, and
*	can be greater than the number of elements stored. To get the number of
*	elements stored in the heap, use cl_heap_get_size.
*
* SEE ALSO
*	Heap, cl_heap_get_size
*********/

/****f* Component Library: Heap/cl_heap_get_size
* NAME
*	cl_heap_get_size
*
* DESCRIPTION
*	The cl_heap_get_size function returns the number of elements stored
*	in the heap.
*
* SYNOPSIS
*/
static inline size_t cl_heap_get_size(IN const cl_heap_t * const p_heap)
{
	CL_ASSERT(p_heap);
	CL_ASSERT(p_heap->state == CL_INITIALIZED);

	return (p_heap->size);
}

/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure whose size to return.
*
* RETURN VALUE
*	Number of elements in the heap.
*
* SEE ALSO
*	Heap, cl_heap_resize, cl_heap_get_capacity
*********/

/****f* Component Library: Heap/cl_heap_is_empty
* NAME
*	cl_heap_is_empty
*
* DESCRIPTION
*	The cl_heap_is_empty function checks whether elements are stored in the
*	heap, or not.
*
* SYNOPSIS
*/
static inline boolean_t cl_heap_is_empty(IN const cl_heap_t * const p_heap)
{
	CL_ASSERT(p_heap);
	CL_ASSERT(p_heap->state == CL_INITIALIZED);

	return (p_heap->size) ? FALSE : TRUE;
}

/*
* PARAMETERS
*	p_heap
*		[in] Pointer to an initialized cl_heap_t structure.
*
* RETURN VALUES
*	TRUE if no elements are stored in the heap, otherwise FALSE.
*
* SEE ALSO
*	Heap, cl_heap_get_size
*********/

/****f* Component Library: Heap/cl_is_stored_in_heap
* NAME
*	cl_is_stored_in_heap
*
* DESCRIPTION
*	The function cl_is_stored_in_heap can be used by the caller to verify
*	that a context, initially supplied via cl_heap_insert, is stored
*	in the heap at a index known by the caller.
*
* SYNOPSIS
*/
boolean_t cl_is_stored_in_heap(IN const cl_heap_t * const p_heap,
			       IN const void *const ctx,
			       IN const size_t index);
/*
* PARAMETERS
*	p_heap
*		[in] Pointer to an initialized cl_heap_t structure.
*
*       context
*		[in] Pointer to a context which "represents" the heap element
*		initially stored with the cl_heap_insert function.
*
*       index
*		[in] Index to the element in the element_array.
*
* RETURN VALUES
*	TRUE if the index is not out-of-bounds and the stored and requested
*	context matches, otherwise FALSE.
*
* SEE ALSO
*	Heap, cl_heap_insert
*********/

/****f* Component Library: Heap/cl_is_heap_inited
* NAME
*	cl_is_heap_inited
*
* DESCRIPTION
*	The function cl_is_heap_inited can be used to verify that the state
*	of a heap is CL_INITIALIZED.
*
* SYNOPSIS
*/
static inline uint32_t cl_is_heap_inited(IN const cl_heap_t * const p_heap)
{
	CL_ASSERT(p_heap);
	CL_ASSERT(cl_is_state_valid(p_heap->state));

	return (p_heap->state == CL_INITIALIZED);
}

/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure.
*
* RETURN VALUES
*	TRUE if the heap is initialized, otherwise FALSE.
*
* NOTES
*	Can be called for every allocaetd heap for which at least
*	cl_heap_construct or cl_heap_init was called beforehand.
*
* SEE ALSO
*	Heap, cl_heap_construct, cl_heap_init
*********/

/****f* Component Library: Heap/cl_verify_heap_property
* NAME
*       cl_verify_heap_property
*
* DESCRIPTION
*       The function cl_verify_heap_property validates the correctness of a
*       given heap.
*
* SYNOPSIS
*/
boolean_t cl_verify_heap_property(IN const cl_heap_t * const p_heap);

/*
* PARAMETERS
*	p_heap
*		[in] Pointer to a cl_heap_t structure.
*
* RETURN VALUES
*	TRUE if the heap complies with the heap property, otherwise FALSE.
*
* SEE ALSO
*	Heap, cl_heap_construct, cl_heap_init
*********/

END_C_DECLS
#endif				/* _CL_HEAP_H_ */
