/** Contains static methods for sorting, together with the methods 
 *  that they use. Included are:
 *    
 *    min, to find the minimum of b[h..k].
 * 
 *    insertValue, to insert a value into its sorted position in b[h..k-1].
 *
 *    linearSearch, to find a value in an array.
 *
 *    partition0, used in quicksort, as done in class.
 *
 *    partition, used in quicksort, done a bit more efficiently.
 *
 *    medianOf3, used in quicksort.
 *
 *    copy, to return a copy of b[h..k].
 *
 *    merge, used in mergesort.
 *
 *    binarySearch, binary search in an array.
 *
 *    selectionSort, to sort an array.
 *
 *    selectionSort1, to sort an array.
 *
 *    insertionSort, to sort an array.
 *
 *    mergesort, to sort an array recursively.
 *
 *    quicksort0, the basic quicksort algorithm.
 *
 *    quicksort, quicksort changed to fix inefficiencies.
 *
 *    Dutch National Flag.
 *
 *    toString, to create a String that contains elements of an array
 *    (alternatively, import java.util.Arrays and use Arrays.toString).
 */
public class Sorting  {
    
    /** Yields: the position of the minimum value of b[h..k].
     *  Precondition: h <= k. */
    public static int min(int[] b, int h, int k) {
        int p= h; 
        int i= h;
        // inv: b[p] is the minimum of b[h..i], i.e.
        //
        //    h-------p------------i---------k
        // b | b[p] is min of this  |    ?    |
        //    --------------------------------
        
        while (i!= k) {
            i= i+1;
            if (b[i] < b[p]) {
              p= i;
            }
        }
        return p;
    }
    
    /** Sort b[h..k]  --put its elements in ascending order.
     *  Precondition: b[h..k-1] is sorted, and b[k] contains a value. */
    public static void insertValue(int[] b, int h, int k) {
        int v= b[k];
        int i= k;
        /* inv P: (1) Placing v in b[i] makes b[h..k] a
                      permutation of its initial value
         (2) b[h..k] with b[i] removed is initial b[h..k-1]
         (3) v < b[i+1..k]
         */
        while ((i != h) && v < b[i-1]) {
            b[i]= b[i-1];
            i= i-1;
        }
        b[i]= v;
    }
    
    /** Yields: index of x in b; or the length of b if x is not in b. */
    public static int linearSearch(int[] b, int x) {
        // invariant: x is not in b[0..i-1]
        int i;
        for (i= 0; i != b.length && b[i] != x; i= i+1){}
        return i;
    }
    
    
    /** Permute b[h..k] and return integer j satisfying R:<br><br>
     *
     *     b[h..j-1] <= b[j] = x <= b[j+1..k],<br><br>
     *
     *  where x stands for the value initially in b[h].
     *  Precondition: b[h..k] has at least three elements.
     */
    public static int partition0(int[] b, int h, int k) {
        // 
        //          h---------------------------k
        // pre:  b |x|     ?                     |  for some x
        //          -----------------------------
        // 
        //          h-------------j-------------k
        // post: b |   <= x      |x|     >= x    |
        //          -----------------------------
        int j= h;
        int i= k;
        // inv P: b[h..j-1] <= b[j] = x <= b[i+1..k], i.e.
        //  
        //          h---------j-------i------------k
        // post: b |   <= x  |x|  ?    |    >= x    |
        //          --------------------------------
        while (j < i) {
            if (b[j+1] <= b[j]) {
                int temp= b[j]; b[j]= b[j+1]; b[j+1]= temp;
                j= j+1;
            } else {
                int temp= b[j+1]; b[j+1]= b[i]; b[i]= temp;
                i= i-1;
            }
        }
        
        return j;
    }
    
