Skip to content

File binaryheap.c

File List > base > binaryheap.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * binaryheap.c - binary heap implementation
 */
#include <stdlib.h>
#include <assert.h>
#include "binaryheap.h"
#include "memory_alloc.h"
#include "error_manager.h"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

#define BH_PARENT(i)    ((i - 1)/2)
#define BH_LEFT(i)  (2*(i) + 1)
#define BH_RIGHT(i) (2*(i)+2)
#define BH_ELEMENT_COPY(heap, dest, src) (memcpy (dest, src, (heap)->elem_size))

#define BH_SWAP(heap, left, right)                            \
  do                                              \
    {                                             \
      BH_ELEMENT_COPY (heap, (heap)->swap_buf, BH_ELEMENT (heap, left));          \
      BH_ELEMENT_COPY (heap, BH_ELEMENT (heap, left), BH_ELEMENT (heap, right));      \
      BH_ELEMENT_COPY (heap, BH_ELEMENT (heap, right), (heap)->swap_buf);         \
    }                                             \
  while (0)

#define BH_CMP(heap, l, r) \
  (heap->cmp_func (BH_ELEMENT (heap, l), BH_ELEMENT (heap, r), heap->cmp_arg))


static void bh_up_heap (BINARY_HEAP * heap, int index);
static void bh_replace_max (BINARY_HEAP * heap, void *elem);

/*
 * bh_up_heap () - push an element up the heap to the correct position
 * return : void
 * heap (in)  : heap
 * index (in) : index of the element to
 */
static void
bh_up_heap (BINARY_HEAP * heap, int index)
{
  if (index == 0)
    {
      return;
    }

  if (BH_CMP (heap, BH_PARENT (index), index) == BH_LT)
    {
      /* swap element with parent */
      BH_SWAP (heap, index, BH_PARENT (index));
      /* shift parent */
      bh_up_heap (heap, BH_PARENT (index));
    }
}

/*
 * bh_down_heap () - push an element down the heap to its correct position
 * return : void
 * heap (in)  : heap
 * index (in) : element index
 */
void
bh_down_heap (BINARY_HEAP * heap, int index)
{
  int left = BH_LEFT (index);
  int right = BH_RIGHT (index);
  int largest = index;

  if (left <= heap->element_count - 1 && BH_CMP (heap, left, largest) == BH_GT)
    {
      largest = left;
    }

  if (right <= heap->element_count - 1 && BH_CMP (heap, right, largest) == BH_GT)
    {
      largest = right;
    }

  if (largest != index)
    {
      BH_SWAP (heap, index, largest);
      bh_down_heap (heap, largest);
    }
}

/*
 * bh_create () - create an empty heap
 * return : heap or NULL
 * max_capacity (in): maximum capacity of the heap
 * elem_size (in)   : the size of one heap element.
 * cmp_func (in)    : pointer to the comparison function
 * cmp_arg (in)     : argument to be passed to the comparison function
 *
 *  Note: This implementation considers the heap to be a MAX heap (i.e.: the root of the heap is the "largest" element).
 *    To use the heap as a MIN heap, callers can use the comparison function to inverse the comparison
 */
BINARY_HEAP *
bh_create (THREAD_ENTRY * thread_p, int max_capacity, int elem_size, bh_key_comparator cmp_func, BH_CMP_ARG cmp_arg)
{
  BINARY_HEAP *heap = NULL;

  heap = (BINARY_HEAP *) db_private_alloc (thread_p, sizeof (BINARY_HEAP));
  if (heap == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (BINARY_HEAP));
      return NULL;
    }

  heap->members = db_private_alloc (thread_p, max_capacity * elem_size);
  if (heap->members == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, max_capacity * elem_size);
      db_private_free (thread_p, heap);
      return NULL;
    }
  heap->swap_buf = db_private_alloc (thread_p, elem_size);
  if (heap->swap_buf == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, elem_size);
      db_private_free (thread_p, heap->members);
      db_private_free (thread_p, heap);
      return NULL;
    }
  memset (heap->members, 0, max_capacity * elem_size);
  heap->max_capacity = max_capacity;
  heap->elem_size = elem_size;
  heap->cmp_func = cmp_func;
  heap->cmp_arg = cmp_arg;
  heap->element_count = 0;
  heap->state = BH_HEAP_CONSISTENT;

  return heap;
}

/*
 * bh_destroy () - destroy a binary heap
 * return : void
 * heap (in) : heap
 */
