/** Demo algorithms from the array algorithm design slides. */
public class Algorithms {

    /** Yields: index of first occurrence of c in b[0..b.length-1]
     *         OR b.length if none is found */
    public static int findFirst(int c, int[] b) {
        // Store in i the index of the first c in b[0..]
        int i = 0;
        
        // invariant: c is not in b[0..i-1]
        while (i < b.length && b[i] != c) {
            i= i + 1;
        }
        
        // post: (b[i] == c OR i == b.length) and c is not in b[0..i-1]
        return i;
    }
    
    /** Yields: "c is in the array b"
     *  Precondition: b sorted in nondescending order */
    public static boolean binarySearch(int c, int[] b) {
        // Store in i the value BEFORE beginning of range to search
        int i = 0;
        // Store in j the end of the range to search
        int j = b.length;
        // The middle position of the range
        int mid = (i+j)/2;
        
        // invariant; b[0..i-1] < c, b[j..] >= c 
        while (j > i) {
            if (b[mid] < c) {
                i = mid+1;
            } else { // b[mid] >= c
                j = mid;
            }
            
            // Compute a new middle.
            mid = (i+j)/2;   
        }
        
        // post: i == j and b[0..i-1] < c and b[j..] >= c
        return (i < b.length && b[i] == c);
    }
    
    /** Insertion Sort: sorts the array b in n^2 time */
    public static void isort(int[] b) {
        // inv: b[0..i-1] sorted    
        for(int i = 0; i  < b.length; i = i+1) {
            pushDown(b,i);
        }    
        // post: b[0..b.length-1] sorted
    }
    
    /** Moves the value at position k into its
     *  sorted position in b[0.k-1].
     *  Precondition: b[0..k-1] is sorted */
    public static void pushDown(int[] b, int k) {
        // inv: b[j..k] is sorted
        for(int j = k; j > 0; j = j - 1) {
            if (b[j-1] > b[j]) {
                swap(b,j-1,j);
            }
        }
        // post: b[0..k] is sorted     
    }
    
    /** Selection Sort: sorts the array b in n^2 time */
    public static void ssort(int[] b) {
        // inv: b[0..i-1] sorted    
        for(int i = 0; i  < b.length; i = i+1) {
            int index = minIndex(b,i);
            swap(b,i,index);
        }    
        // post: b[0..b.length-1] sorted
    }
    
    /** Yields: the index of the minimum value in b[h..] */
    private static int minIndex(int[] b, int h) {
        int index = h;
        
        // inv: index position of min in b[h..i]
        for(int i = h+1; i < b.length; i = i+1) {
            if (b[i] < b[index]) {
                index = i;
            }
        }
        
        // post: index position of min in b[h..b.length]
        return index;
    }
    
    /** Quick Sort: sorts the array b in n log n average time */
    public static void qsort(int[] b) {
        qsort(b,0,b.length-1);
    }

    /** Quick Sort: sorts the array b[h..k] in n log n average time  */
    public static void qsort(int[] b, int h, int k) {
        if (k-h < 1) {
            return;
        }
        
        int j = partition(b, h, k);
        // b[h..j-1] <= b[j] <= b[j+1..k]
        // Sort b[h..j-1]  and  b[j+1..k]
        qsort(b, h, j-1);
        qsort(b, j+1, k);
    }
    
    
    // NOTE: This uses a DIFFERENT invariant than the lab
    /** Partitions array b[0..b.length-1] around a pivot x 
     *  Precondition: x is in b[0..b.length-1] 
     *  Yields: the new location of x                      */
    public static int partition(int[] b, int h, int k) {
        // position i is end of first paritition range
        int i = h;
        // position j is BEFORE beginning of second partition range
        int j = k;
        
        // Find the first element in the array.
        int x = b[h];
        
        // invariant: b[h..i] < x, b[j+1..k] >= x
        while (i != j) {
            if (b[i+1] >= x) {
                // Move this to the end of the block.
                swap(b,i+1,j);
                j = j - 1;
            } else { // b[i+1] < x
                swap(b,i,i+1);
                i = i + 1;
            }
        }
        
        // post: b[h..i-1] < x, b[i] is x, and b[i+1..k] >= x
        return i;
    }
    
    /** Dutch National Flag algorithm to arrange the elements of b[h..k] 
     *  and produce a Point object Point (i, j) */
    public static java.awt.Point dnf(int[] b, int h, int k) {
        int t= h;
        int j= k;
        int i= k+1;
        // inv: b[h..t-1] < 0, b[t..i-1] unknown, b[i..j] = 0, and b[j+1..k] > 0 
        while (t < i) { 
            if (b[i-1] < 0) {
                swap(b,i-1,t); 
                t= t+1; 
            } else if (b[i-1] == 0) { 
                i= i-1; 
            } else { 
                swap(b,i-1,j); 
                i= i-1; 
                j= j-1; 
            } 
        }       
        return new java.awt.Point(i, j); 
    } 

    
    /** Procedure swaps b[h] and b[k] */
    public static void swap (int[] b, int h, int k) {
        int temp= b[h];
        b[h]= b[k];
        b[k]= temp;
    }
    
    /** Scrambles the array to resort again */
    public static void scramble(int[] b) {
        // inv: b[0..i-1] is scrambled
        for(int i = 0; i < b.length; i = i+1) {
            int size = b.length-i;
            int pos  = (int)(Math.random()*size);
            swap(b,i,i+pos);
        }
        // post: b[0..b.length] is scrambled    
    }
    
    
}