aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--include/bh/algo.h154
-rw-r--r--src/algo.c360
-rw-r--r--tests/src/algo.c282
4 files changed, 798 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 51d4cd1..f3c061d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,11 +20,13 @@ enable_testing()
# Set library code
set(BHLIB_SOURCE
+ src/algo.c
src/hashmap.c
src/queue.c
)
set(BHLIB_HEADER
+ include/bh/algo.h
include/bh/hashmap.h
include/bh/queue.h
)
diff --git a/include/bh/algo.h b/include/bh/algo.h
new file mode 100644
index 0000000..9beae6f
--- /dev/null
+++ b/include/bh/algo.h
@@ -0,0 +1,154 @@
+#ifndef BHLIB_ALGO_H
+#define BHLIB_ALGO_H
+
+#include "bh.h"
+
+/**
+ * @brief Swap two elements.
+ *
+ * @param lhs Pointer to the first element
+ * @param rhs Pointer to the second element
+ * @param size Element size
+ */
+void bh_swap(void *lhs,
+ void *rhs,
+ size_t size);
+
+/**
+ * @brief Partition the array.
+ *
+ * @param pivot Pointer to the pivot element
+ * @param array Pointer to the array
+ * @param size Array size
+ * @param element Element size
+ * @param equal Equal/compare function
+ *
+ * @return Pointer to the first element that are greater-or-equal to pivot
+ * element.
+ *
+ * @warning Pivot element can be an element of the partitioned array itself.
+ */
+void *bh_partition(void *pivot,
+ void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal);
+
+/**
+ * @brief Sort the array.
+ *
+ * @param array Pointer to the array
+ * @param size Array size
+ * @param element Element size
+ * @param equal Equal/compare function
+ */
+void bh_sort(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal);
+
+/**
+ * @brief Sort the array using insert sort.
+ *
+ * @param array Pointer to the array
+ * @param size Array size
+ * @param element Element size
+ * @param equal Equal/compare function
+ */
+void bh_sort_insert(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal);
+
+/**
+ * @brief Sort the array using shell sort.
+ *
+ * @param array Pointer to the array
+ * @param size Array size
+ * @param element Element size
+ * @param equal Equal/compare function
+ */
+void bh_sort_shell(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal);
+
+/**
+ * @brief Sort the array using intro sort.
+ *
+ * @param array Pointer to the array
+ * @param size Array size
+ * @param element Element size
+ * @param equal Equal/compare function
+ */
+void bh_sort_intro(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal);
+
+/**
+ * @brief Sort the array using heap sort.
+ *
+ * @param array Pointer to the array
+ * @param size Array size
+ * @param element Element size
+ * @param equal Equal/compare function
+ */
+void bh_sort_heap(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal);
+
+/**
+ * @brief Make heap from the array.
+ *
+ * @param array Pointer to the array
+ * @param size Array size
+ * @param element Element size
+ * @param equal Equal/compare function
+ *
+ * @sa bh_heap_remove, bh_heap_insert
+ */
+void bh_heap_make(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal);
+
+/**
+ * @brief Remove element from the heap.
+ *
+ * @param array Pointer to the array
+ * @param size Array size
+ * @param element Element size
+ * @param equal Equal/compare function
+ *
+ * @warning Removed element is placed at the end of the array
+ *
+ * @sa bh_heap_make, bh_heap_remove
+ */
+void bh_heap_remove(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal);
+
+/**
+ * @brief Insert element to the heap.
+ *
+ * @param value Pointer to the inserted value (optional)
+ * @param array Pointer to the array
+ * @param size Array size
+ * @param element Element size
+ * @param equal Equal/compare function
+ *
+ * @warning If value is not passed, function assumes inserted element
+ * is already placed at the end of the array.
+ *
+ * @sa bh_heap_make, bh_heap_remove
+ */
+void bh_heap_insert(void *value,
+ void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal);
+
+#endif /* BHLIB_ALGO_H */
diff --git a/src/algo.c b/src/algo.c
new file mode 100644
index 0000000..7f3287d
--- /dev/null
+++ b/src/algo.c
@@ -0,0 +1,360 @@
+#include <bh/algo.h>
+#include <string.h>
+#include <stdio.h>
+
+void bh_swap(void *lhs,
+ void *rhs,
+ size_t size)
+{
+ int tmp;
+
+ /* Swap values in int sized chunks */
+ while (size >= sizeof(tmp))
+ {
+ memmove(&tmp, lhs, sizeof(tmp));
+ memmove(lhs, rhs, sizeof(tmp));
+ memmove(rhs, &tmp, sizeof(tmp));
+
+ lhs = (char *)lhs + sizeof(tmp);
+ rhs = (char *)rhs + sizeof(tmp);
+ size -= sizeof(tmp);
+ }
+
+ /* Swap the rest */
+ if (size)
+ {
+ memmove(&tmp, lhs, size);
+ memmove(lhs, rhs, size);
+ memmove(rhs, &tmp, size);
+ }
+}
+
+void *bh_partition(void *pivot,
+ void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal)
+{
+ char *start, *end, *i, *j;
+
+ /* Calculate start, end and item pointers */
+ start = (char *)array;
+ end = start + size * element;
+ i = start;
+ j = start + (size - 1) * element;
+
+ /* Iterate over array */
+ while (1)
+ {
+ /* Find first element from the left that are bigger then pivot */
+ while (i < end && equal(i, pivot) < 0)
+ i += element;
+
+ /* Find first element from the right that are less then pivot*/
+ while (j >= start && equal(j, pivot) >= 0)
+ j -= element;
+
+ /* If item elemetns passed each other - we are done */
+ if (i >= j)
+ break;
+
+ /* Special case when pivot is actually part of the array */
+ if (pivot == i)
+ pivot = j;
+ else if (pivot == j)
+ pivot = i;
+
+ /* Swap elements and continue */
+ bh_swap(i, j, element);
+ i += element;
+ j -= element;
+ }
+
+ /* Return pointer to the middle of the partition */
+ return j + element;
+}
+
+void bh_sort(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal)
+{
+ /* Use intro sort as default */
+ bh_sort_intro(array, size, element, equal);
+}
+
+void bh_sort_insert(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal)
+{
+ size_t i, j;
+
+ /* Standard insert sort */
+ for (i = 1; i < size; i++)
+ {
+ for (j = i; j >= 1; j -= 1)
+ {
+ char *lhs, *rhs;
+
+ lhs = (char *)array + j * element;
+ rhs = (char *)array + (j - 1) * element;
+
+ if (equal(lhs, rhs) < 0)
+ bh_swap(lhs, rhs, element);
+ else
+ break;
+ }
+ }
+
+}
+
+void bh_sort_shell(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal)
+{
+ static const size_t gaps[9] = {1750, 701, 301, 132, 57, 23, 10, 4, 1};
+ const size_t *gap;
+ size_t i, j;
+
+ /* Shell sort with A102549 sequence*/
+ for (gap = gaps; gap < gaps + sizeof(gaps) / sizeof(*gaps); gap++)
+ {
+ for (i = *gap; i < size; i++)
+ {
+ for (j = i; j >= *gap; j -= *gap)
+ {
+ char *lhs, *rhs;
+
+ lhs = (char *)array + j * element;
+ rhs = (char *)array + (j - *gap) * element;
+
+ if (equal(lhs, rhs) < 0)
+ bh_swap(lhs, rhs, element);
+ else
+ break;
+ }
+ }
+ }
+}
+
+static void bh_sort_intro_r(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal,
+ size_t depth)
+{
+ /* Introsort (with manual tail call optimization) */
+ while (1)
+ {
+ char *start, *middle, *end, *pivot;
+
+ if (size < 16)
+ {
+ /* There are less then 16 elements left - use Shell/Insert sort */
+ bh_sort_shell(array, size, element, equal);
+ return;
+ }
+ else if (!depth)
+ {
+ /* Max depth reached - use heap sort */
+ bh_sort_heap(array, size, element, equal);
+ return;
+ }
+
+ /* Calculate start, middle and end pointers */
+ start = (char *)array;
+ middle = start + (size / 2) * element;
+ end = start + (size - 1) * element;
+
+ /* Select middle element */
+ if (equal(start, middle) > 0)
+ {
+ if (equal(middle, end) > 0)
+ pivot = middle;
+ else if (equal(start, end) > 0)
+ pivot = end;
+ else
+ pivot = start;
+ }
+ else
+ {
+ if (equal(start, end) > 0)
+ pivot = start;
+ else if (equal(middle, end) > 0)
+ pivot = end;
+ else
+ pivot = middle;
+ }
+
+ /* Partition the array */
+ middle = bh_partition(pivot, array, size, element, equal);
+
+ /* Recursive call into first half */
+ bh_sort_intro_r(array, (middle - start) / element, element, equal, depth - 1);
+
+ /* Setup array and size for the second half */
+ array = middle;
+ size = size - (middle - start) / element;
+ depth--;
+ }
+}
+
+void bh_sort_intro(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal)
+{
+ size_t depth, depth_log;
+
+ /* Calculate max depth */
+ depth = 0;
+ depth_log = 1;
+ while (depth_log < size)
+ {
+ depth++;
+ depth_log <<= 1;
+ }
+
+ /* Call main sorting function */
+ bh_sort_intro_r(array, size, element, equal, depth);
+}
+
+void bh_sort_heap(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal)
+{
+ /* Use heap_make and heap_remove for sorting */
+ bh_heap_make(array, size, element, equal);
+ while (size)
+ bh_heap_remove(array, size--, element, equal);
+}
+
+
+void bh_heap_make(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal)
+{
+ char *start, *end;
+ size_t i;
+
+ /* Calculate start and end pointers of the array */
+ start = (char *)array;
+ end = start + size * element;
+
+ /* Bottom up heapify algorithm */
+ for (i = size / 2 + 1; i; --i)
+ {
+ char *current, *left, *right;
+
+ /* Calculate current and children pointers */
+ current = start + (i - 1) * element;
+ left = start + (current - start) * 2 + element;
+ right = left + element;
+
+ while (left < end)
+ {
+ char *biggest;
+
+ /* Determine biggest child */
+ biggest = left;
+ if (right < end && equal(left, right) < 0)
+ biggest = right;
+
+ /* Compare biggest child with current */
+ if (equal(current, biggest) < 0)
+ {
+ /* Swap content and recalculate children pointers */
+ bh_swap(current, biggest, element);
+ current = biggest;
+ left = start + (current - start) * 2 + element;
+ right = left + element;
+ }
+ else
+ break;
+ }
+ }
+}
+
+void bh_heap_remove(void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal)
+{
+ char *start, *end, *current, *left, *right;
+
+ if (size <= 1)
+ return;
+
+ /* Calculate start, end and children pointers */
+ start = (char *)array;
+ end = start + (size - 1) * element;
+ current = start;
+ left = start + (current - start) * 2 + element;
+ right = left + element;
+
+ /* Swap first and last element */
+ bh_swap(current, end, element);
+
+ /* Iterate until we reach the end */
+ while (left < end)
+ {
+ char *biggest;
+
+ /* Determine biggest child */
+ biggest = left;
+ if (right < end && equal(left, right) < 0)
+ biggest = right;
+
+ /* Compare biggest child with current */
+ if (equal(current, biggest) < 0)
+ {
+ /* Swap content and recalculate children pointers */
+ bh_swap(current, biggest, element);
+ current = biggest;
+ left = start + (current - start) * 2 + element;
+ right = left + element;
+ }
+ else
+ break;
+ }
+}
+
+void bh_heap_insert(void *value,
+ void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal)
+{
+ char *start, *end, *current;
+
+ /* Calculate begin and end pointers */
+ start = (char *)array;
+ end = start + size * element;
+ current = end;
+
+ /* Copy value into array */
+ if (value)
+ memmove(current, value, element);
+
+ while (current > start)
+ {
+ char *parent;
+
+ /* Calculate parent pointer */
+ parent = start + (((current - start) / element - 1) / 2) * element;
+
+ /* Compare current and parent */
+ if (equal(parent, current) < 0)
+ {
+ /* Swap current and parent */
+ bh_swap(parent, current, element);
+ current = parent;
+ }
+ else
+ break;
+ }
+}
diff --git a/tests/src/algo.c b/tests/src/algo.c
new file mode 100644
index 0000000..861158b
--- /dev/null
+++ b/tests/src/algo.c
@@ -0,0 +1,282 @@
+#include <bh/algo.h>
+#include <bh/unit.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define SIZE (4 * 1024)
+#define MAX_VALUE 0x1000
+#define TEST_RUNS 64
+
+static int int_equal(const void *lhs, const void *rhs)
+{
+ return *(const int*)lhs - *(const int*)rhs;
+}
+
+static int check_partition(size_t index, int pivot, int *array, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < index; i++)
+ if (array[i] >= pivot)
+ return -1;
+
+ for (i = index; i < size; i++)
+ if (array[i] < pivot)
+ return -1;
+
+ return 0;
+}
+
+static int check_heap(int *array, size_t size)
+{
+ size_t i, left, right;
+
+ for (i = 0; i < size; i++)
+ {
+ left = i * 2 + 1;
+ right = i * 2 + 2;
+
+ if (left < size && array[i] < array[left])
+ return -1;
+ if (right < size && array[i] < array[right])
+ return -1;
+ }
+
+ return 0;
+}
+
+static void reference_sort(int *array, size_t size)
+{
+ size_t i, j;
+ int tmp;
+
+ /* Reference sort is... buble sort! */
+ for (i = 0; i < size; i++)
+ {
+ for (j = 1; j < size; j++)
+ {
+ if (array[j - 1] > array[j])
+ {
+ tmp = array[j - 1];
+ array[j - 1] = array[j];
+ array[j] = tmp;
+ }
+ }
+ }
+}
+
+static int partition(void)
+{
+ int input[SIZE], in_count[MAX_VALUE], out_count[MAX_VALUE], *middle, pivot;
+ size_t i, j;
+
+ for (i = 0; i < TEST_RUNS; i++)
+ {
+ memset(in_count, 0, sizeof(in_count));
+ memset(out_count, 0, sizeof(out_count));
+
+ /* Populate test array and count numbers */
+ for (j = 0; j < SIZE; j++)
+ {
+ input[j] = rand() % MAX_VALUE;
+ in_count[input[j]]++;
+ }
+
+ /* Partition the array */
+ if (i == 0)
+ pivot = -1;
+ else if (i == TEST_RUNS - 1)
+ pivot = MAX_VALUE;
+ else
+ pivot = MAX_VALUE / 2;
+
+ middle = (int *)bh_partition(&pivot, input, SIZE, sizeof(int), int_equal);
+
+ /* Count numbers in array */
+ for (j = 0; j < SIZE; j++)
+ out_count[input[j]]++;
+
+ /* Check partition */
+ bh_unit_assert(check_partition(middle - input, pivot, input, SIZE) == 0);
+
+ /* Verify, that array has same amount of numbers */
+ for (j = 0; j < MAX_VALUE; j++)
+ bh_unit_assert(in_count[j] == out_count[j]);
+ }
+
+ return 0;
+}
+
+static int sort(void)
+{
+ int input[SIZE], reference[SIZE];
+ size_t i, j;
+
+ for (i = 0; i < TEST_RUNS; i++)
+ {
+ for (j = 0; j < SIZE; j++)
+ {
+ input[j] = rand() % MAX_VALUE;
+ reference[j] = input[j];
+ }
+
+ reference_sort(reference, SIZE);
+ bh_sort(input, SIZE, sizeof(int), int_equal);
+ bh_unit_assert(memcmp(reference, input, sizeof(input)) == 0);
+
+ qsort(reference, SIZE, sizeof(int), int_equal);
+ bh_unit_assert(memcmp(reference, input, sizeof(input)) == 0);
+ }
+
+ return 0;
+}
+
+static int sort_insert(void)
+{
+ int input[SIZE], reference[SIZE];
+ size_t i, j;
+
+ for (i = 0; i < TEST_RUNS; i++)
+ {
+ for (j = 0; j < SIZE; j++)
+ {
+ input[j] = rand() % MAX_VALUE;
+ reference[j] = input[j];
+ }
+
+ reference_sort(reference, SIZE);
+ bh_sort_insert(input, SIZE, sizeof(int), int_equal);
+ bh_unit_assert(memcmp(reference, input, sizeof(input)) == 0);
+
+ qsort(reference, SIZE, sizeof(int), int_equal);
+ bh_unit_assert(memcmp(reference, input, sizeof(input)) == 0);
+ }
+
+ return 0;
+}
+
+static int sort_shell(void)
+{
+ int input[SIZE], reference[SIZE];
+ size_t i, j;
+
+ for (i = 0; i < TEST_RUNS; i++)
+ {
+ for (j = 0; j < SIZE; j++)
+ {
+ input[j] = rand() % MAX_VALUE;
+ reference[j] = input[j];
+ }
+
+ reference_sort(reference, SIZE);
+ bh_sort_shell(input, SIZE, sizeof(int), int_equal);
+ bh_unit_assert(memcmp(reference, input, sizeof(input)) == 0);
+
+ qsort(reference, SIZE, sizeof(int), int_equal);
+ bh_unit_assert(memcmp(reference, input, sizeof(input)) == 0);
+
+ }
+
+ return 0;
+}
+
+static int sort_heap(void)
+{
+ int input[SIZE], reference[SIZE];
+ size_t i, j;
+
+ for (i = 0; i < TEST_RUNS; i++)
+ {
+ for (j = 0; j < SIZE; j++)
+ {
+ input[j] = rand() % MAX_VALUE;
+ reference[j] = input[j];
+ }
+
+ reference_sort(reference, SIZE);
+ bh_sort_heap(input, SIZE, sizeof(int), int_equal);
+ bh_unit_assert(memcmp(reference, input, sizeof(input)) == 0);
+
+ qsort(reference, SIZE, sizeof(int), int_equal);
+ bh_unit_assert(memcmp(reference, input, sizeof(input)) == 0);
+ }
+
+ return 0;
+}
+
+static int sort_intro(void)
+{
+ int input[SIZE], reference[SIZE];
+ size_t i, j;
+
+ for (i = 0; i < TEST_RUNS; i++)
+ {
+ for (j = 0; j < SIZE; j++)
+ {
+ input[j] = rand() % MAX_VALUE;
+ reference[j] = input[j];
+ }
+
+ reference_sort(reference, SIZE);
+ bh_sort_intro(input, SIZE, sizeof(int), int_equal);
+ bh_unit_assert(memcmp(reference, input, sizeof(input)) == 0);
+
+ qsort(reference, SIZE, sizeof(int), int_equal);
+ bh_unit_assert(memcmp(reference, input, sizeof(input)) == 0);
+
+ }
+
+ return 0;
+}
+
+static int heap(void)
+{
+ int heap[SIZE], value;
+ size_t i, j;
+
+ for (i = 0; i < TEST_RUNS; i++)
+ {
+ for (j = 0; j < SIZE; j++)
+ heap[j] = rand() % MAX_VALUE;
+
+ bh_heap_make(heap, SIZE, sizeof(int), int_equal);
+ bh_unit_assert(check_heap(heap, SIZE) == 0);
+ }
+
+ for (i = 0; i < TEST_RUNS; i++)
+ {
+ for (j = 0; j < SIZE; j++)
+ {
+ value = rand() % MAX_VALUE;
+ if (i == 0)
+ {
+ heap[j] = value;
+ bh_heap_insert(NULL, heap, j, sizeof(int), int_equal);
+ }
+ else
+ {
+ bh_heap_insert(&value, heap, j, sizeof(int), int_equal);
+ }
+ }
+
+ bh_unit_assert(check_heap(heap, SIZE) == 0);
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ (void)argc;
+ (void)argv;
+
+ bh_unit_add("partition", partition);
+ bh_unit_add("sort_insert", sort_insert);
+ bh_unit_add("sort_shell", sort_shell);
+ bh_unit_add("sort_heap", sort_heap);
+ bh_unit_add("sort_intro", sort_intro);
+ bh_unit_add("sort", sort);
+ bh_unit_add("heap", heap);
+
+ return bh_unit_run();
+}