/*
 * mergesort using data generated by RNG.
 *
 * sequential version using Java.
 *
 * command-line arguments specify number of elements to sort, seed for RNG.
 */
package csci3366sample.mergesort;

import java.util.Random;

public class MergeSortSeq {

    /* main method */

    public static void main(String[] args) {

        String usageMessage = "Arguments: numElements seed";

        /* process command-line arguments */
        if (args.length < 2) {
            System.err.println(usageMessage);
            System.exit(1);
        }
        int n = 0;
        int seed = 0;
        try {
            n = Integer.parseInt(args[0]);
            seed = Integer.parseInt(args[1]);
        }
        catch (NumberFormatException e) {
            System.err.println("Arguments must be integers");
            System.exit(1);
        }

        /* generate data */
        Integer[] ints = new Integer[n];
        Random randGen = new Random((long) seed);
        for (int i = 0; i < ints.length; ++i) {
            ints[i] = new Integer(randGen.nextInt());
        }

        /* sort (time this part only) */
        long startTime = System.currentTimeMillis();
        ints = sort(ints);
        long endTime = System.currentTimeMillis();

        /* print parameters */
        System.out.println("Sequential mergesort of " + n + 
                " random integers (seed " + seed + ")");

        /* check whether sort succeeded */
        if (isSorted(ints)) {
            System.out.println("Sort succeeded");
        }
        else {
            System.out.println("Sort failed");
        }

        /* print timing result */
        System.out.println("Time " +
                ((double) (endTime - startTime) / 1000) + " seconds");
    }

    /* check whether array is sorted */
    public static boolean isSorted(Integer[] data) {
        for (int i = 0; i < data.length-1; ++i) {
            if (data[i].compareTo(data[i+1]) > 0)
                return false;
        }
        return true;
    }

    /* main mergesort routine */
    public static Integer[] sort(Integer[] data) { 
        Integer[] temp = mergesort(data, 0, data.length-1);
        return temp;
    }

    /* 
     * recursive mergesort:
     *   sorts data[firstIndex..lastIndex] 
     *   and returns result (in a new array)
     */
    private static Integer[] mergesort(Integer[] data, 
            int firstIndex, int lastIndex) 
    {
        if (firstIndex > lastIndex) {
            return new Integer[0];
        }
        else if (firstIndex == lastIndex) {
            return new Integer[] { data[firstIndex] };
        }
        int midIndex = (firstIndex + lastIndex) / 2;
        Integer[] temp1 = mergesort(data, firstIndex, midIndex);
        Integer[] temp2 = mergesort(data, midIndex+1, lastIndex);
        return merge(temp1, temp2);
    }

    /* merge sorted arrays a1, a2, and return result */
    private static Integer[] merge(Integer[] a1, Integer[] a2) {
        Integer[] result = new Integer[a1.length + a2.length];

        int i1 = 0; /* index into a1 */
        int i2 = 0; /* index into a2 */

        for (int j = 0; j < result.length; ++j) {
            if (i2 >= a2.length) {
                result[j] = a1[i1++];
            }
            else if (i1 >= a1.length) {
                result[j] = a2[i2++];
            }
            else if (a1[i1].compareTo(a2[i2]) <= 0) {
                result[j] = a1[i1++];
            }
            else {
                result[j] = a2[i2++];
            }
        }
        return result;
    }
}