/**
 * Copyright © 2016 Xavier G. <xavier@kindwolf.org>
 * This work is free. You can redistribute it and/or modify it under the
 * terms of the Do What The Fuck You Want To Public License, Version 2,
 * as published by Sam Hocevar. See the COPYING file for more details.
 */

void array_sort(unsigned int **array, unsigned int count);
#define ARRAY_SORT 1
#include "common.c"

#define min(a, b) ((a < b) ? (a) : (b))

/*
 * Implementation of merge sort:
 */
void array_sort(unsigned int **array, unsigned int count) {
	unsigned int width, i, left_i, right_i, left_end, right_end, j;
	unsigned int *current, *other, *swap;

	/* Do nothing unless there are at least two items to sort: */
	if (count < 2) return;

	/* Although in-place (or more precisely, in-place-like) variants exist,
	merge sort is not an in-place algorithm per se. */
	current = *array;
	/* Reserve enough space to copy the whole source array; indeed, merge sort's
	worst case space complexity is O(n). */
	other = malloc(sizeof(unsigned int) * count);

	/* We start with an array of n sorted, 1-item arrays. */
	for (width = 1; width < count; width *= 2) {
		VERBOSE(1, "  Merging pairs of sorted, %u-item(s) arrays, \n", width);
		for (i = 0; i < count; i += 2 * width) {
			left_i    = i;
			right_i   = min(left_i + width,      count - 1);
			left_end  = min(left_i + width - 1,  count - 1);
			right_end = min(right_i + width - 1, count - 1);
			VERBOSE(2, "    Merging [%u-%u] and [%u-%u]\n", left_i, left_end, right_i, right_end);

            /* Spare a few comparisons and assignments if there is only one item: */
            if (left_i == right_end) {
                VERBOSE(3, "      Simply copying [%u] here\n", left_i);
                other[left_i] = current[left_i];
                continue;
            }

            /* Do the same if there is actually only one pair: */
            if (left_end == right_end) {
                VERBOSE(3, "      Simply copying [%u-%u] here\n", left_i, left_end);
                for (j = left_i; j <= left_end; ++ j) {
                    other[j] = current[j];
                }
                continue;
            }

			for (j = left_i; j <= right_end; ++ j) {
				/* Pick from the left array... */
				if (
					left_i <= left_end && /* if still available, and if... */
					(
						right_i > right_end || /* the right one was emptied... */
						current[left_i] <= current[right_i] /* or the left value is smaller. */
					)
				) {
					other[j] = current[left_i];
					++ left_i;
				}
				else {
					other[j] = current[right_i];
					++ right_i;
				}
			}
		}
		/* Swap the roles of our arrays: */
		swap = current;
		current = other;
		other = swap;
	}
    *array = current;
}