import Array;
import Comparator;
import IncomparableException;
import Sortable;
import java.util.Random;

/**
 * Arrays that can be sorted using the amazing Quicksort routine.  Note
 * that we violate Java's method naming conventions because Quicksort is
 * a proper name.
 *
 * @author Samuel A. Rebelsky (general structure)
 * @author Mable Mathemagician
 * @author Myron Mathemagician
 * @version 1.3 of October 1999
 */
public class AltQuicksortable
  extends Array
  implements Sortable
{
  // +--------+--------------------------------------------------
  // | Fields |
  // +--------+

  /** A random number generator for use in picking pivots. */
  Random generator;
  
  // +--------------+--------------------------------------------
  // | Constructors |
  // +--------------+
  
  /**
   * Build a new sortable array of a particular size.
   */
  public AltQuicksortable(int n) {
    super(n);
    generator = new Random();
  } // AltQuicksortable(int)
  
  /**
   * Build a new sortable array with a particular set of elements. 
   */
  public AltQuicksortable(Object[] elements) {
    super(elements);
    generator = new Random();
  } // AltQuicksortable(Object[])
  
  // +-----------------------+-----------------------------------
  // | Methods from Sortable |
  // +-----------------------+
  
  /**
   * Sort an array using Quicksort.  
   * Pre: All elements in the array can be compared to each other.
   * Post: The array is sorted (using the standard meaning).
   */
  public void sort(Comparator compare) 
    throws IncomparableException
  {
    sort(compare, null);
  } // sort(Object[])

  /**
   * Sort an array using Quicksort.  If observer is non-null,
   * prints out pithy notes about the sorting.
   * Pre: All elements in the array can be compared to each other.
   * Post: The array is sorted (using the standard meaning).
   */
  public void sort(Comparator compare, SimpleOutput observer) 
    throws IncomparableException
  {
    Quicksort(0, size()-1, compare, observer);
  } // sort(Object[])

  // +----------------+------------------------------------------
  // | 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 < size()
   * Post: The array is sorted (using the standard meaning).
   */
  protected void Quicksort(int lb, int ub, 
                           Comparator compare,
                           SimpleOutput observer) 
    throws IncomparableException
  {
    if (observer != null) {
      observer.println("lb = " + lb + "; ub = " + ub);
    }
    // Variables
    Object pivot;	// The pivot we select.  Must be part of the subarray.
    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.
    swap(lb,pickPivot(lb,ub));
    // Determine the position of the pivot, while rearranging the array.
    mid = partition(lb,ub,compare);
    // Recurse on nonempty subarrays.
    if (lb<=mid-1) Quicksort(lb,mid-1, compare, observer);
    if (mid+1<=ub) Quicksort(mid+1,ub, compare, observer);
  } // Quicksort
  
  /**
   * Split the subarray 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 parts.  Uses the first element of the array 
   * as the pivot.
   *
   * Based on the partition method given on pp. 54-55 of the 
   * 31 March 1999 draft of Java Plus Data Structures by Nell Dale,
   * Sam Rebelsky, and Chip Weems.  Changes are marked with CHANGED.
   * The most common change was to use get(i) instead of A[i].  Since
   * our comparators lack lessEqual, I needed to implement that with
   * both (lessThan(..) or equal(..)).  At Nell's urging, I also 
   * switched preincrement and predecrement (++x and --x) to
   * postincrement and postdecrement (x++ and x--).  I also changed
   * the swap for last and first to eliminate the test.  I used 
   * this.swap rather than swap.  I added braces around the bodies of
   * loops to make them easier to read.  Finally, since I was required 
   * to use slightly different parameters, I did so.
   *
   * Pre: All the elements in the subarray can be compared.
   * Post: Returns n such that for all i between lb and n inclusive
   *   and j between n+1 and ub inclusive, get(i) <= pivot < get(j).
   */
  protected int partition(int lb, int ub, Comparator compare) 
    throws IncomparableException
  {
    // Our pivot, used to determine "smaller" and "larger" elements.
    Object pivot = this.get(lb);	// CHANGED 
    System.err.println("Pivot: " + pivot);
    // Elements get(lb) .. get(first) are <= pivot.  Since  // CHANGED 
    // the pivot is at the front, we can set first to lb.
    int first = lb;
    // Elements get(last+1) .. get(ub) are > pivot.  Since  // CHANGED
    // we know nothing about the contents of the array, we set 
    // last to the end.
    int last = ub;
    // Keep going until we run out of out-of-place elements.
    while (first < last) {
      // COMMENTS DROPPED
      // Skip over big elements on the right.		// CHANGED
      while ( (compare.lessThan(pivot, get(last))) &&	// CHANGED
              (first < last) ) {			// CHANGED
        System.err.println("Skipping large element: " + get(last));
        last--;						// CHANGED
      }	// while there are big elements on the right	// ADDED
      // COMMENTS DROPPED
      // Skip over small elements on the left.		// CHANGED
      while ( ( compare.lessThan(get(first), pivot) 	// CHANGED
                || compare.equals(get(first), pivot) )	// CHANGED
              && (first < last) ) {
        System.err.println("Skipping small element: " + get(first));
        first++;
      }	// while there are small elements on the left	// ADDED
      // COMMENTS DROPPED.  NEW COMMENT FOLLOWS
      // What do we know?  We stopped the second loop when
      // we hit a larger element or when first equaled last.
      // In either case, we can safely swap first and last.
      System.err.println("Swapping " + get(first) + " and " + get(last));
      this.swap(first,last);				// CHANGED
    } // while

    // COMMENTS DROPPED.
    // The element at postion first is <= pivot.  	// CHANGED
    // Hence, we can safely swap it and the pivot so	// CHANGED
    // that the pivot is in the middle.			// CHANGED
    this.swap(lb,first);				// CHANGED

    // Return the location of the pivot.
    return first;
  } // partition(int, int, Comparator)
  
  /**
   * Pick a pivot in a subarray.
   * Pre: 0 <= lb <= ub < size()
   * Post: returns the index of the pivot (a value between lb and 
   *   ub inclusive).
   */
  protected int pickPivot(int lb, int ub) {
    // Pick a random number.
    int pivloc = generator.nextInt();
    // Make it positive.
    pivloc = Math.abs(pivloc);
    // Reduce it to 0 .. ub-lb.
    pivloc = pivloc % (1+ub-lb);
    // Scale it to lb .. ub.
    pivloc = pivloc + lb;
    // That's it, we're done.
    return pivloc;
  } // pickPivot(int,int)
} // AltQuicksortable

