import java.math.BigInteger;
import edu.mit.ai.psg.traveler.*;
import java.lang.reflect.*;

/** FutureFactorial computes factorial via rangeproduct, which
    multiplies a range of numbers by dividing the range in two and starting
    futures to recursively compute the rangeproduct for each half.

    This version has no inner classes, Jeva does not implement inner classes.
    To run, cd to the directory containing this file.  Make sure
    no <tt>.class</tt> files exist in this directory.  Then run
    <pre>
      java edu.mit.ai.psg.traveler.jevaHooks.TraceCallsEvalHook FutureFactorialNoInner
    </pre>    

    @author CarlManning, caroma@ai.mit.edu<br>
    Copyright (c) 1999 Massachusetts Institute of Technology
**/

public class FutureFactorialNoInner { 
  // --- constants ---
  static final BigInteger ONE = BigInteger.ONE;
  static final BigInteger TWO = new BigInteger("2");
  /** computes the factorial of its arg, or 8 if no arg given. **/
  public static void main(String[] args) {
    BigInteger arg = new BigInteger(args.length == 0 ? "8" : args[0]);

    System.out.println(factorial(arg));
  }
  /** returns n! if n > 0
      @throws IllegalArgumentException if 1 > n; **/
  public static BigInteger factorial(final BigInteger n) {
    if (1 == n.signum()) // positive
      return rangeProduct(ONE, n);
    else
      throw new IllegalArgumentException("n < 1");
  }
  /** concurrently computes product of integers from lo to hi, inclusive. **/
  public static BigInteger rangeProduct(final BigInteger lo,
					final BigInteger hi) {
    if (lo.equals(hi)) return lo;
    else if ((lo.add(BigInteger.ONE)).equals(hi))
      return lo.multiply(hi);
    else {
      final BigInteger mid = (lo.add(hi)).divide(TWO);
      // future creation cannot be nested in .value() expression, else thread
      // would wait for value before creating second future -- non concurrent.
      Future loProd = new RangeProductFuture(lo, mid);
      Future hiProd = new RangeProductFuture(mid.add(ONE), hi);
      return ((BigInteger)loProd.value()).multiply((BigInteger)hiProd.value());
    }
  }
}

/** this would be an anonymous class if Jeva implemented anonymous classes. **/
final class RangeProductFuture extends Future {
  BigInteger lo, hi;
  RangeProductFuture(BigInteger lo, BigInteger hi) {
    this.lo = lo; this.hi = hi; start(); }
  public Object compute() { return FutureFactorialNoInner.rangeProduct(lo, hi); }
}


/** implements runnable rather than thread so that when completed, thread
    can be gc'ed **/
abstract class Future implements Runnable, Cloneable {
  // --- fields ---
  private State myState = UNINITIALIZED;
  private Object myValue = null;
  // --- constants for state ---
  private static final
    State UNINITIALIZED          = new State("Uninitialized");
  private static final
    State INITIALIZED_AND_STARTED= new State("Initialized and started");
  private static final
    State COMPLETED_RETURN       = new State("Completed with return");
  private static final
    State COMPLETED_THROW        = new State("Completed with throw");
  // --- constructor ---
  Future() {}
  /** Start computing value of future in a new thread. <code>start()</code>
      must not be called until future is initialized; this includes
      initializing free final variables in derived classes' compute
      method.  It may be called at end of initialization of a derived
      class IF that class is anonymous or final (so no classes can
      ever be derived from it).
      @throws IllegalStateException if has already been started.
  **/
  public synchronized void start() {
    if (myState == UNINITIALIZED) {
      Thread thread = new Thread(this);
      thread.start(); 
      myState = INITIALIZED_AND_STARTED;

      return;
    } else {
      throw new IllegalStateException("Already started.");
    }
  }
  /** compute value of future; called by run after future is started **/
  protected abstract Object compute(); 
  // --- Runnable ---
  public synchronized void run() { 
    while (myState == UNINITIALIZED) 
      try { this.wait(); } catch (InterruptedException e) {}
    
    if (myState == INITIALIZED_AND_STARTED) {
      try { 
	myValue = compute();
	myState = COMPLETED_RETURN;
      } catch(Throwable t) {
	myValue = t;
	myState = COMPLETED_THROW;
      }
      this.notifyAll();
    }
  }
  /** get value of future, waiting for computation to complete if necessary.
      @throws Error if {@link #compute()} completes by throwing an Error.
      @throws RuntimeException if {@link #compute()} completes by throwing
      a runtime exception. **/
  public synchronized Object value() {
    while (! (myState == COMPLETED_RETURN || myState == COMPLETED_THROW)) {
      try {
	this.wait();
      }
      catch (InterruptedException e) {
	throw new Error(e.toString());
      }
    }
    if (myState == COMPLETED_RETURN)
      return myValue; 
    else { // myState == COMPLETED_THROW
      Throwable thrown = (Throwable) myValue;
      if (dbgLev> 0) thrown.printStackTrace();
      if (thrown instanceof Error) throw (Error) thrown;
      else throw (RuntimeException) thrown;
    }
  }
  public String toString() {
    return "Future(state="+myState+", value="+myValue+")";}
  /** control printStackTrace on exceptions **/
  public static int dbgLev = 1;
}

class State {
  private String stateName;
  State(String stateName) { this.stateName = stateName; }
  public String toString() { return stateName; }
}
