/** Classes may be used as a kind of module consisting of a collection of
    "static" variables and methods (functions).  The externally-visible
    components are marked "public"; components for internal use only are
    marked "private".  Variables marked "final" are constants; they cannot be
    assigned once initialized.

    There may be multiple definitions for a method, differing in the number
    and types of their arguments.  Java uses static type information to select
    the appropriate version based on the presence and types of arguments at
    the call site.  */

class Factorial {

  /* Default argument for factorial.  Not assignable. */
  private final static int default_arg = 5;

  /* Recursive factorial. */
  private static int rec_factorial (int n) {
    if (n==0) {
      return 1;
    } else {
      return n * rec_factorial (n-1);
    }
  }

  /* Recursive factorial, using a switch statement. */
  private static int rec_sw_factorial (int n) {
    switch (n) {
    case 0: return (1);
    default: return (n * rec_sw_factorial (n-1));
    }
  }

  /* Factorial, computed using a for loop. */
  private static int for_factorial (int n) {
    int result = 1;
    for (int i=n; i!=0; i--) {
      /* Invariant: result = n!/i! */
      result *= i;
    }
    return result;
  }

  /* Factorial, using a while loop. */
  private static int while_factorial (int n) {
    /* Invariant: result = n!/i! */
    int result = 1;
    int i = n;
    while (i != 0) {
      result *= i;
      i--;
    }
    return result;
  }

  /* Public entry point, re-directing to any of several versions. */
  public static int factorial (int n) {
    return rec_factorial (n);
  }

  /* Public entry, with a default argument. */
  public static int factorial () {
    return factorial (default_arg);
  }

}

/* A simple demonstration.  The components of a class are accessed using "dot
   notation". */

class StaticDemo {

  public static void main (String argv[]) {
    if (argv.length == 0) {
      int result = Factorial.factorial();
      System.out.println
	("Factorial returns " + result);
    } else {
      int arg = Integer.parseInt(argv[0]);
      int result = Factorial.factorial(arg);
      System.out.println
	("Factorial of " + arg + " is " + result);
    }
  }
}
