/**
 * Una fracción inmutable. Invariante: el denominador es positivo y la
 * fracción está reducida (numerador y denominador sin factores comunes).
 */
public class Fraction {
    private final long numerator;
    private final long denominator;

    /** Crea numerator/denominator, reducida. El denominador no puede ser cero. */
    public Fraction(long numerator, long denominator) {
        if (denominator == 0) {
            throw new IllegalArgumentException("denominador cero");
        }
        if (denominator < 0) {          // el signo va en el numerador
            numerator = -numerator;
            denominator = -denominator;
        }
        long g = gcd(Math.abs(numerator), denominator);
        this.numerator = numerator / g;
        this.denominator = denominator / g;
    }

    public long numerator() {
        return numerator;
    }

    public long denominator() {
        return denominator;
    }

    /** Devuelve this + other como una fracción nueva; this no cambia. */
    public Fraction plus(Fraction other) {
        return new Fraction(numerator * other.denominator + other.numerator * denominator,
                denominator * other.denominator);
    }

    /** Devuelve this × other como una fracción nueva. */
    public Fraction times(Fraction other) {
        return new Fraction(numerator * other.numerator, denominator * other.denominator);
    }

    // Máximo común divisor (Euclides). Es static: no usa ningún campo.
    private static long gcd(long a, long b) {
        while (b != 0) {
            long r = a % b;
            a = b;
            b = r;
        }
        return a == 0 ? 1 : a;
    }
}
