/**
 * Rational numbers (those that can be expressed as the ratio of two 
 * integers).
 *
 * @author Samuel A. Rebelsky
 * @author Yvonne Palm
 * @author Alex Leach
 * @author Evan Case
 * @author Jonathan Wellons
 */
public class Rational
  implements Multipliable
{
  // +--------+----------------------------------------------
  // | Fields |
  // +--------+

  /** The numerator of the number. */
  private long numerator;
  /** The denominator of the number. */
  private long denominator;

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

  /** Convert a MyInteger to a rational. */
  public Rational(MyInteger mint)
  {
    this(mint.toString());
  } // Rational(MyInteger)

  /** Convert a string of the form "n/d" or just "n" to a rational. */
  public Rational(String str) {
    int pos = str.indexOf('/');
    if (pos == -1) {
      this.numerator = Long.parseLong(str);
      this.denominator = 1;
    }
    else {
      this.numerator = Long.parseLong(str.substring(0, pos));
      this.denominator = Long.parseLong(str.substring(pos+1));
      this.simplify();
    }
  } // Rational(String)

  /** Create the rational number num/denom. */
  public Rational(long num, long denom) {
    this.numerator = num;
    this.denominator = denom;
    this.simplify();
  } // Rational(long,long)

  public Rational(float num) {
    double n = num;
    long d = 1;
    // As long as there is a fractional part
    while (n - Math.floor(n) > 0) {
      // Increment both n and d by the same factor
      n = n * 2;
      d = d * 2;
    }
    this.numerator = (long) n;
    this.denominator = d;
    this.simplify();
  } // Rational(float)

  // +------------------+------------------------------------
  // | Exported Methods |
  // +------------------+

  /** Multiply by another multipliable value. */
  public Multipliable multiply(Multipliable other)
  {
    if (other instanceof Rational) {
      Rational r = (Rational) other;
      return new Rational(this.numerator * r.numerator, 
                          this.denominator * r.denominator);
    }
    else if (other instanceof MyInteger) {
      MyInteger mint = (MyInteger) other;
      return new Rational(this.numerator * mint.value,
                          this.denominator);
    }
    else {
      return new Rational(0);
    }
  } // multiply(Multipliable)

  /** Convert this rational number to a double. */
  public double doubleValue()
  {
    return ((double) this.numerator) / this.denominator;
  } // doubleValue();

  /** Convert this rational number to a string of the form num/denom. */
  public String toString()
  {
    return this.numerator + "/" + this.denominator;
  } // toString()

  // +-----------------+-------------------------------------
  // | Private Methods |
  // +-----------------+

  /**
   * Simplify the rational, so that gcd(numerator,denominator) = 1.
   */
  private void simplify() 
  {
    // Find the greatest common divisor of the numerator and denom.
    long gcd = Rational.greatestCommonDivisor(this.numerator, this.denominator);
    // Divide both numerator and denominator by that value.
    this.numerator = this.numerator / gcd;
    this.denominator = this.denominator / gcd;
  } // simplify()


  // +---------------+---------------------------------------
  // | Class Methods |
  // +---------------+

  /** Divide a by b. */
  public static Rational divide(Rational a, Rational b) {
    return new Rational(0,1); // STUB
  } // divide(Rational,Rational)

  /** Multiply a by b. */
  public static Rational multiply(Rational a, Rational b) {
    return new Rational(a.numerator * b.numerator, 
                        a.denominator * b.denominator);
  } // multiply(Rational,Rational)

  // +-----------------------+-------------------------------
  // | Private Class Methods |
  // +-----------------------+

  /** Find the greatest common divisor of x and y. */
  private static long greatestCommonDivisor(long x, long y)
  {
    // If y is 0, the gcd is x. 
    if (y == 0) return x;
    // If y is 1, the gcd is 1.
    if (y == 1) return 1;
    // Euclid said: gcd(x,y) = gcd(y, x mod y)
    return greatestCommonDivisor(y, x % y);
  } // greatestCommonDivisor(int, int)
} // class Rational
