/**
 * An immutable rational number.
 *
 * @author Drue Coles
 */
public class RationalNumber {

    public static final RationalNumber ZERO = new RationalNumber(0);
    public static final RationalNumber ONE = new RationalNumber(1);
    public static final RationalNumber ONE_FOURTH = new RationalNumber(1, 4);
    public static final RationalNumber ONE_THIRD = new RationalNumber(1, 3);
    public static final RationalNumber ONE_HALF = new RationalNumber(1, 2);

    private final int numerator;
    private final int denominator;

    /**
     * Constructs a rational number with a specified numerator and denominator. If the
     * denominator is zero, the constructor fails silently. 
     * 
     * @param numerator an arbitrary integer
     * @param denominator a non-zero integer
     */
    public RationalNumber(int numerator, int denominator) {

        // The greatest common divisor of the numerator and denominator will be factored
        // out so that each rational number has a unique representation.
        int gcd = gcd(numerator, denominator);

        // It is convenient to store the denominator as a positive integer, so if needed
        // we can take the additive inverse of both numerator and denominator.
        if (denominator < 0) {
            this.numerator = -numerator / gcd;
            this.denominator = -denominator / gcd;
        } else {
            this.numerator = numerator / gcd;
            this.denominator = denominator / gcd;
        }
    }

    /**
     * Constructs an integer rational number (i.e., denominator is 1).
     */
    public RationalNumber(int num) {
        this.numerator = num;
        this.denominator = 1;
    }

    /**
     * Returns the multiplicative inverse of this object.
     */
    public RationalNumber inverse() {
        return new RationalNumber(denominator, numerator);
    }

    /**
     * Returns the sum of this rational number and a given rational number.
     */
    public RationalNumber add(RationalNumber other) {
        int n = this.numerator * other.denominator + other.numerator * this.denominator;
        int d = this.denominator * other.denominator;
        return new RationalNumber(n, d);
    }

    /**
     * Returns the result of subtracting a given rational number from this rational
     * number.
     */
    public RationalNumber subtract(RationalNumber other) {
        int n = this.numerator * other.denominator - other.numerator * this.denominator;
        int d = this.denominator * other.denominator;
        return new RationalNumber(n, d);
    }

    /**
     * Returns the product of this rational number and a given rational number.
     */
    public RationalNumber multiply(RationalNumber other) {
        int n = this.numerator * other.numerator;
        int d = this.denominator * other.denominator;
        return new RationalNumber(n, d);
    }

    /**
     * Returns the result of dividing this rational number by a given rational number.
     */
    public RationalNumber divide(RationalNumber other) {
        return multiply(other.inverse());
    }

    /**
     * Returns the rational number as a string in the standard format.
     */
    @Override
    public String toString() {
        if (denominator == 1) {
            return "" + numerator;
        }
        if (numerator == 0) {
            return "0";
        }
        return numerator + "/" + denominator;
    }

    /**
     * Returns true if and only if this RationalNumber is numerically equal to obj.
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }

        RationalNumber other = (RationalNumber) obj;

        // We do not need to cross multiply, since all RationalNumbers are represented
        // by a numerator and denominator in lowest terms.
        return numerator == other.numerator && denominator == other.denominator;
    }

    /**
     * Returns the hash code value for this RationalNumber.
     */
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 89 * hash + this.numerator;
        hash = 89 * hash + this.denominator;
        return hash;
    }

    /**
     * Returns the greatest common divisor of two integers.
     */
    private int gcd(int x, int y) {
        x = Math.abs(x);
        y = Math.abs(y);

        // Euclid's algorithm
        int a = Math.max(x, y);
        int b = Math.min(x, y);
        while (b > 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
}