/**
 * Una fracción inmutable. Invariante: el denominador es positivo y la
 * fracción está reducida, así que dos fracciones iguales tienen los mismos
 * campos.
 */
public final class Fraction {
    private final long num;
    private final long den;

    public Fraction(long num, long den) {
        if (den == 0) {
            throw new IllegalArgumentException("denominador cero");
        }
        if (den < 0) {                  // el signo va en el numerador
            num = -num;
            den = -den;
        }
        long g = gcd(Math.abs(num), den);
        this.num = num / g;
        this.den = den / g;
    }

    private static long gcd(long a, long b) {
        while (b != 0) {
            long r = a % b;
            a = b;
            b = r;
        }
        return a;
    }

    public long num() {
        return num;
    }

    public long den() {
        return den;
    }

    public Fraction plus(Fraction other) {
        return new Fraction(num * other.den + other.num * den, den * other.den);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;                // el mismo objeto
        }
        if (!(o instanceof Fraction f)) {
            return false;               // null u otro tipo
        }
        return num == f.num && den == f.den;
    }

    @Override
    public int hashCode() {
        return 31 * Long.hashCode(num) + Long.hashCode(den);
    }

    @Override
    public String toString() {
        return den == 1 ? Long.toString(num) : num + "/" + den;
    }
}
