import StringSorter;
import SimpleOutput;
import QuicksortStrings;
import MergeSortStrings;
import java.util.Random;

/**
 * Test a sorting routine.  Fun fun fun.  Created as part of the
 * answer key for assignment 7 of Grinnell College's CSC152 99S.
 * This can either be used as a helper class, in which case you
 * need to create a StringSorter and pass it to the constructor, 
 * or as the main class, in which case you need to specify the 
 * sorting method on the command line (quick or merge).
 *
 * The technique we use for sorting is to start with a sorted
 * list, mix the list up, sort it, and see if we got the same
 * list.  We do it again and again and again, for lists of
 * varying sizes.
 *
 * @author Samuel A. Rebelsky
 * @version 1.0 of March 1999
 */
public class SortTester {

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

  /** The sorting method we use. */
  protected StringSorter sorter;

  /** The maximum size of list to try to sort. */
  protected int listSize;

  /** The number of times we try to sort each list. */
  protected int repetitions;

  /** A string of many A's.  Used to generate sample strings. */
  protected String As;

  /** Are we testing our tester? */
  protected boolean TESTING = false;

  /** A random number generator. */
  protected Random generator;


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

  /**
   * Build a tester that uses a default maximum size and
   * number of repetitions.
   */
  public SortTester(StringSorter sorter) {
    this(sorter, 20, 10);
  } // SortTester(StringSorter)

  /**
   * Build a tester that uses a specified sorting routine,
   * maximum list size, and number of repetitions.
   */
  public SortTester(StringSorter sorter, int size, int reps) {
    this.sorter = sorter;
    this.listSize = size;
    this.repetitions = reps;
    this.As = "AAAAA";
    this.generator = new Random();
  } // SortTester(StringSorter, int, int)


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

  /**
   * Run the tests, reporting the results.
   */
  public void runTests(SimpleOutput out) {
    String[] sorted;	// A sorted array
    String[] mixedup;	// A "mixed up" version
    String[] resorted;	// That array, resorted

    // For each list size
    for (int size = 1; size <= listSize; ++size) {
      // Build a sorted list of the appropriate size
      sorted = makeSortedList(size);
      // Print some information
      out.println();
      out.println("Checking list of size " + size);
      // Print it out when testing
      if (TESTING) printArray(out, sorted);
      // Make a copy
      mixedup = copyArray(sorted);
      // Run the tests
      for (int test = 0; test < repetitions; ++test) {
        // Some feedback so that you can tell it's running
        out.print(".");
        // Build a permutation.
        mixup(mixedup);
        // Sort the permutation
        resorted = sorter.sort(mixedup);
        // Print out an example when testing
        if ((TESTING) && (test == 0)) {
          out.println();
          out.println("Mixed up");
          printArray(out, mixedup);
          out.println();
          out.println("Resorted");
          printArray(out, resorted);
        }
        // See if it's correctly sorted.
        if (different(sorted,resorted)) {
          out.println("Problem sorting!");
          out.println("=== ORIGINAL ARRAY ===");
          printArray(out, mixedup);
          out.println("=== 'SORTED' ARRAY ===");
          printArray(out, resorted);
        } // if it's not correctly sorted
      } // for each test
      out.println();
    } // for each list size
  } // runTests(SimpleOutput)


  // +----------------+------------------------------------------
  // | Helper Methods |
  // +----------------+

  /**
   * Copy an array of strings.
   */
  protected String[] copyArray(String[] strings) {
    String[] copy = new String[strings.length];
    for (int i = 0;i < strings.length; ++i) {
      copy[i] = strings[i];
    }
    return copy;
  } // copyArray(String[])

  /**
   * See if two arrays of strings are different.
   */
  protected boolean different(String[] alpha, String[] beta) {
    if (alpha.length != beta.length) return true;
    for (int i = 0; i < alpha.length; ++i) {
      if (!alpha[i].equals(beta[i])) return true;
    }
    // If we've made it this far, they must be the same.
    return false;
  } // different(String[], String[])

  /**
   * Make a sorted list of an appropriate size.
   */
  protected String[] makeSortedList(int size) {
    String[] sorted = new String[size];
    for (int i = 0; i < size; ++i) {
      sorted[i] = nAs(i+1);
    }
    return sorted;
  } // makeSortedList(int)

  /**
   * Mix up the elements of an array.
   */
  protected void mixup(String[] strings) {
    // Randomly swap some number of times.
    for (int i = 0; i < strings.length; ++i) {
      swap(strings, 
           Math.abs(generator.nextInt() % strings.length),
           Math.abs(generator.nextInt() % strings.length));
    } // for
  } // mixup(String[])

  /**
   * Create a string of n "A"s.
   */
  protected String nAs(int n) {
    while (As.length() < n) {
      As = As.concat(As);
    }
    return As.substring(0,n);
  } // nAs(int)

  /**
   * Print an array of strings.
   */
  protected void printArray(SimpleOutput out, String[] strings) {
    for (int i = 0; i < strings.length; ++i) {
      out.println(strings[i]);
    }
  } // printArray(SimpleOutput, String[])

  /**
   * Swap two elements.
   */
  protected void swap(String[] strings, int x, int y) {
    String tmp = strings[x];
    strings[x] = strings[y];
    strings[y] = tmp;
  } // swap(String[], int, int)


  // +------+----------------------------------------------------
  // | Main |
  // +------+

  public static void main(String[] args) {
    SimpleOutput out = new SimpleOutput();
    SortTester tester = new SortTester(new MergeSortStrings());
    if (args.length < 1) {
      out.println("You must specificy quick or merge");
      return;
    }
    if (args[0].equals("quick")) {
      tester = new SortTester(new QuicksortStrings());
    }
    else if (args[0].equals("merge")) {
      // already set
    }
    else {
      out.println("Invalid sorting method: " + args[0]);
      out.println("You must specificy quick or merge");
      return;
    }
    tester.runTests(out);
  } // main(String[])
} // SortTester

