import java.util.Arrays;
public class MoreRecursionExamples {

    private static String intArrayToString( int[] a ) {
        String ret = "[";
        if (a.length >= 1)
            ret += Integer.toString(a[0]);
        for (int i = 1; i < a.length; i++)
            ret += "," + Integer.toString(a[i]);
        ret += "]";
        return ret;
    }
    
    private static String makeSpaces(int n) {
        String ret = "";
        for (int i = 0; i < n; i++) 
            ret += " ";
        return ret;
    }

    private static boolean binarySearch( int[] a, int start, int stop, int goal ) {
        if (start == stop)
            return a[start] == goal;
        int halfway = (stop+start)/2;
        if (a[halfway] == goal)
            return true;
        if (a[halfway] > goal)
            return binarySearch( a, start, halfway, goal );
        else
            return binarySearch( a, halfway+1, stop, goal );
    }    

    public static boolean binarySearch( int[] a, int goal ) {
        if (a.length == 0)
            return false;
        return binarySearch( a, 0, a.length-1, goal );
    }

    // merge and make a new array.
    public static int[] merge( int[] a, int[] b ) {
        //System.out.println( "merging " + intArrayToString(a) + " and " + intArrayToString(b) );
        int[] merged = new int[a.length+b.length];
        int ai = 0, bi = 0, mi = 0;
        for (mi = 0; mi < merged.length; mi++) {
            if (ai == a.length || bi == b.length)
                break;
            if (a[ai] <= b[bi]) {
                merged[mi] = a[ai];
                ai++;
            }
            else {
                merged[mi] = b[bi];
                bi++;
            }
        }
        // take care of the left-overs
        if (ai < a.length) {
            for (; mi < merged.length; mi++) {
                merged[mi] = a[ai];
                ai++;
            }
        }
        else if (bi < b.length) {
            for (; mi < merged.length; mi++) {
                merged[mi] = b[bi];
                bi++;
            }
        }
        return merged;
    }
    
    // merge a[start] .. a[mid]
    //  with a[mid+1] .. a[end]
    public static void merge( int[] a, int start, int mid, int end ) {
        int[] merged = merge( Arrays.copyOfRange( a, start, mid+1 ),
                              Arrays.copyOfRange( a, mid+1, end+1 ) );
        System.arraycopy( merged, 0, a, start, merged.length );
    }
    
    // merge a[start] .. a[mid]
    //  with a[mid+1] .. a[end]
    public static void annotatedMerge( int[] a, int start, int mid, int end, int level ) {
        int[] left = Arrays.copyOfRange( a, start, mid+1 );
        int[] right = Arrays.copyOfRange( a, mid+1, end+1 );
        int[] merged = merge( left, right );
        System.out.println( makeSpaces(level) + intArrayToString(left) + intArrayToString(right) + " ---> " + intArrayToString(merged) );
        System.arraycopy( merged, 0, a, start, merged.length );
    }

    // sorts (in place) a[start]..a[end]
    private static void mergeSort(int[] a, int start, int end) {
        if (start == end)
            return;
            
        mergeSort(a, start, (start+end)/2 );
        mergeSort(a, (start+end)/2+1, end );
        merge( a, start, (start+end)/2, end );
    }

    //merges (in place) a[start]..a[end]
    public static void mergeSort(int[] a) {
        mergeSort(a, 0, a.length-1);
    }
    
    //merges (in place) a[start]..a[end]
    private static void annotatedMergeSort(int[] a, int start, int end, int level) {
 //       System.out.println( makeSpaces(level) + "mergeSort( a, " + start + ", " + end + "), i.e. " + 
   //     intArrayToString(Arrays.) );
                if( start >= end ) return;
    
        annotatedMergeSort(a, start, (start+end)/2, level+1);
        annotatedMergeSort(a, (start+end)/2+1, end, level+1);
        annotatedMerge(a, start, (start+end)/2, end, level);
    }
    
    public static void annotatedMergeSort(int[] a) {
        annotatedMergeSort(a,0,a.length-1,0);
    }

    public static void testBinarySearch(  ) {
        int[] a = {1,2,3,4,5}; // odd number of elems, goal in middle
        int[] b = {1,3,4,5}; // even number of elems
        int[] c = {3,4,5,6,7}; // odd number of elems, goal in beginning
        int[] d = {3,4,5,6,7,8}; // even number of elems, goal in beginning
        int[] e = {-1,0,1,2,3}; // odd number of elems, goal at end
        int[] f = {0,1,2,3}; // even number of elems, goal at end
        int[] g = {1,2,4,5,6}; // odd number of elems, goal missing
        int[] h = {1,2,4,5}; // even number of elems, goal missing
        int[] i = {}; // empty list
        System.out.println( "binarySearch(a,3) returns " + binarySearch(a,3) );
        System.out.println( "binarySearch(b,3) returns " + binarySearch(b,3) );
        System.out.println( "binarySearch(c,3) returns " + binarySearch(c,3) );
        System.out.println( "binarySearch(d,3) returns " + binarySearch(d,3) );
        System.out.println( "binarySearch(e,3) returns " + binarySearch(e,3) );
        System.out.println( "binarySearch(f,3) returns " + binarySearch(f,3) );
        System.out.println( "binarySearch(g,3) returns " + binarySearch(g,3) );
        System.out.println( "binarySearch(h,3) returns " + binarySearch(h,3) );
        System.out.println( "binarySearch(i,3) returns " + binarySearch(i,3) ); 
    }  
    
    public static void testMergeSort() {
//         int[] a1 = {1,3,5};
//         int[] b1 = {2,4,6};
//         int[] c1 = merge( a1, b1 );
//         System.out.println( "testing merge v1" );
//         System.out.println( intArrayToString(c1) );
//         
//         int[] d1 = {1,3,5,2,4,6};
//         merge( d1, 0, 2, 5 );
//         System.out.println( "testing merge v2" );
//         System.out.println( intArrayToString(d1) );
//         
//     
//         System.out.println( "testing merge sort" );
//         int[] a = {4,1,5,2};
//         System.out.print( intArrayToString(a) + "--->" );
//         mergeSort(a);
//         System.out.println( intArrayToString(a) );
//         
        System.out.println( "Merge sort" );
        int[] b = {4,1,5,2};
        System.out.println( "At beginning: " + intArrayToString(b) );
        annotatedMergeSort(b);
        System.out.println( "At end: " + intArrayToString(b) );
    }      
    
    public static void main( String[] args ) {
        testBinarySearch();
        //testMergeSort();
    }
}