import java.util.Random;


public class Merger {
	
	public static final int SIZE = 20;
	
	// This sorts the elements of A between index first and index last
	// using temp as temporary storage for the merger.
	private static <E extends Comparable<? super E>> 
							void MergeSort(E[] A, int first, int last, Object[] temp) {
		if (first < last) {
			int mid = (first+last)/2;
			MergeSort(A, first, mid, temp);
			MergeSort(A, mid+1, last,temp);
			merge(A, first, mid, last, temp);
		}
		
	}
	
	// Merges the elements of A between first and mid, and the elements between mid+1 and last
	// into one sorted list
	public static <E extends Comparable<? super E>> 
						void merge(E[] A, int first, int mid, int last, Object[] temp) {
		int p1 = first;
		int p2 = mid+1;
		int p = first;
		while (p1 <=mid && p2 <= last) {
			if (A[p1].compareTo(A[p2]) < 0){
				temp[p] = A[p1];
				p1 += 1;
			}
			else {
				temp[p] = A[p2];
				p2 += 1;
			}
			p += 1;		
		}
		while (p1 <= mid) {
			temp[p] = A[p1];
			p1 += 1;
			p += 1;
		}
		while(p2 <= last) {
			temp[p] = A[p2];
			p2 += 1;
			p += 1;
		}
		for (int i = first; i <= last; i++)
			A[i] = (E) temp[i];
		
	}

	public static <E extends Comparable<? super E>>void MergeSort(E[] array ) {
		Object[] temp = new Object[array.length];
		MergeSort(array, 0, array.length-1, temp);
	}
	
	static void build(Object[] a) {
		Random rand = new Random();
		
		for (int i = 0; i < a.length; i++)
			a[i] = rand.nextInt(10*SIZE);
	}

	static void Print( String text, Object[] a) {
		if (a.length <= 10) {
			System.out.printf( "%s: ", text);
			for(int i = 0; i < a.length; i++ ) 
				System.out.printf( "%d   ", a[i]);
			System.out.println();
		}
		else {
			System.out.println( text );
			for (int i = 0; i < a.length; i++) 
				System.out.println( String.format( "   %d", a[i]) );
		}
	}
	
	public static void main(String[] args) {
		Integer[] data = new Integer[SIZE];
		build( data );
		Print("Original data", data);
		MergeSort(data);
		Print("Sorted data", data);
	}
}
