473 lines
11 KiB
C
473 lines
11 KiB
C
/*
|
|
* vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "Ecore_Data.h"
|
|
|
|
#define HEAP_INCREMENT 4096
|
|
|
|
#define PARENT(i) (i / 2)
|
|
#define LEFT(i) (2 * i)
|
|
#define RIGHT(i) (2 * i + 1)
|
|
|
|
static void _ecore_sheap_heapify(Ecore_Sheap *heap, int i);
|
|
static void _ecore_sheap_update_data(Ecore_Sheap *heap);
|
|
|
|
/**
|
|
* Allocate and initialize a new binary heap
|
|
* @param compare The function for comparing keys, NULL for direct comparison
|
|
* @param size The number of elements to allow in the heap
|
|
* @return A pointer to the newly allocated binary heap on success, NULL on
|
|
* failure.
|
|
*/
|
|
EAPI Ecore_Sheap *
|
|
ecore_sheap_new(Ecore_Compare_Cb compare, int size)
|
|
{
|
|
Ecore_Sheap *heap = NULL;
|
|
|
|
heap = (Ecore_Sheap *)malloc(sizeof(Ecore_Sheap));
|
|
if (!heap)
|
|
return NULL;
|
|
memset(heap, 0, sizeof(Ecore_Sheap));
|
|
|
|
if (!ecore_sheap_init(heap, compare, size))
|
|
{
|
|
FREE(heap);
|
|
return NULL;
|
|
}
|
|
|
|
return heap;
|
|
}
|
|
|
|
/**
|
|
* Initialize a binary heap to default values
|
|
* @param heap The heap to initialize
|
|
* @param compare The function for comparing keys, NULL for direct comparison
|
|
* @param size The number of elements to allow in the heap
|
|
* @return TRUE on success, FALSE on failure
|
|
*/
|
|
EAPI int
|
|
ecore_sheap_init(Ecore_Sheap *heap, Ecore_Compare_Cb compare, int size)
|
|
{
|
|
CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE);
|
|
|
|
heap->space = size;
|
|
if (!compare)
|
|
heap->compare = ecore_direct_compare;
|
|
else
|
|
heap->compare = compare;
|
|
heap->order = ECORE_SORT_MIN;
|
|
|
|
heap->data = (void **)malloc(heap->space * sizeof(void *));
|
|
if (!heap->data)
|
|
return FALSE;
|
|
memset(heap->data, 0, heap->space * sizeof(void *));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Free up the memory used by the heap
|
|
*
|
|
* Frees the memory used by @a heap, calls the destroy function on each data
|
|
* item if necessary.
|
|
*
|
|
* @param heap The heap to be freed
|
|
*/
|
|
EAPI void
|
|
ecore_sheap_destroy(Ecore_Sheap *heap)
|
|
{
|
|
int i;
|
|
|
|
CHECK_PARAM_POINTER("heap", heap);
|
|
|
|
/*
|
|
* Free data in heap
|
|
*/
|
|
if (heap->free_func)
|
|
for (i = 0; i < heap->size; i++)
|
|
heap->free_func(heap->data[i]);
|
|
|
|
FREE(heap->data);
|
|
|
|
FREE(heap);
|
|
}
|
|
|
|
/**
|
|
* Set the function for freeing data.
|
|
* @param heap The heap that will use this function when nodes are
|
|
* destroyed.
|
|
* @param free_func The function that will free the key data.
|
|
* @return @c TRUE on successful set, @c FALSE otherwise.
|
|
*/
|
|
EAPI int
|
|
ecore_sheap_free_cb_set(Ecore_Sheap *heap, Ecore_Free_Cb free_func)
|
|
{
|
|
CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE);
|
|
|
|
heap->free_func = free_func;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Insert new data into the heap.
|
|
* @param heap The heap to insert @a data.
|
|
* @param data The data to add to @a heap.
|
|
* @return TRUE on success, NULL on failure. Increases the size of the heap if
|
|
* it becomes larger than available space.
|
|
*/
|
|
EAPI int
|
|
ecore_sheap_insert(Ecore_Sheap *heap, void *data)
|
|
{
|
|
int i;
|
|
void *temp;
|
|
int parent;
|
|
int position;
|
|
|
|
CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE);
|
|
|
|
/*
|
|
* Increase the size of the allocated data area if there isn't enough
|
|
* space available to add this data
|
|
*/
|
|
if (heap->size >= heap->space)
|
|
return FALSE;
|
|
|
|
heap->sorted = FALSE;
|
|
|
|
/*
|
|
* Place the data at the end of the heap initially. Then determine the
|
|
* parent and position in the array of it's parent.
|
|
*/
|
|
heap->data[heap->size] = data;
|
|
position = heap->size;
|
|
heap->size++;
|
|
i = heap->size;
|
|
parent = PARENT(i) - 1;
|
|
|
|
/*
|
|
* Check the order of the heap to decide where to place the inserted
|
|
* data. The loop is placed inside the if statement to reduce the
|
|
* number of branching decisions that must be predicted.
|
|
*/
|
|
if (heap->order == ECORE_SORT_MIN)
|
|
{
|
|
while ((position > 0) && heap->compare(heap->data[parent],
|
|
heap->data[position]) > 0)
|
|
{
|
|
|
|
/*
|
|
* Swap the data with it's parents to move it up in
|
|
* the heap.
|
|
*/
|
|
temp = heap->data[position];
|
|
heap->data[position] = heap->data[parent];
|
|
heap->data[parent] = temp;
|
|
|
|
/*
|
|
* Now determine the new position for the next
|
|
* iteration of the loop, as well as it's parents
|
|
* position.
|
|
*/
|
|
i = PARENT(i);
|
|
position = i - 1;
|
|
parent = PARENT(i) - 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while ((position > 0) && heap->compare(heap->data[parent],
|
|
heap->data[position]) < 0)
|
|
{
|
|
|
|
/*
|
|
* Swap the data with it's parents to move it up in
|
|
* the heap.
|
|
*/
|
|
temp = heap->data[position];
|
|
heap->data[position] = heap->data[PARENT(i) - 1];
|
|
heap->data[PARENT(i) - 1] = temp;
|
|
|
|
/*
|
|
* Now determine the new position for the next
|
|
* iteration of the loop, as well as it's parents
|
|
* position.
|
|
*/
|
|
i = PARENT(i);
|
|
position = i - 1;
|
|
parent = PARENT(i) - 1;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Extract the item at the top of the heap
|
|
* @param heap The heap to remove the top item
|
|
* @return The top item of the heap on success, NULL on failure.
|
|
* @note The extract function maintains the heap properties after the
|
|
* extract.
|
|
*/
|
|
EAPI void *
|
|
ecore_sheap_extract(Ecore_Sheap *heap)
|
|
{
|
|
void *extreme;
|
|
|
|
if (heap->size < 1)
|
|
return NULL;
|
|
|
|
heap->sorted = FALSE;
|
|
|
|
extreme = heap->data[0];
|
|
heap->size--;
|
|
heap->data[0] = heap->data[heap->size];
|
|
|
|
_ecore_sheap_heapify(heap, 1);
|
|
|
|
return extreme;
|
|
}
|
|
|
|
/**
|
|
* Examine the item at the top of the heap
|
|
* @param heap The heap to examine the top item
|
|
* @return The top item of the heap on success, NULL on failure.
|
|
* @note The function does not alter the heap.
|
|
*/
|
|
EAPI void *
|
|
ecore_sheap_extreme(Ecore_Sheap *heap)
|
|
{
|
|
if (heap->size < 1)
|
|
return NULL;
|
|
|
|
return heap->data[0];
|
|
}
|
|
|
|
/**
|
|
* Change the value of the specified item in the heap
|
|
* @param heap The heap to search for the item to change
|
|
* @param item The item in the heap to change
|
|
* @param newval The new value assigned to the item in the heap
|
|
* @return TRUE on success, FALSE on failure.
|
|
* @note The heap does not free the old data since it must be passed
|
|
* in, so the caller can perform the free if desired.
|
|
*/
|
|
EAPI int
|
|
ecore_sheap_change(Ecore_Sheap *heap, void *item, void *newval)
|
|
{
|
|
int i;
|
|
|
|
CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE);
|
|
|
|
for (i = 0; i < heap->size && heap->compare(heap->data[i], item); i++);
|
|
|
|
if (i < heap->size)
|
|
heap->data[i] = newval;
|
|
else
|
|
return FALSE;
|
|
|
|
/*
|
|
* FIXME: This is not the correct procedure when a change occurs.
|
|
*/
|
|
_ecore_sheap_heapify(heap, 1);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Change the comparison function for the heap
|
|
* @param heap The heap to change comparison function
|
|
* @param compare The new function for comparing nodes
|
|
* @return TRUE on success, FALSE on failure.
|
|
*
|
|
* The comparison function is changed to @compare and the heap is heapified
|
|
* by the new comparison.
|
|
*/
|
|
EAPI int
|
|
ecore_sheap_compare_set(Ecore_Sheap *heap, Ecore_Compare_Cb compare)
|
|
{
|
|
CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE);
|
|
|
|
if (!compare)
|
|
heap->compare = ecore_direct_compare;
|
|
else
|
|
heap->compare = compare;
|
|
|
|
_ecore_sheap_update_data(heap);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Change the order of the heap
|
|
* @param heap The heap to change the order
|
|
* @param order The new order of the heap
|
|
*
|
|
* Changes the heap order of @heap and re-heapifies the data to this new
|
|
* order. The default order is a min heap.
|
|
*/
|
|
EAPI void
|
|
ecore_sheap_order_set(Ecore_Sheap *heap, char order)
|
|
{
|
|
CHECK_PARAM_POINTER("heap", heap);
|
|
|
|
heap->order = order;
|
|
|
|
_ecore_sheap_update_data(heap);
|
|
}
|
|
|
|
/**
|
|
* Sort the data in the heap
|
|
* @param heap The heap to be sorted
|
|
*
|
|
* Sorts the data in the heap into the order that is used for the heap's
|
|
* data.
|
|
*/
|
|
EAPI void
|
|
ecore_sheap_sort(Ecore_Sheap *heap)
|
|
{
|
|
int i = 0;
|
|
void **new_data;
|
|
|
|
CHECK_PARAM_POINTER("heap", heap);
|
|
|
|
new_data = (void **)malloc(heap->size * sizeof(void *));
|
|
|
|
/*
|
|
* Extract the heap and insert into the new data array in order.
|
|
*/
|
|
while (heap->size > 0)
|
|
new_data[i++] = ecore_sheap_extract(heap);
|
|
|
|
/*
|
|
* Free the old data array and update the heap with the new data, also
|
|
* mark as sorted.
|
|
*/
|
|
FREE(heap->data);
|
|
heap->data = new_data;
|
|
heap->size = i;
|
|
heap->sorted = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Access the item at the ith position in the heap
|
|
* @param heap The heap to access the internal data
|
|
* @param i The index of the data within the heap
|
|
* @return The data located at the ith position within @heap on success,
|
|
* NULL on failure.
|
|
* @note The data is guaranteed to be in sorted order.
|
|
*/
|
|
EAPI inline void *
|
|
ecore_sheap_item(Ecore_Sheap *heap, int i)
|
|
{
|
|
if (i >= heap->size)
|
|
return NULL;
|
|
|
|
/*
|
|
* Make sure the data is sorted so we return the correct value.
|
|
*/
|
|
if (!heap->sorted)
|
|
ecore_sheap_sort(heap);
|
|
|
|
return heap->data[i];
|
|
}
|
|
|
|
/*
|
|
* Regain the heap properties starting at position i
|
|
* @param heap The heap to regain heap properties
|
|
* @param i The position to start heapifying
|
|
*/
|
|
static void
|
|
_ecore_sheap_heapify(Ecore_Sheap *heap, int i)
|
|
{
|
|
int extreme;
|
|
int left = LEFT(i);
|
|
int right = RIGHT(i);
|
|
|
|
if (heap->order == ECORE_SORT_MIN)
|
|
{
|
|
if (left <= heap->size && heap->compare(heap->data[left - 1],
|
|
heap->data[i - 1]) < 0)
|
|
extreme = left;
|
|
else
|
|
extreme = i;
|
|
|
|
if (right <= heap->size && heap->compare(heap->data[right - 1],
|
|
heap->data[extreme - 1]) < 0)
|
|
extreme = right;
|
|
}
|
|
else
|
|
{
|
|
if (left <= heap->size && heap->compare(heap->data[left - 1],
|
|
heap->data[i - 1]) > 0)
|
|
extreme = left;
|
|
else
|
|
extreme = i;
|
|
|
|
if (right <= heap->size && heap->compare(heap->data[right - 1],
|
|
heap->data[extreme - 1]) > 0)
|
|
extreme = right;
|
|
}
|
|
|
|
/*
|
|
* If the data needs to be swapped down the heap, recurse on
|
|
* heapifying it's new placement.
|
|
*/
|
|
if (extreme != i)
|
|
{
|
|
void *temp;
|
|
|
|
temp = heap->data[extreme - 1];
|
|
heap->data[extreme - 1] = heap->data[i - 1];
|
|
heap->data[i - 1] = temp;
|
|
|
|
_ecore_sheap_heapify(heap, extreme);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_ecore_sheap_update_data(Ecore_Sheap *heap)
|
|
{
|
|
int i, old_size;
|
|
void **data;
|
|
|
|
/*
|
|
* Track the old values from the heap
|
|
*/
|
|
old_size = heap->size;
|
|
data = heap->data;
|
|
|
|
heap->size = 0;
|
|
heap->data = malloc(heap->space * sizeof(void *));
|
|
|
|
for (i = 0; i < old_size; i++)
|
|
ecore_sheap_insert(heap, data[i]);
|
|
|
|
FREE(data);
|
|
}
|
|
|
|
int
|
|
ecore_direct_compare(const void *key1, const void *key2)
|
|
{
|
|
unsigned long k1, k2;
|
|
|
|
k1 = (unsigned long) key1;
|
|
k2 = (unsigned long) key2;
|
|
|
|
if (k1 > k2)
|
|
return 1;
|
|
|
|
if (k1 < k2)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|