package thisandthat;

/*************************************************************************
 *  Compilation:  javac MergeSortX.java
 *  Execution:    java MergeSortX N
 *  
 *  Generate N pseudo-random numbers between 0 and 1 and mergesort them.
 *
 *************************************************************************/

public class MergeSortX {
    private final static int CUTOFF = 6;  // cutoff to insertion sort

    private static void mergeAB(Comparable<?>[] c, int cl, int cr,
                                Comparable<?>[] a, int al, int ar, 
                                Comparable<?>[] b, int bl, int br ) {
        int i = al, j = bl;
        for (int k = cl; k < cr; k++) {
            if      (i >= ar)          c[k] = b[j++];
            else if (j >= br)          c[k] = a[i++];
            else if (less(b[j], a[i])) c[k] = b[j++];   // make stable
            else                       c[k] = a[i++];
        }
    }

    private static void mergesortAB(Comparable<?>[] a, Comparable<?>[] b, int l, int r) {
        if (r <= l + CUTOFF) { 
            insertionsort(a, l, r);
            return;
        }
        int m = (r + l) / 2;
        mergesortAB(b, a, l, m);  
        mergesortAB(b, a, m, r);

        if (!less(b[m], b[m-1])) {
            for (int i = l; i < r; i++) a[i] = b[i];
            return;
        }

        mergeAB(a, l, r, b, l, m, b, m, r);
    }

    public static void sort(Comparable<?>[] a) {
        Comparable<?>[] aux = new Comparable[a.length];
        for (int i = 0; i < a.length; i++) aux[i] = a[i];
        // Comparable[] aux = a.clone();
        mergesortAB(a, aux, 0, a.length);  
    }


    // sort from a[left] to a[right - 1]
    private static void insertionsort(Comparable<?>[] a, int left, int right) {
        for (int i = left; i < right; i++)
            for (int j = i; j > left; j--)
                if (less(a[j], a[j-1])) exch(a, j, j-1);
                else break;
    }


    // exchange a[i] and a[j]
    private static void exch(Comparable<?>[] a, int i, int j) {
        Comparable<?> swap = a[i];
        a[i] = a[j];
        a[j] = swap;
    }

    // is a[i] < a[j]?
    private static boolean less(Comparable<?> a, Comparable<?> b) {
        return (((Double)a).compareTo((Double)b) < 0);
    }

   /***********************************************************************
    *  Check if array is sorted - useful for debugging
    ***********************************************************************/
    private static boolean isSorted(Comparable<?>[] a) {
        for (int i = 1; i < a.length; i++)
            if (less(a[i], a[i-1])) return false;
        return true;
    }

   /***********************************************************************
    *  Show results
    ***********************************************************************/
    public static void show(Comparable<?>[] a) {
        for (int i = 0; i < a.length; i++)
            System.out.println(a[i]);
    }




    // test client
    public static void main(String[] args) {
    	args = new String[]{"50"};
        int N = Integer.parseInt(args[0]);

        // generate N random real numbers between 0 and 1
        long start = System.currentTimeMillis();
        Double[] a = new Double[N];
        for (int i = 0; i < N; i++)
            a[i] = Math.random();
        long stop = System.currentTimeMillis();
        double elapsed = (stop - start) / 1000.0;
        System.out.println("Generating input:  " + elapsed + " seconds");

        // sort them
        start = System.currentTimeMillis();
        sort(a);
        stop = System.currentTimeMillis();
        elapsed = (stop - start) / 1000.0;
        System.out.println("MergeSortX: " + elapsed + " seconds");
        System.out.println(isSorted(a));

        if (N < 100) show(a);
    }
}

