/** INSTRUCTIONS FOR ALL TASKS
 ** 
 ** Rewrite every function so that they no longer use ANY of the following
 ** constructs: { try, catch, throw, goto, continue, break } and each function
 ** may have only *one* use of the return form, at the end of the function
 ** definition, e.g.:
 ** 
 ** int abs(int x) {
 **   z = x;
 **   if (x < 0) 
 **     z = -x;
 **   return z;
 **  }
 ** 
 **/

class MP7 { 

/* Task 1: return */

/* usage: roots( a, b, c ) returns number of roots of
 * quadratic equation ax^2 + bx + c.
 */

static int roots( int a, int b, int c ) {
  int discrim = (b*b - 4*a*c);
  if (discrim > 0) {
    return 2;
  }
  else if (discrim == 0) {
    return 1;
  }
  else { 
    return 0;
  }
}

/* Task 2: break */

/* requires: array has length k
 * usage: prod( A, k ) returns the product A[0] * A[1] * ... * A[k-1].
 */

static int prod( int array[], int k ) {
  int index = 0;
  int product = 1;
  while ( index < k ) {
    if (array[index] == 0) {
      product = 0;
      break;
    }
    else {
      product = product * array[index];
      index = index + 1;
    }
  }

  return product;
}

/* Task 3: break (again) */

/* requires: array has length k
 * usage: find( A, k, j ) returns the index of the first occurrence 
 *    of j in A.  If j does not occur in A, then returns k.
 */

static int find( int array[], int k, int j ) {
  int index = 0;
  while ( index < k ) {
    if (array[index] == j) {
      break;
    }
    index = index + 1;
  }
 
  return index;
}

/* Task 4: continue */

/* requires: array has length k
 * usage: mystery(A, k) returns a mysterious sum of some elements in A.
 */

static int mystery( int array[], int k ) {
  int i, j;
  int sum = 0;
  i = 0;
  while ( i < k ) {
    j = i;
    i = i + 1;
    while ( j < k ) {
      j = j + 1;
      if (array[j-1] == 1) {
        continue;
      }
      sum = sum + array[j-1];
    }
  }

  return sum;
}

/* Task 5: FakeGoto */

static class FakeGoto extends Exception {} 

/* requires: a or b is non-zero.
 * usage: gcd(a,b) returns the greatest common divisor of a and b
 */

static int gcd( int a, int b ) {
  int t;
  FakeGoto theFakeGoto = new FakeGoto();
  while (true) {
      try {
	  if (b != 0) {
	      t = b;
	      b = a % b;
	      a = t;
	      throw theFakeGoto;
	  }
	  return a;
      }
      catch (FakeGoto ignore_arg) { }
  }
}

/* Task 6: FakeJmp */

static class FakeJmp extends Exception {}

/* Let P = k * k-1 * k-2 * ... * 1.
 * If P fits in a Java integer, returns P.  Else returns k.
 */ 

static int fact_like( int k ) {
  /* The try special form saves its continuation, Kappa, and within
   * the body of the try, throws of Exceptions that "match" the
   * catch clause will jump to the catch's body. */
  try {
      FakeJmp exn = new FakeJmp();
      return fact_core( k, exn );
  }
  catch (FakeJmp ignore_the_exn) {
      return k;
  }
}

/* Helper for fact_like.  Computes factorial, but escapes through 
 * continuation stored in env if we encounter overflow.
 */

static int fact_core( int k, FakeJmp env ) throws FakeJmp {
  if (k == 1) {
    return 1;
  }
  else {
    int factprev = fact_core( k-1, env );
    int retval = k * factprev;
    int orig   = retval / k;

    /* How could orig != factprev? */

    if ( orig != factprev ) {
      /* longjmp( env, X ) abandons its continuation and returns X
       * through the continuation Kappa saved in env.  */
      throw env;
    }
    else {
      return retval;
    }
  }
}

/**
 ** 
 ** START OF TESTING CODE (both test procedures and example input data). 
 ** 
 ** 
 **/

static int four_twos[]        = { 2, 2, 2, 2 };
static int nine_to_zero[]     = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
static int ten_to_one[]       = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
static int ten_to_one_twice[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 
                                  10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
static int four_twoones[]     = { 2, 1, 2, 1, 
                                  2, 1, 2, 1 };

static int test( int task_num, String test_name, int actual, int expected ) {
  int test_failure;
  if (actual == expected) {
    System.out.print( "test success task "+task_num+" "+test_name+"\n");
    test_failure = 0;
  }
  else {
    System.out.print( "TEST FAILURE TASK " + task_num
                      + "\"" + test_name + "\" expected: "
                      + expected + " actual: " + actual + "\n");
    test_failure = 1;
  }
  return test_failure;
}

/* usage: returns number of test failures for Task 1. */
static int test_task1() {
  int c = 0;
  c = c + test( 1, "roots 4x^2",          roots( 4, 0, 0 ), 1 );
  c = c + test( 1, "roots 4x^2 + 1",      roots( 4, 0, 1 ), 0 );
  c = c + test( 1, "roots  x^2 - 4x",     roots( 1,-4, 0 ), 2 );
  c = c + test( 1, "roots  x^2 + 4",      roots( 1, 0, 4 ), 0 );
  c = c + test( 1, "roots  x^2 + 2x + 1", roots( 1, 2, 1 ), 1 );
  c = c + test( 1, "roots 2x^2 + 6x + 4", roots( 2, 6, 4 ), 2 );
  c = c + test( 1, "roots 2x^2 + 6x + 5", roots( 2, 6, 5 ), 0 );
  return c;
}

/* usage: returns number of test failures for Task 2. */
static int test_task2() {
  int c = 0;
  c = c + test( 2, "prod [2,2,2,2]",     prod( four_twos,     4 ), 16 );
  c = c + test( 2, "prod [9,8,...,0])",  prod( nine_to_zero, 10 ), 0 );
  c = c + test( 2, "prod [10,9,...,1])", prod( ten_to_one,   10 ), 3628800 );
  return c;
}

/* usage: returns number of test failures for Task 3. */
static int test_task3() {
  int c = 0;
  c = c + test( 3, "find 2 in [2,2,2,2]",    find( four_twos,    4,  2 ), 0 );
  c = c + test( 3, "find 8 in [9,8,...,0]",  find( nine_to_zero, 10, 8 ), 1 );
  c = c + test( 3, "find 7 in [9,8,...,0]",  find( nine_to_zero, 10, 7 ), 2 );
  c = c + test( 3, "find 7 in [9,8,...,0]",  find( nine_to_zero, 10, 7 ), 2 );
  c = c + test( 3, "find 1 in [10,9,...,1]", find( ten_to_one,   10, 1 ), 9 );
  c = c + test( 3, "find 1 in [10,9,...,1,10,9,...,1]", 
                find( ten_to_one_twice, 10, 1 ), 9 );
  c = c + test( 3, "find 0 in [10,9,...,1]", find( ten_to_one,   10, 0 ), 10);
  return c;
}

/* usage: returns number of test failures for Task 4. */
static int test_task4() {
  int c = 0;
  c += test( 4, "mystery [2,2,2,2]",         mystery( four_twos, 4 ), 20);
  c += test( 4, "mystery [2,1,2,1,2,1,2,1]", mystery( four_twoones, 8 ), 32);
  return c;
}

/* usage: returns number of test failures for Task 5. */
static int test_task5() {
  int c = 0;
  c = c + test( 5, "gcd(42,56)",     gcd( 42, 56 ), 14);
  c = c + test( 5, "gcd(1071,1029)", gcd( 1071, 1029 ), 21);
  return c;
}

/* usage: returns number of test failures for Task 6. */
static int test_task6() {
  int c = 0;
  c = c + test( 6, "10!",             fact_like( 10 ), 3628800);
  c = c + test( 6, "12!",             fact_like( 12 ), 479001600);
  c = c + test( 6, "fact_like(1000)", fact_like( 1000 ), 1000);
  return c;
}

/* usage: runs test suite for MP7; returns number of test failures.
 * Optional argument indicates individual task to test. */
public static void main( String argv[] ) {
  int failure_count = 0;

  if (argv.length == 0) {
    failure_count = failure_count + test_task1();
    failure_count = failure_count + test_task2();
    failure_count = failure_count + test_task3();
    failure_count = failure_count + test_task4();
    failure_count = failure_count + test_task5();
    failure_count = failure_count + test_task6();
  }
  else {
    if (argv[1].equals("1")) {
      failure_count = failure_count + test_task1();
    }
    else if (argv[1].equals("2")) {
      failure_count = failure_count + test_task2();
    }
    else if (argv[1].equals("3")) {
      failure_count = failure_count + test_task3();
    }
    else if (argv[1].equals("4")) {
      failure_count = failure_count + test_task4();
    }
    else if (argv[1].equals("5")) {
      failure_count = failure_count + test_task5();
    }
    else if (argv[1].equals("6")) {
      failure_count = failure_count + test_task6();
    }
    else {
      System.out.print("warning: argument was \"%s\","
                       + " expected 1 2 3 4 5 or 6.\n");
    }
  }
  System.out.println("Testing complete; " + failure_count + " test failures.");

  if (failure_count != 0) {
    System.exit(1);
  }
  else {
    System.exit(0);
  }
}
}



