import SimpleInput;
import SimpleOutput;
import StringArrayInput;
import java.util.Random;
import StringSorter;

/**
 * Tools for sorting lists of strings using Quicksort.  Created 
 * for the answer key for assignment 7 in Grinnell's CSC152 99S.
 *
 * @author Samuel A. Rebelsky
 * @version 1.1 of March 1999
 */
public class QuicksortStrings 
  implements StringSorter
{

  // +--------+--------------------------------------------------
  // | Fields |
  // +--------+

  /** A random number generator, used to pick the pivot. */
  protected Random generator;

  // +--------------+--------------------------------------------
  // | Constructors |
  // +--------------+

  /**
   * Build a new thing that can sort arrays of strings.
   */
  public QuicksortStrings() {
    generator = new Random();
  } // QuicksortStrings()


  // +----------------+------------------------------------------
  // | Public Methods |
  // +----------------+

  /**
   * Sort an array using Quicksort.  Included to support the
   * StringSorter interface.
   */
  public String[] sort(String[] strings) {
    // Make a copy.
    String[] stuff = new String[strings.length];
    for (int i = 0; i < strings.length; ++i) {
      stuff[i] = strings[i];
    }
    // Sort it.
    quickSort(stuff);
    // Return it.
    return stuff;
  } // sort(String[])
   
  /**
   * Sort an array in place using Quicksort.
   * Pre: All elements in the array can be compared to each other.
   * Post: The vector is sorted (using the standard meaning).
   */
  public void quickSort(String[] A) {
    quickSort(A, 0, A.length-1);
  } // quickSort(String[])
 
 
  // +----------------+------------------------------------------
  // | Helper Methods |
  // +----------------+

  /**
   * Sort part of an array using Quicksort.
   * Pre: All elements in the subarray can be compared to each other.
   * Pre: 0 <= lb <= ub < A.length
   * Post: The vector is sorted (using the standard meaning).
   */
  protected void quickSort(String[] A, int lb, int ub) {
    // Variables
    int mid;		// The position of the pivot
    // Base case: size one arrays are sorted.
    if (lb == ub) return;
    // Pick a pivot and put it at the front of the array.  
    putPivotAtFront(A,lb,ub);
    // Determine the position of the pivot, while rearranging the array.
    mid = partition(A, lb, ub);
    // Recurse.
    if (mid-1>=lb) quickSort(A, lb, mid-1);
    if (mid+1<=ub) quickSort(A, mid+1, ub);
  } // quickSort
  
  /**
   * Split the array given by [lb .. ub] into ``smaller'' and
   * ``larger'' elements, where smaller and larger are defined by
   * their relationship to a pivot.  Return the index of the pivot 
   * between those elements.  Uses the first element of the array 
   * as the pivot.
   */
  protected int partition(String[] A, int lb, int ub) {
    //  Use the first element of the subsequence as the pivot value.
    String pivotval = A[lb];
    int l=lb; // Elements [lb..l] are all <= pivotval
    int r=ub; // Elements [r+1..ub] are all > pivotval
    // Keep going until we run out of elements to put in the correct place.
    while (l < r) {
      // At this point, we know that 
      //   (1) l < r
      //   (2) Elements [lb..l] are all <= pivotval
      //   (3) Elements [r+1..ub] are all > pivotval
  
      // Skip over any large elements in the right half
      while ((pivotval.compareTo(A[r]) < 0) && (r > l)) {
        --r;
      }
  
      // At this point, we know that
      //    (1) l <= r (we stop moving r left when we hit l or run out
      //                of large elements)
      //    (2) elements [lb..l] are all <= pivotval (we haven't moved l)
      //    (3) elements [r+1..ub] are all > pivotval (by the for loop)
      //    (4) element r is <= pivotval (we either stopped moving when
      //        we hit such an element or (a) r = l and (b) l indexes such 
      //        an element)
  
      // Skip over any small elements in the left half.
      while ((A[l].compareTo(pivotval) <= 0) && (l < r)) {
        ++l;
      }
  
      // At this point, we know that
      //    (1) l <= r (we stop moving r left when we hit l or 
      //        possibly sooner; we stop moving l right when we hit
      //        r or possibly sooner)
      //    (2) elements [lb..l-1] are all <= pivotval (by the for loop)
      //    (3) elements [r+1..ub] are all > pivotval (we haven't moved r)
      //    (4) element r is <= pivotval (we either stopped moving when
      //        we hit such an element or r = l (and l indexes such an
      //        element)
      //    (5) if l < r then element l is > pivotval (by the for loop)
      //    (6) if l = r then element l is <= pivotval
    
      // Do we have a large element in the left and a small element
      // on the right?
      if (A[l].compareTo(A[r]) > 0) {
        swap(A,l,r);
      } 
    } // while
  
    // At this point, we know that
    //    (1) elements [lb..l] are all <= pivotval 
    //    (2) elements [l+1..ub] are all > pivotval
  
    // Put the pivot in the middle.  Note that at this point, element l is
    // <= pivotval, so this is a safe swap
    swap(A,lb,l);
  
    // And we're done
    return l;
  } // partition(String[], int, int)

  /**
   * Pick a pivot, and put it at the front of the subarray.
   * Pre: The subarray is nonempty.
   * Post: No elements are added.
   * Post: No elements are removed.
   * Post: Elements may have been rearranged.
   */
  protected void putPivotAtFront(String[] A, int lb, int ub) {
    // Determine the length of the subarray.
    int length = (ub - lb) + 1;
    // Pick a random number between 0 and length-1
    int r = Math.abs(generator.nextInt()) % length;
    // And swap
    swap(A,lb,lb+r);   
  } // putPivotAtFront(String[], int, int)

  /**
   * Swap two elements of an array.
   * Pre: Both indices are in the array.
   * Post: Afterwards, the two elements have been swapped.
   */
  protected void swap(String[] A, int x, int y) {
    String tmp = A[x];
    A[x] = A[y];
    A[y] = tmp;
  } // swap(String[], int, int)

} // class QuicksortStrings