    /** Permute b[h..k] and return integer j satisfying R:<br><br>
     *
     *     b[h..j-1] <= b[j] = x <= b[j+1..k]<br><br>
     *
     *  where x stands for the value initially in b[h].
     *  Precondition: b[h..k] has at least three elements. */
    public static int partition(int[] b, int h, int k) {
        int j;
        // Truthify R1: b[h+1..j] <= b[h] = x <= b[j+1..k];
        int i= h+1; j= k;
        // inv P: b[h+1..i-1] <= b[h] = x <= b[j+1..k], i.e.
        //  
        //
        //    h---------i------j----------k
        // b |x| <= x  |    ?   |  >= x    |
        //    -----------------------------
        while (i <= j) {
            if (b[i] < b[h]) {
                i= i+1;
            }
            else if (b[j] > b[h]) {
                j= j-1;
            } else {// b[j] < x < b[i]
                int t1= b[i]; b[i]= b[j]; b[j]= t1;
                i= i+1; j= j-1;
            }
        }
        int t= b[h]; b[h]= b[j]; b[j]= t;
        // R
        return j;
    }
    
    /** Permute b[h], b[(h+k)/2], and b[k] to put their median in b[h]. */
    public static void medianOf3(int[] b, int h, int k) {
        int e= (h+k)/2;  // index of middle element of array
        int m;           // index of median
        // Return if b[h] is median; else store index of median in m
        if (b[h] <= b[e]) {
            if (b[h] >= b[k]) return;
            // b[h] is smallest of the three
            if (b[e] <= b[k]) {
                m= e;
            } else {
                m= k;
            }
        } else {
            if (b[h] <= b[k]) return;
            // b[h] is largest of the three
            if (b[e] <= b[k]) {
                m= k;
            } else {
                m= e;
            }
        }
        int t= b[h]; b[h]= b[m]; b[m]= t;
    }
    
    /** Yields: a copy of array segment b[h..k]. */
    public static int[] copy(int[] b, int h, int k) {
        int[] c= new int[k+1-h];
        // inv: b[h..i-1] has been copied to c[0..i-h-1]
        for (int i= h; i != k+1; i= i+1) {
            c[i-h]= b[i];
        }
        return c;
    }
    
    
    /** Sort b[h..k]  --put its elements in ascending order.
     *  Precondition: Segments b[h..e] and b[e+1..k] are already sorted.
     */
    public static void merge (int b[], int h, int e, int k) {
        int[] c= copy(b,h,e);
        // c is a copy of original b[h..e]
        int i= h; int j= e+1; int m= 0;
        /* inv: b[h..i-1] contains its final, sorted values
           b[j..k] remains to be transferred
           c[m..e-h] remains to be transferred
         */
        for (i= h; i != k+1; i= i+1) {
            if (j <= k && (m > e-h || b[j] <= c[m])) {
                b[i]= b[j]; j= j+1;
            } else {
                b[i]= c[m]; m= m+1;
            }
        }
    }
    
    /** Yields: the index i that satisfies R: b[i] <= x < b[i+1].
     *  Precondition: b is in non-descending order. */
    public static int binarySearch(int[] b, int x) {
        int i= -1; int j= b.length;
        // inv: b[0..i] <= x < b[j..] and -1 <= i < j <= b.length, i.e.
        
        //          0--------i----------j---------- b.length
        //       b |     <=x  |    ?   |   >x      |
        //          -------------------------------
        while (j != i+1) {
            int e= (i+j)/2;
            // -1 <= i < e < j <= b.length
            if (b[e] <= x) {
                i= e;
            } else {
                j= e;
            }
        }
        return i;
    }
    
    /** Sort b: put its elements in ascending order. */
    public static void selectionSort(int[] b) {
        int j= 0;
        // inv P:  b[0..j-1] is sorted and b[0..j-1] <= b[j..], i.e.
        
        //          0---------------j--------------- b.length
        // inv : b |  sorted, <=   |    >=          |
        //          --------------------------------
        
        while (j != b.length) {
            int p= min(b, j, b.length-1);
            // b[p] is minimum of b[j..b.length-1]
            
            // Swap b[j] and b[p]
            int t= b[j]; b[j]= b[p]; b[p]= t;
            j= j+1;
        }
    }
    