void
bh_destroy (THREAD_ENTRY * thread_p, BINARY_HEAP * heap)
{
  if (heap != NULL)
    {
      if (heap->members != NULL)
    {
      db_private_free (thread_p, heap->members);
    }
      if (heap->swap_buf != NULL)
    {
      db_private_free (thread_p, heap->swap_buf);
    }
      db_private_free (thread_p, heap);
    }
}

/*
 * bh_add () - add an element to the heap without preserving the heap property
 * return : error code if capacity is exceeded or NO_ERROR
 * heap (in) : heap
 * elem (in) : new element
 *
 *  Note: This function is provided as a convenience to speed up heap creation.
 *    It is more efficient to load about half the heap with unordered elements and call bh_build_heap to restore the
 *    heap property.
 */
int
bh_add (BINARY_HEAP * heap, void *elem)
{
  assert (heap != NULL);
  assert (elem != NULL);

  heap->state = BH_HEAP_INCONSISTENT;

  if (heap->element_count >= heap->max_capacity)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BINARY_HEAP_OUT_OF_RANGE, 0);
      return ER_BINARY_HEAP_OUT_OF_RANGE;
    }
  BH_ELEMENT_COPY (heap, BH_ELEMENT (heap, heap->element_count), elem);
  heap->element_count++;

  return NO_ERROR;
}

/*
 * bh_insert () - insert an element in the correct position in the heap
 * return : error code or NO_ERROR
 * heap (in) : heap
 * elem (in) : new element
 */
int
bh_insert (BINARY_HEAP * heap, void *elem)
{
  assert (heap != NULL);
  assert (elem != NULL);

  if (heap->element_count != 0)
    {
      assert_release (heap->state == BH_HEAP_CONSISTENT);
    }
  else
    {
      /* This is the first element, set the consistent property */
      heap->state = BH_HEAP_CONSISTENT;
    }

  if (heap->element_count >= heap->max_capacity)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BINARY_HEAP_OUT_OF_RANGE, 0);
      return ER_BINARY_HEAP_OUT_OF_RANGE;
    }

  BH_ELEMENT_COPY (heap, BH_ELEMENT (heap, heap->element_count), elem);
  heap->element_count++;
  bh_up_heap (heap, heap->element_count - 1);

  return NO_ERROR;
}

/*
 * bh_try_insert () - insert an element into the heap if heap hasn't reached the full capacity or if new element is
 *            smaller than the top element
 * return    : BH_TRY_INSERT_RESULT
 *         - BH_TRY_INSERT_ACCEPTED if the heap was not full
 *         - BH_TRY_INSERT_REJECTED if the heap was full and new element was rejected
 *         - BH_TRY_INSERT_REPLACED if the heap was full and new element replaced old root.
 * heap (in)      : heap
 * elem (in)      : new element
 * replaced (out) : value of replaced element (if replaced).
 */
BH_TRY_INSERT_RESULT
bh_try_insert (BINARY_HEAP * heap, void *elem, void *replaced)
{
  assert (heap != NULL);
  assert (elem != NULL);

  if (heap->element_count < heap->max_capacity)
    {
      bh_insert (heap, elem);
      return BH_TRY_INSERT_ACCEPTED;
    }
  /* if root is larger than new element, replace root */
  if (heap->cmp_func (BH_ROOT (heap), elem, heap->cmp_arg) == BH_GT)
    {
      if (replaced != NULL)
    {
      BH_ELEMENT_COPY (heap, replaced, BH_ROOT (heap));
    }
      bh_replace_max (heap, elem);
      return BH_TRY_INSERT_REPLACED;
    }
  return BH_TRY_INSERT_REJECTED;
}

/*
 * bh_build_heap () - restore the heap property in an unordered heap
 * return : void
 * heap (in) : heap
 */
void
bh_build_heap (BINARY_HEAP * heap)
{
  int i;

  assert (heap != NULL);

  if (heap->state == BH_HEAP_CONSISTENT)
    {
      /* already consistent, nothing to build */
      return;
    }

  for (i = BH_PARENT (heap->element_count - 1); i >= 0; i--)
    {
      bh_down_heap (heap, i);
    }
  heap->state = BH_HEAP_CONSISTENT;
}

/*
 * bh_extract_max () - pop the largest element from a heap
 * return         : true if heap is not empty and element is extracted, false if heap is empty.
 * heap (in)          : heap
 * extract_elem (out) : extracted element.
 */
