// MergeSort.java
// Merge Sort Parallel Test...
// Bryan Chadwick : Sep. 2008

// All the needed functionality to run simple parallel tests on
//   multiple processors.
// Real (pretty good) results with as little as 20,000 elements:
//   java -Xss2M MergeSort 20000 
// Where -Xss2M tells Java to use 2Mb thread stacks (functional lists
//   are tough without proper tail recursion...)
public class MergeSort{
    static void p(String s){ System.out.println(s); }

    // Main Method
    public static void main(String[] s){
        int len = s.length>0?Integer.parseInt(s[0]):2000;
        L<Integer> l = rand(new id(), len);
        p(" Length = "+len);

        // Sequential, Parallel, and Array based Sorts...
        p("  Sort: "+new Sorter<Integer>().timesort(l));
        p(" PSort: "+new PSorter<Integer>().timesort(l));
        p(" ASort: "+new ASorter<Integer>().timesort(l, new Integer[len]));

    // Create a D from an integer... so we can build random lists
    //   of different kinds of things for testing
    static abstract class Creator<D>{ abstract D make(int i); }

    // ID for Integers
    static class id extends Creator<Integer>{
        Integer make(int i){ return i; }
    //  Make a List with a Creator and integer parameters
    static <D extends Comparable<D>> L<D> make(D ... xs){ return make(xs,0,xs.length); }
    static <D extends Comparable<D>> L<D> make(D[] xs, int i, int len){
        return (i==len)?new L<D>():make(xs,i+1,len).push(xs[i]);
    // Make a List with a Creator and integer parameters
    static <D extends Comparable<D>> L<D> make(Creator<D> c, int ... xs){
        return make(c,xs,xs.length-1);
    // Same here... but from an array, where 'i' is the index currently
    //   being worked on (count down)
    static <D extends Comparable<D>> L<D> make(Creator<D> c, int[] xs, int i){
        return (i < 0)?new L<D>():make(c,xs,i-1).push(c.make(xs[i]));

    // Return a Random integer in [0..19]
    static int randInt(){ return (int)(Math.random()*20); }
    // Make a List with a Creator and random integers
    static <D extends Comparable<D>> L<D> rand(Creator<D> c, int k){
        return (k==0?new L<D>():rand(c,k-1).push(c.make(randInt())));
    // Dynamic Sorter Object
    static class Sorter<D extends Comparable<D>>{
        // Outer Sort Call
        public L<D> sort(L<D> l){ return recsort(l); }
        // Inner Sort Call for recursion
        public L<D> recsort(L<D> l){
            int len = l.length();
            if(len <= 1)return l;
            L.P<D> p = l.split(len/2);
            //p(" recsort["+Thread.currentThread().getId()+"]: "+l);
            return merge(recsort(p.lt), recsort(p.rt));
        // Timing Result class
        class T{
            long mil;
            L<D> lst;
            T(long m, L<D> l){ mil = m; lst = l; }
            public String toString(){ return " MSec: "+mil; }//+" : "+lst; }
        // Caculate Timing Results for List sorting
        public T timesort(L<D> l){
            // Make sure Java doesn't interrupt us...
            long tm = System.currentTimeMillis();
            L<D> r = sort(l);
            // Must be careful about argument evaluation order...
            return new T(System.currentTimeMillis()-tm, r);

    // Sort Using java.util.Arrays
    static class ASorter<D extends Comparable<D>> extends Sorter<D>{
        public T timesort(L<D> l, D[] a){
            a = l.toArray(a);
            long tm = System.currentTimeMillis();
            return new T(System.currentTimeMillis()-tm, make(a));
    // Parallel Sorter version
    static class PSorter<D extends Comparable<D>> extends Sorter<D>{
        // Outer Sort call splits into a seperate thread for the
        //   Left Split (half) of the list.
        public L<D> sort(L<D> l){
            int len = l.length();
            if(len <= 1)return l;
            L.P<D> p = l.split(len/2);
            Thrd lt = new Thrd(p.lt); lt.start();
            L<D> rt = recsort(p.rt);
            p = null;
            return merge(lt.res, rt);
        // Thunkish class to support delayed sorting in a separate thread
        class Thrd extends Thread{
            L<D> lst, res;
            boolean fin = false;
            Thrd(L<D> l){ lst = l; res = null; }
            public void run(){ res = recsort(lst); done(); }
            synchronized void done(){ fin = true; this.notifyAll(); }
            synchronized void waitFor(){ try{ while(!fin)wait(); }catch(InterruptedException e){} }
    // Classic merge method
    static <D extends Comparable<D>> L<D> merge(L<D> l1, L<D> l2){
        if(l1.isEmpty())return l2;
        if(l2.isEmpty())return l1;
        if(l1.first().compareTo(l2.first()) <= 0)
            return merge(l1.rest(), l2).push(l1.first());
        return merge(l1, l2.rest()).push(l2.first());
    // Functional Generic Lists with the typical methods
    static class L<D extends Comparable<D>>{
        // Inner class for Pair of Lists when Splitting
        static class P<D extends Comparable<D>>{
            L<D> lt, rt;
            P(L<D> l, L<D> r){ lt = l; rt = r;}
            public String toString(){ return "<"+lt+" :: "+rt+">"; }
        boolean isEmpty(){ return true; }
        D first(){ throw new RuntimeException("L.first() Bad"); }
        L<D> rest(){ throw new RuntimeException("L.rest() Bad"); }
        int length(){ return 0; }
        L<D> reverse(){ return reverse(new L<D>()); }
        L<D> reverse(L<D> ac){ return ac; }
        L<D> push(D d){ return new C<D>(d, this); }
        L<D> append(L<D> l){ return l; }
        P<D> split(int k){ return split(k,new L<D>()); }
        P<D> split(int k, L<D> ac){ throw new RuntimeException("L.split() Bad"); }
        public String toString(){ return ""; }
        D[] toArray(D[] d){ return toArray(d, 0); }
        D[] toArray(D[] d, int i){ return d; }
    static class C<D extends Comparable<D>> extends L<D>{
        D f;
        L<D> r;
        C(D ff, L<D> rr){ f = ff; r = rr; }
        boolean isEmpty(){ return false; }
        D first(){ return f; }
        L<D> rest(){ return r; }
        int length(){ return 1+r.length(); }
        L<D> reverse(L<D> ac){ return r.reverse(ac.push(f)); }
        L<D> append(L<D> l){ return r.append(l).push(f); }
        P<D> split(int k, L<D> ac){ 
            return k==0?new P<D>(ac.reverse(),this):r.split(k-1, ac.push(f));    
        public String toString(){ return f+" "+r; }
        D[] toArray(D[] d, int i){ d[i] = f; return r.toArray(d, i+1); }