public class Ratio {
public static Ratio valueOf(String value){
return valueOf(new BigDecimal(value));
}
public static Ratio valueOf(BigDecimal value){
int scale = value.scale();
BigDecimal numerator = value.scaleByPowerOfTen(scale);
BigDecimal denominator = BigDecimal.TEN.scaleByPowerOfTen(scale - 1);
return reduce(numerator.toBigInteger(), denominator.toBigInteger());
}
public static Ratio valueOf(String numerator, String denominator){
return new Ratio(new BigInteger(numerator), new BigInteger(denominator));
}
public static Ratio valueOf(double value){
if (Double.isInfinite(value)) {
throw new IllegalArgumentException("Value must be finite");
}
if (Double.isNaN(value)) {
throw new IllegalArgumentException("Value must be a number. NaN is not acceptable.");
}
return valueOf(Double.toString(value));
}
public static Ratio valueOf(long numerator){
return new Ratio(BigInteger.valueOf(numerator), BigInteger.ONE);
}
public static Ratio valueOf(long numerator, long denominator){
return reduce(BigInteger.valueOf(numerator), BigInteger.valueOf(denominator));
}
private static Ratio reduce(BigInteger numerator, BigInteger denominator) {
if (denominator.equals(BigInteger.ZERO)) {
throw new IllegalArgumentException("Denominator cannot be zero");
}
if (denominator.compareTo(BigInteger.ZERO) < 0) {
numerator = numerator.negate();
denominator = denominator.negate();
}
BigInteger gcd = numerator.gcd(denominator);
return new Ratio(numerator.divide(gcd), denominator.divide(gcd));
}
private final BigInteger numerator;
private final BigInteger denominator;
private Ratio(BigInteger numerador, BigInteger denominador){
this.numerator = numerador;
this.denominator = denominador;
}
public Ratio plus(Ratio other){
// a soma de a/b + c/d é igual a (ad+cb)/bd
BigInteger ad = this.numerator.multiply(other.denominator);
BigInteger cd = this.denominator.multiply(other.numerator);
BigInteger bd = this.denominator.multiply(other.denominator);
return new Ratio(ad.add(cd), bd); // não ha divisão
}
public Ratio subtract(Ratio other){
return this.plus(other.negate());
}
public Ratio negate(){
return new Ratio(this.numerator.negate(), this.denominator);
}
public Ratio multiply(Ratio other){
// o produto de a/b x c/d é igual a ac/bd
BigInteger ac = this.numerator.multiply(other.numerator);
BigInteger bd = this.denominator.multiply(other.denominator);
return new Ratio(ac, bd); // não ha divisão
}
public Ratio divide(Ratio other){
// a divisão de a/b por c/d é igual
// ao produto de a/b x d/c que é igual a ad/bc
BigInteger ad = this.numerator.multiply(other.denominator);
BigInteger bc = this.denominator.multiply(other.numerator);
return new Ratio(ad, bc); // não ha divisão
}
public boolean isZero(){
return numerator.signum() == 0;
}
public boolean isOne(){
return numerator.compareTo(denominator) == 0;
}
public BigDecimal asDecimal(){
return numerator.signum()==0
? BigDecimal.ZERO
: denominator.compareTo(BigInteger.ONE) == 0
? new BigDecimal(numerator)
: new BigDecimal(numerator).divide(
new BigDecimal(denominator),
15,
RoundingMode.HALF_EVEN
);
}
public int compareTo(Ratio other){
// a/b == c/d <=> ad == cb
BigInteger cb = denominator.multiply(other.numerator);
BigInteger ad = numerator.multiply(other.denominator);
return ad.compareTo(cb);
}
public int hashCode(){
return this.numerator.hashCode() + 31 * this.denominator.hashCode();
}
public boolean equals(Object other){
return other instanceof Ratio && compareTo((Ratio)other) == 0;
}
}