/* -*- mode: C -*- */ /* IGraph library. Copyright (C) 2007-2012 Gabor Csardi 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "igraph_memory.h" #include "igraph_error.h" #include "config.h" #include #include /* memcpy & co. */ #include #define PARENT(x) (((x)+1)/2-1) #define LEFTCHILD(x) (((x)+1)*2-1) #define RIGHTCHILD(x) (((x)+1)*2) /** * \ingroup heap * \function igraph_heap_init * \brief Initializes an empty heap object. * * Creates an empty heap, but allocates size for some elements. * \param h Pointer to an uninitialized heap object. * \param alloc_size Number of elements to allocate memory for. * \return Error code. * * Time complexity: O(\p alloc_size), assuming memory allocation is a * linear operation. */ int FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, long int alloc_size) { if (alloc_size <= 0 ) { alloc_size = 1; } h->stor_begin = igraph_Calloc(alloc_size, BASE); if (h->stor_begin == 0) { IGRAPH_ERROR("heap init failed", IGRAPH_ENOMEM); } h->stor_end = h->stor_begin + alloc_size; h->end = h->stor_begin; h->destroy = 1; return 0; } /** * \ingroup heap * \function igraph_heap_init_array * \brief Build a heap from an array. * * Initializes a heap object from an array, the heap is also * built of course (constructor). * \param h Pointer to an uninitialized heap object. * \param data Pointer to an array of base data type. * \param len The length of the array at \p data. * \return Error code. * * Time complexity: O(n), the number of elements in the heap. */ int FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *h, BASE* data, long int len) { h->stor_begin = igraph_Calloc(len, BASE); if (h->stor_begin == 0) { IGRAPH_ERROR("heap init from array failed", IGRAPH_ENOMEM); } h->stor_end = h->stor_begin + len; h->end = h->stor_end; h->destroy = 1; memcpy(h->stor_begin, data, (size_t) len * sizeof(igraph_real_t)); FUNCTION(igraph_heap, i_build) (h->stor_begin, h->end - h->stor_begin, 0); return 0; } /** * \ingroup heap * \function igraph_heap_destroy * \brief Destroys an initialized heap object. * * \param h The heap object. * * Time complexity: O(1). */ void FUNCTION(igraph_heap, destroy)(TYPE(igraph_heap)* h) { if (h->destroy) { if (h->stor_begin != 0) { igraph_Free(h->stor_begin); h->stor_begin = 0; } } } /** * \ingroup heap * \function igraph_heap_empty * \brief Decides whether a heap object is empty. * * \param h The heap object. * \return \c TRUE if the heap is empty, \c FALSE otherwise. * * TIme complexity: O(1). */ igraph_bool_t FUNCTION(igraph_heap, empty)(TYPE(igraph_heap)* h) { assert(h != NULL); assert(h->stor_begin != NULL); return h->stor_begin == h->end; } /** * \ingroup heap * \function igraph_heap_push * \brief Add an element. * * Adds an element to the heap. * \param h The heap object. * \param elem The element to add. * \return Error code. * * Time complexity: O(log n), n is the number of elements in the * heap if no reallocation is needed, O(n) otherwise. It is ensured * that n push operations are performed in O(n log n) time. */ int FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem) { assert(h != NULL); assert(h->stor_begin != NULL); /* full, allocate more storage */ if (h->stor_end == h->end) { long int new_size = FUNCTION(igraph_heap, size)(h) * 2; if (new_size == 0) { new_size = 1; } IGRAPH_CHECK(FUNCTION(igraph_heap, reserve)(h, new_size)); } *(h->end) = elem; h->end += 1; /* maintain heap */ FUNCTION(igraph_heap, i_shift_up)(h->stor_begin, FUNCTION(igraph_heap, size)(h), FUNCTION(igraph_heap, size)(h) - 1); return 0; } /** * \ingroup heap * \function igraph_heap_top * \brief Top element. * * For maximum heaps this is the largest, for minimum heaps the * smallest element of the heap. * \param h The heap object. * \return The top element. * * Time complexity: O(1). */ BASE FUNCTION(igraph_heap, top)(TYPE(igraph_heap)* h) { assert(h != NULL); assert(h->stor_begin != NULL); assert(h->stor_begin != h->end); return h->stor_begin[0]; } /** * \ingroup heap * \function igraph_heap_delete_top * \brief Return and removes the top element * * Removes and returns the top element of the heap. For maximum heaps * this is the largest, for minimum heaps the smallest element. * \param h The heap object. * \return The top element. * * Time complexity: O(log n), n is the number of elements in the * heap. */ BASE FUNCTION(igraph_heap, delete_top)(TYPE(igraph_heap)* h) { BASE tmp; assert(h != NULL); assert(h->stor_begin != NULL); tmp = h->stor_begin[0]; FUNCTION(igraph_heap, i_switch)(h->stor_begin, 0, FUNCTION(igraph_heap, size)(h) - 1); h->end -= 1; FUNCTION(igraph_heap, i_sink)(h->stor_begin, h->end - h->stor_begin, 0); return tmp; } /** * \ingroup heap * \function igraph_heap_size * \brief Number of elements * * Gives the number of elements in a heap. * \param h The heap object. * \return The number of elements in the heap. * * Time complexity: O(1). */ long int FUNCTION(igraph_heap, size)(TYPE(igraph_heap)* h) { assert(h != NULL); assert(h->stor_begin != NULL); return h->end - h->stor_begin; } /** * \ingroup heap * \function igraph_heap_reserve * \brief Allocate more memory * * Allocates memory for future use. The size of the heap is * unchanged. If the heap is larger than the \p size parameter then * nothing happens. * \param h The heap object. * \param size The number of elements to allocate memory for. * \return Error code. * * Time complexity: O(\p size) if \p size is larger than the current * number of elements. O(1) otherwise. */ int FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, long int size) { long int actual_size = FUNCTION(igraph_heap, size)(h); BASE *tmp; assert(h != NULL); assert(h->stor_begin != NULL); if (size <= actual_size) { return 0; } tmp = igraph_Realloc(h->stor_begin, (size_t) size, BASE); if (tmp == 0) { IGRAPH_ERROR("heap reserve failed", IGRAPH_ENOMEM); } h->stor_begin = tmp; h->stor_end = h->stor_begin + size; h->end = h->stor_begin + actual_size; return 0; } /** * \ingroup heap * \brief Build a heap, this should not be called directly. */ void FUNCTION(igraph_heap, i_build)(BASE* arr, long int size, long int head) { if (RIGHTCHILD(head) < size) { /* both subtrees */ FUNCTION(igraph_heap, i_build)(arr, size, LEFTCHILD(head) ); FUNCTION(igraph_heap, i_build)(arr, size, RIGHTCHILD(head)); FUNCTION(igraph_heap, i_sink)(arr, size, head); } else if (LEFTCHILD(head) < size) { /* only left */ FUNCTION(igraph_heap, i_build)(arr, size, LEFTCHILD(head)); FUNCTION(igraph_heap, i_sink)(arr, size, head); } else { /* none */ } } /** * \ingroup heap * \brief Shift an element upwards in a heap, this should not be * called directly. */ void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, long int size, long int elem) { if (elem == 0 || arr[elem] HEAPLESS arr[PARENT(elem)]) { /* at the top */ } else { FUNCTION(igraph_heap, i_switch)(arr, elem, PARENT(elem)); FUNCTION(igraph_heap, i_shift_up)(arr, size, PARENT(elem)); } } /** * \ingroup heap * \brief Moves an element down in a heap, this function should not be * called directly. */ void FUNCTION(igraph_heap, i_sink)(BASE* arr, long int size, long int head) { if (LEFTCHILD(head) >= size) { /* no subtrees */ } else if (RIGHTCHILD(head) == size || arr[LEFTCHILD(head)] HEAPMOREEQ arr[RIGHTCHILD(head)]) { /* sink to the left if needed */ if (arr[head] HEAPLESS arr[LEFTCHILD(head)]) { FUNCTION(igraph_heap, i_switch)(arr, head, LEFTCHILD(head)); FUNCTION(igraph_heap, i_sink)(arr, size, LEFTCHILD(head)); } } else { /* sink to the right */ if (arr[head] HEAPLESS arr[RIGHTCHILD(head)]) { FUNCTION(igraph_heap, i_switch)(arr, head, RIGHTCHILD(head)); FUNCTION(igraph_heap, i_sink)(arr, size, RIGHTCHILD(head)); } } } /** * \ingroup heap * \brief Switches two elements in a heap, this function should not be * called directly. */ void FUNCTION(igraph_heap, i_switch)(BASE* arr, long int e1, long int e2) { if (e1 != e2) { BASE tmp = arr[e1]; arr[e1] = arr[e2]; arr[e2] = tmp; } }