package gui; import java.math.BigDecimal; /** * @author Oliver Chu */ public class Rational { public long n; public long d; private static ForkedTable allRationals = new ForkedTable(); // makeRat memoizes rational number construction. public static Rational makeRat(long numer, long denom) { long[] lngs = {numer, denom}; Rational already = allRationals.getRat(lngs); if (already == null) { return new Rational(numer, denom); } else { return already; } } private Rational(long num, long den) { n = num; d = den; simplify(); save(); } private void save() { long[] lngs = {n, d}; allRationals.bindLongsToRat(lngs, this); } public static boolean closeEnough(double a, double b) { // If they really are equal... if (a == b) { return true; } String alpha = "" + a; String beta = "" + b; if (alpha.endsWith(".0") && beta.endsWith(".0")) { return ((int) a) == ((int) b); } else { if (alpha.equals(b)) { return true; } if (a > b) { return (a - b) < 0.00000001; } else { return (b - a) < 0.00000001; } } } public static double divideCorrectly(long numer, long denom) { BigDecimal top = new BigDecimal("" + numer); BigDecimal bottom = new BigDecimal("" + denom); top = top.divide(bottom, 32, BigDecimal.ROUND_HALF_UP); double answer = top.doubleValue(); String dblAsString = "" + answer; String reality = top.toString(); while (!(reality.startsWith(dblAsString)) && dblAsString.length() > 12) { dblAsString = StrTools2.slice(dblAsString, 0, -1); } return Double.parseDouble(dblAsString); } public static double divideCorrectlyFast(double numer, double denom) { double inverse = 1.0 / denom; return numer * inverse; } public static double divideCorrectly(double n0, double d0) { return divideCorrectlyFast(n0, d0); /* BigDecimal top = new BigDecimal(n0); BigDecimal bottom = new BigDecimal(d0); top = top.divide(bottom, 32, BigDecimal.ROUND_HALF_UP); double answer = top.doubleValue(); String dblAsString = "" + answer; String reality = top.toString(); while (!(reality.startsWith(dblAsString)) && dblAsString.length() > 12) { dblAsString = StrTools2.slice(dblAsString, 0, -1); } return Double.parseDouble(dblAsString); */ } public Integer getInteger() { if (d == 1) { return (int) n; } return null; } public Rational add(Rational r) { long newDenom = r.d * d; long newNumer = n * r.d + r.n * d; return makeRat(newNumer, newDenom); } public Rational sub(Rational r) { long newDenom = r.d * d; long newNumer = n * r.d - r.n * d; return makeRat(newNumer, newDenom); } public Rational mul(Rational r) { long newNumer = n * r.n; long newDenom = d * r.d; return makeRat(newNumer, newDenom); } public Rational div(Rational r) { long newNumer = n * r.d; long newDenom = d * r.n; return makeRat(newNumer, newDenom); } public String sign() { if ((n < 0 && d < 0) || (n > 0 && d > 0) || n == 0) { return ""; } return "-"; } public BigDecimal toBigDecimal() { BigDecimal seme = new BigDecimal("" + n); BigDecimal uke = new BigDecimal("" + d); // We invert first, for more accuracy. BigDecimal pose = BigDecimal.ONE.divide(uke, 48, BigDecimal.ROUND_HALF_UP); BigDecimal answer = seme.multiply(pose); // We chop off the last numbers by rounding, not truncating. answer.setScale(32, BigDecimal.ROUND_HALF_UP); return answer; } public double toDouble() { return divideCorrectly(n, d); } public String toString() { String inf = getInfinity(); if (inf != null) { return inf; } return "" + toDouble(); } public String getInfinity() { if (d == 0 && n == 0) { return "undefined (infinity multiplied by 0)"; } if (d != 0) { return null; } byte scale = (byte) Math.abs(n); boolean isAlephNull = scale == 1; return (n < 0 ? "-" : "") + "infinity" + (isAlephNull ? "" : " of magnitude " + Math.abs(n)); } public long greatestCommonDivisor(long a, long b) { if (a == 0) { return b; } if (b == 0) { return a; } return greatestCommonDivisor(b % a, a); } public final void simplify() { long gcd = greatestCommonDivisor(n, d); long positive = Math.abs(gcd); if (positive != 0L && positive != 1L) { n /= gcd; d /= gcd; } } public String exact() { String inf = getInfinity(); if (inf != null) { return inf; } return sign() + Math.abs(n) + "/" + Math.abs(d); } }