    /** Sort b: put its elements in ascending order. */
    public static void selectionSort1(int[] b) {
        int j= 0;
        // inv P: b[0..j-1] is sorted and b[0..j-1] <= b[j..], i.e.
        
        //          0---------------j--------------- b.length
        // inv : b |  sorted, <=   |    >=          |
        //          --------------------------------
        
        while (j != b.length) {
            // Put into p the index of smallest element in b[j..]
            int p= j; 
            for (int i= j+1; i != b.length; i++) {
                if (b[i] < b[p])  {
                    p= i;
                }
            }
            
            // Swap b[j] and b[p]
            int t= b[j]; b[j]= b[p]; b[p]= t;
            j= j+1;
        }
    }
    
    /** Sort b[h..k]: put its elements in ascending order. */
    public static void insertionSort(int[] b, int h, int k) {
        // inv: h <= j <= k+1  and  b[h..j-1] is sorted, i.e.
        
        //          h---------------j--------------k
        // inv : b |  sorted,      |     ?          |
        //          --------------------------------
        
        for (int j= h; j <= k; j= j+1) {
            // Sort b[0..j], given that b[0..j-1] is sorted
            insertValue(b,0,j);
        } 
    }
    
    /** Sort b[h..k]: put its elements in ascending order. */
    public static void mergeSort(int[] b, int h, int k) {
        if (h >= k) {
            return;
        }
        
        int e= (h+k)/2;
        mergeSort(b, h, e);   // Sort b[h..e]
        mergeSort(b, e+1, k); // Sort b[e+1..k]
        merge(b, h, e, k);    // Merge the 2 segments
    }
    
    /** Sort b[h..k]  --put its elements in ascending order. */
    public static void quickSort0(int[] b, int h, int k) {
        if (k+1-h < 2) {
            return;
        }
        
        int j= partition(b,h,k);
        // b[h..j-1] <= b[j] <= b[j+1..k]
        quickSort0(b,h,j-1);
        quickSort0(b,j+1,k);
    }
    
    /** Sort b[h..k]: put its elements in ascending order. */
    public static void quickSort(int[] b, int h, int k) {
        tailRecursionLoop: while (true) {         
            if (k+1-h < 10) {
                insertionSort(b,h,k);
                return;
            }
            medianOf3(b,h,k);
            
            // b[h] is between b[(h+k)/2] and b[k]
            int j= partition(b,h,k);
            // b[h..j-1] <= b[j] <= b[j+1..k], i.e.
            //
            //    h---------j---------k
            // b |   <= x  |x|  >= x   |   for some x
            //    ---------------------
            if (j-h <= k-j) {
                quickSort(b,h,j-1);
                // quicksort(b,j+1,k);
                h= j+1;
                continue tailRecursionLoop;
            }
            else {
                quickSort(b,j+1,k);
                // quicksort(b,h,j-1);
                k= j-1;
                continue tailRecursionLoop;
            }
        } // tailRecursionLoop
    }
    
    /** Swap the values of b so that the negative ones are
     *  first, then the 0's, and finally the positive ones.
     *  In the original problem, the negative values are red
     *  balls, 0's are white balls, positive values are blue balls*/
    public static void DutchNationalFlag(int[] b) {
        //        0------------------------------------ b.length
        // pre: b|              ?                      |
        //        -------------------------------------
        // 
        //        0------------------------------------ b.length
        // post:b|   <0    |    = 0     |    >0        |
        //        -------------------------------------
        
        int h= 0;
        int k= 0;
        int t= b.length-1;
        
        //        0-------h-------k------t------------- b.length
        // inv :b|  <0   |  = 0  |   ?    |    >0      |
        //        -------------------------------------
        
        while (k <= t) {
            if (b[k] < 0) {
                int temp= b[k]; b[k]= b[h]; b[h]= temp;
                h= h+1;  k= k+1;
            } else if (b[k] == 0) {
                k= k+1;
            } else {
                int temp= b[k]; b[k]= b[t]; b[t]= temp;
                t= t-1; 
            }
        }
        
    }
    
    /** Yields: values of b, separated by ", " and with
     *  the whole list delimited by "[" and "]".
     */
    public static String toString(int[] b) {
        String res= "[";
        // inv: res contains b[0..k-1], with "[" at
        //      beginning and values separated by ", "
        for (int k= 0; k != b.length; k= k+1) {
            if (k != 0)
                res= res + ", ";
            res= res + b[k];
        }
        return res + "]";
    }
    
}