package rozwiazania_zadan.r3.z3_fraction;

public class Fraction extends Number implements Comparable<Fraction> {
	private final long nom;
	private final long denom;
	
	public static final Fraction ZERO = Fraction.of(0);
	public static final Fraction ONE = Fraction.of(1);
	public static final Fraction HALF = Fraction.of(1, 2);
	public static final Fraction PERCENT = Fraction.of(1, 100);
	
	private Fraction(long nominator, long denominator) {
		this.nom = nominator;
		this.denom = denominator;
	}
	
	public static Fraction of(long nominator, long denominator) {
		if(denominator == 0) {
			throw new ArithmeticException("denominator zero");
		}
		if(denominator < 0) {
			denominator = -denominator;
			nominator = -nominator;
		}
		return new Fraction(nominator, denominator);
	}

	public static Fraction of(long whole) {
		return new Fraction(whole, 1);
	}

	public long getNominator() {
		return nom;
	}

	public long getDenominator() {
		return denom;
	}
	
	@Override
	public String toString() {
		return String.format("%d/%d", nom, denom);
	}

	// equals i hashCode napisałem sam, ale to absolutnie nie znaczy, że to są najlepsze implementacje na świecie ;)
	@Override
	public boolean equals(Object obj) {
		if (obj == this) {
			return true;
		}
		if (obj == null || obj.getClass() != Fraction.class) {
			return false;
		}
		Fraction that = (Fraction)obj;
		return this.nom == that.nom && this.denom == that.denom;
	}
	
	@Override
	public int hashCode() {
		long mix = 1009 * denom + nom;
		return (int)mix ^ (int)(mix >> 32);
	}
	
	@Override
	public int compareTo(Fraction other) {
		return Long.compare(this.nom * other.denom, other.nom * this.denom);
	}
	
	public Fraction shortened() {
		if(nom == 0) {
			return Fraction.ZERO;
		}
		final long gcd = Utils.gcd(nom, denom);
		if(gcd == 1) {
			return this;
		}
		return new Fraction(nom / gcd, denom / gcd);
	}
	
	public Fraction repricodal() {
		return Fraction.of(denom, nom);
	}
	
	public Fraction negated() {
		if(nom == 0) {
			return this;
		}
		return new Fraction(-nom, denom);
	}
	
	public Fraction add(Fraction other) {
		return Fraction.of(this.nom * other.denom + other.nom * this.denom, this.denom * other.denom).shortened();
	}
	
	public Fraction sub(Fraction other) {
		return this.add(other.negated());
	}

	public Fraction mul(Fraction other) {
		return Fraction.of(this.nom * other.nom, this.denom * other.denom).shortened();
	}

	public Fraction mul(long number) {
		return Fraction.of(this.nom * number, this.denom).shortened();
	}

	public Fraction div(Fraction other) {
		return this.mul(other.repricodal());
	}

	public Fraction div(long number) {
		return Fraction.of(this.nom, this.denom * number).shortened();
	}

	public Fraction pow(int exp) {
		return Fraction.of(Utils.pow(this.nom, exp), this.denom).shortened();
	}
	
	@Override
	public double doubleValue() {
		return (double)nom / (double)denom;
	}
	
	@Override
	public float floatValue() {
		return (float)nom / (float)denom;
	}
	
	@Override
	public long longValue() {
		return nom / denom;
	}
	
	@Override
	public int intValue() {
		return (int)longValue();
	}
}
