/*
	This program accepts an integer n on the command line, then
	fills an array of size n with random integers.  The integers
	are sorted using merge sort.  The order of the elements is
	verified and a checksum mod 65536 compared to ensure the elements
	were sorted properly.  The time taken to do the sort is printed
	to standard output.

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int *v1, *v2; /* global pointers for merge */

/* prototype for missing heapsort */

void heap_sort (int [], int);

/* this function returns 1 iff v[0..n-1] is sorted */

int is_sorted (int v[], int n) {
	int	i;

	for (i=1; i<n; i++) if (v[i] < v[i-1]) return 0;
	return 1;
}

/* this function returns the sum of all n elements of v mod 65536*/

int sum (int v[], int n) {
	int	i;
	int	s = 0.0;

	for (i=0; i<n; i++) {
		s += v[i];
		s %= 0x10000;
	}
	return s;
}

/* this function merges v[start..middle-1] with v[middle..end-1] */

void merge (int v[], int start, int middle, int end) {
	int	v1_n, 		/* size of first subarray */
		v2_n, 		/* size of second subarray */
		v1_index, 	/* index into v1 */
		v2_index, 	/* index into v2 */
		i;		/* loop counter */

	/* figure out the sizes of the subarrays */

	v1_n = middle - start;
	v2_n = end - middle;

	/* fill v1 and v2 with elements from the subarrays */

	for (i=0; i<v1_n; i++) v1[i] = v[start + i];
	for (i=0; i<v2_n; i++) v2[i] = v[middle + i];

	/* merge the two subarrays back into v */

	v1_index = 0;
	v2_index = 0;
	for (i=0; (v1_index < v1_n) && (v2_index < v2_n); i++) {
		if (v1[v1_index] < v2[v2_index]) 
			v[start + i] = v1[v1_index++];
		else
			v[start + i] = v2[v2_index++];
	}

	/* clean up any leftover elements */

	for (; v1_index < v1_n; i++) v[start + i] = v1[v1_index++];
	for (; v2_index < v2_n; i++) v[start + i] = v2[v2_index++];
}
	
/* this function does mergesort on v[start..end-1] */

void merge_sort (int v[], int start, int end) {
	int	middle;

	if (start == end) return;	/* no elements to sort */
	if (start == end - 1) return;	/* one element; already sorted! */

	/* find the middle */

	middle = (start + end) / 2;

	/* sort the first half */

	merge_sort (v, start, middle);

	/* sort the second half */

	merge_sort (v, middle, end);

	/* put them back together */

	merge (v, start, middle, end);
}


/* main accepts a single command line argument: the number of elements to sort */
int main (int argc, char *argv[]) {
	int	*v, 		/* the array of random elements */
		*w, 		/* the sorted array */
		checksum, 	/* sum of elements in v */
		total;		/* sum of elements in w */
	int	i, n, t0, t1;

	/* complain if the user doesn't give us a number */

	if (argc != 2) {
		fprintf (stderr, "Usage: %s <number-of-items>\n", argv[0]);
		exit (1);
	}

	/* get the number */

	n = atoi (argv[1]);

	/* get enough storage for v, w, and the subarrays for merge sort */

	v = (int *) malloc (n * sizeof (int));
	w = (int *) malloc (n * sizeof (int));
	v1 = (int *) malloc (n * sizeof (int));
	v2 = (int *) malloc (n * sizeof (int));

	/* woops, out of memory */

	if (!v || !w || !v1 || !v2) {
		fprintf (stderr, "memory allocation error!\n");
		exit (1);
	}

	/* fill v with random numbers */

	for (i=0; i<n; i++) v[i] = rand () % 32767;

	/* compute the checksum for later reference */

	checksum = sum (v, n);

	/* copy v to w, preserving the order of v for later use */

	memcpy (w, v, n * sizeof (int));

	/* start the clock */

	t0 = clock ();

	/* sort the array */

	merge_sort (w, 0, n);

	/* stop the clock */

	t1 = clock ();

	/* compute another checksum; it should be the same */

	total = sum (w, n);

	/* print statistics */

	printf ("time for merge sort = %0.6f secs\n", 
		(t1 - t0) / (float) CLOCKS_PER_SEC);
	if (!is_sorted (w, n)) {
		fprintf (stderr, "error! array is not sorted!\n");
		exit (1);
	}
	if (checksum != total) {
		fprintf (stderr, "error! some elements are changed!\n");
		fprintf (stderr, "old sum = %i, new sum = %i\n", 
			checksum, total);
		exit (1);
	}

	/* copy v to w, preserving the order of v for later use */

	memcpy (w, v, n * sizeof (int));

	/* start the clock */

	t0 = clock ();

	/* sort the array */

	heap_sort (w, n);

	/* stop the clock */

	t1 = clock ();

	/* compute another checksum; it should be the same */

	total = sum (w, n);

	/* print statistics */

	printf ("time for heap sort = %0.6f secs\n", 
		(t1 - t0) / (float) CLOCKS_PER_SEC);
	if (!is_sorted (w, n)) {
		fprintf (stderr, "error! array is not sorted!\n");
		exit (1);
	}
	if (checksum != total) {
		fprintf (stderr, "error! some elements are changed!\n");
		fprintf (stderr, "old sum = %i, new sum = %i\n", 
			checksum, total);
		exit (1);
	}

	/* free storage */

	free (v1);
	free (v2);
	free (v);
	free (w);
	exit (0);
}