bool
bh_extract_max (BINARY_HEAP * heap, void *extract_elem)
{
  assert (heap != NULL);
  assert (extract_elem != NULL);

  if (heap->element_count <= 0)
    {
      /* empty */
      return false;
    }
  /* extract algorithm is:
   * 1. replace root with last element on the last level.
   * 2. recursive down-swap new root with children until the order is correct.
   */
  /* decrement element count; this also becomes the index of last element in heap */
  heap->element_count--;
  /* Copy root. */
  BH_ELEMENT_COPY (heap, extract_elem, BH_ROOT (heap));
  if (heap->element_count > 0)
    {
      /* replace root with last element. */
      BH_ELEMENT_COPY (heap, BH_ROOT (heap), BH_ELEMENT (heap, heap->element_count));
      /* down-swap until order is correct again */
      bh_down_heap (heap, 0);
    }
  return true;
}

/*
 * bh_replace_max () - replace the largest element from a heap with a new
 *             element
 * return : void
 * heap (in) : heap
 * elem (in) : new element
 */
void
bh_replace_max (BINARY_HEAP * heap, void *elem)
{
  assert (heap != NULL);
  assert (elem != NULL);

  /* heap_max greater than elem */
  BH_ELEMENT_COPY (heap, BH_ROOT (heap), elem);
  bh_down_heap (heap, 0);
}

/*
 * bh_peek_max () - peek the value of the largest element in the heap
 * return      : true if heap is not empty and element is extracted, false if heap is empty.
 * heap (in)       : heap
 * peek_elem (out) : peeked value of largest element.
 */
bool
bh_peek_max (BINARY_HEAP * heap, void *peek_elem)
{
  assert (heap != NULL);
  assert (peek_elem != NULL);

  if (heap->element_count == 0)
    {
      return false;
    }
  BH_ELEMENT_COPY (heap, peek_elem, BH_ROOT (heap));
  return true;
}

/*
 * bh_to_sorted_array () - transform a binary heap into a sorted array
 * return    : void
 * heap (in) : heap
 *
 *  Note: the array is sorted smallest to largest (smallest element on the first position)
 */
void
bh_to_sorted_array (BINARY_HEAP * heap)
{
  int element_count;

  assert (heap != NULL);

  element_count = heap->element_count;
  /* while has elements, extract max */
  while (heap->element_count > 1)
    {
      BH_SWAP (heap, 0, heap->element_count - 1);
      heap->element_count--;
      bh_down_heap (heap, 0);
    }
  /* no longer consistent */
  heap->state = BH_SORTED_ARRAY;

  /* reset element count */
  heap->element_count = element_count;
}

#if defined(CUBRID_DEBUG)
/*
 * bh_tests_consistent () - test if the elements stored in the heap
 *              have the heap property
 * return : true if the heap is consistent, false otherwise
 * heap (in) : heap
 */
bool
bh_tests_consistent (BINARY_HEAP * heap)
{
  int i;

  assert (heap != NULL);

  if (heap->element_count <= 1)
    {
      /* at most one element, it is consistent */
      heap->state = BH_HEAP_CONSISTENT;
      return true;
    }

  /* test heap property: CHILD <= PARENT */
  for (i = 1; i < heap->element_count; i++)
    {
      if (BH_CMP (heap, BH_PARENT (i), i) == BH_LT)
    {
      heap->state = BH_HEAP_INCONSISTENT;
      return false;
    }
    }
  heap->state = BH_HEAP_CONSISTENT;
  return true;
}
#endif

/*
 * bh_element_at () - get element at specified index from the array
 * return : element
 * heap (in)  : heap
 * index (in) : index
 * elem (out) : element at index.
 */
void
bh_element_at (BINARY_HEAP * heap, int index, void *elem)
{
  assert (heap != NULL);
  assert (index < heap->element_count);
  assert (elem != NULL);

  BH_ELEMENT_COPY (heap, elem, BH_ELEMENT (heap, index));
}

/*
 * bh_is_consistent () - return true if heap is in consistent state
 * return : true or false
 * heap (in) : heap
 */
bool
bh_is_consistent (BINARY_HEAP * heap)
{
  assert (heap != NULL);
  return (heap->state == BH_HEAP_CONSISTENT);
}

/*
 * bh_is_full () - test if heap holds maximum capacity elements
 * return : true if heap is at maximum capacity
 * heap (in) : heap
 */
bool
bh_is_full (BINARY_HEAP * heap)
{
  assert (heap != NULL);
  return (heap->element_count >= heap->max_capacity);
}