I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# Arithmetic/Rational/Java

Modeled after BigInteger/BigDecimal. Instances of this class are immutable, and simplified upon construction. The returned numerator() and denominator() are never negative; the sign can be retrieved via signum(). The denominator for zero is always 1 (e.g. 0/5 is simplified to 0/1), and signed zeros are not supported (e.g. 0/-1 is simplified to 0/1).

`import java.math.BigDecimal;import java.math.BigInteger;import java.math.RoundingMode; public class BigRational extends Number implements Comparable<BigRational>{   public final static BigRational ZERO = new BigRational(false, BigInteger.ZERO, BigInteger.ONE);  public final static BigRational ONE = new BigRational(false, BigInteger.ONE, BigInteger.ONE);   private final boolean isNegative;  private final BigInteger numerator;  private final BigInteger denominator;  private final int hashCode;   private BigRational(boolean isNegative, BigInteger nonNegativeNumerator, BigInteger nonNegativeDenominator)  {    this.isNegative = isNegative;    this.numerator = nonNegativeNumerator;    this.denominator = nonNegativeDenominator;    this.hashCode = computeHashCode(isNegative, nonNegativeNumerator, nonNegativeDenominator);  }   private BigRational(BigInteger numerator, BigInteger denominator, boolean ignoreComponentSigns, boolean forcedSign)  {    if (denominator.equals(BigInteger.ZERO))      throw new ArithmeticException("Denominator is zero");    boolean isNegative = ignoreComponentSigns ? forcedSign : false;    if (numerator.equals(BigInteger.ZERO))    {      denominator = BigInteger.ONE;      isNegative = false;    }    else    {      if (numerator.signum() < 0)      {        if (!ignoreComponentSigns)          isNegative = true;        numerator = numerator.negate();      }      if (denominator.signum() < 0)      {        if (!ignoreComponentSigns)          isNegative = !isNegative;        denominator = denominator.negate();      }      BigInteger gcd = numerator.gcd(denominator);      if (!gcd.equals(BigInteger.ONE))      {        numerator = numerator.divide(gcd);        denominator = denominator.divide(gcd);      }    }    this.isNegative = isNegative;    this.numerator = numerator;    this.denominator = denominator;    this.hashCode = computeHashCode(isNegative, numerator, denominator);  }   public BigRational(BigInteger numerator, BigInteger denominator)  {  this(numerator, denominator, false, false);  }   public BigRational abs()  {  return isNegative ? new BigRational(false, numerator, denominator) : this;  }   public BigRational add(BigRational br)  {    if (isNegative == br.isNegative)      return addIgnoreNegatives(isNegative, this, br);    if (isNegative)      return subtractIgnoreNegatives(br, this);    return subtractIgnoreNegatives(this, br);  }   public int compareTo(BigRational br)  {    if (isNegative != br.isNegative)      return isNegative ? -1 : 1;    return subtract(br).signum();  }   public BigRational decrement()  {    if (isNegative)      return new BigRational(numerator.add(denominator), denominator, true, true);    return new BigRational(numerator.subtract(denominator), denominator, true, (numerator.compareTo(denominator) < 0));  }   public BigInteger denominator()  {  return denominator;  }   public BigRational divide(BigInteger bi)  {    boolean isNegative = (bi.signum() < 0);    if (isNegative)      bi = bi.negate();    isNegative = (isNegative != this.isNegative);    return new BigRational(numerator, denominator.multiply(bi), true, isNegative);  }   public BigRational divide(BigRational divisor)  {  return multiply(divisor.reciprocal());  }   public double doubleValue()  {  return toBigDecimal(18, RoundingMode.HALF_EVEN).doubleValue();  }   public boolean equals(Object o)  {    if ((o == null) || !(o instanceof BigRational))      return false;    BigRational br = (BigRational)o;    return (isNegative == br.isNegative) && numerator.equals(br.numerator) && denominator.equals(br.denominator);  }   public float floatValue()  {  return toBigDecimal(9, RoundingMode.HALF_EVEN).floatValue();  }   public int hashCode()  {  return hashCode;  }   public BigRational increment()  {    if (!isNegative)      return new BigRational(numerator.add(denominator), denominator, true, false);    return new BigRational(numerator.subtract(denominator), denominator, true, (numerator.compareTo(denominator) > 0));  }   public int intValue()  {  return toBigDecimal(12, RoundingMode.HALF_EVEN).intValue();  }   public boolean isWholeNumber()  {  return denominator.equals(BigInteger.ONE);  }   public boolean isZero()  {  return numerator.equals(BigInteger.ZERO);  }   public long longValue()  {  return toBigDecimal(21, RoundingMode.HALF_EVEN).longValue();  }   public BigRational max(BigRational br)  {  return (compareTo(br) >= 0) ? this : br;  }   public BigRational min(BigRational br)  {  return (compareTo(br) <= 0) ? this : br;  }   public Object[] mixedFraction()  {    BigInteger[] dar = numerator.divideAndRemainder(denominator);    BigInteger whole = dar[0];    if (isNegative)      whole = whole.negate();    BigRational fraction = new BigRational(dar[1], denominator, true, isNegative);    return new Object[] { whole, fraction };  }   public BigRational multiply(BigInteger bi)  {    boolean isNegative = (bi.signum() < 0);    if (isNegative)      bi = bi.negate();    isNegative = (isNegative != this.isNegative);    return new BigRational(numerator.multiply(bi), denominator, true, isNegative);  }   public BigRational multiply(BigRational br)  {    BigInteger numerator = this.numerator.multiply(br.numerator);    BigInteger denominator = this.denominator.multiply(br.denominator);    return new BigRational(numerator, denominator, true, isNegative != br.isNegative);  }   public BigRational negate()  {    if (isZero())      return this;    return new BigRational(!isNegative, numerator, denominator);  }   public BigInteger numerator()  {  return numerator;  }   public BigRational pow(int n)  {    BigInteger numerator = this.numerator.pow(n);    BigInteger denominator = this.denominator.pow(n);    return new BigRational(numerator, denominator, true, isNegative ? ((n & 1) != 0) : false);  }   public BigRational reciprocal()  {    if (isZero())      throw new ArithmeticException("Can not calculate reciprocal of zero");    return new BigRational(isNegative, denominator, numerator);  }   public int signum()  {    if (isZero())      return 0;    return isNegative ? -1 : 1;  }   public BigRational subtract(BigRational br)  {    if (isNegative != br.isNegative)      return addIgnoreNegatives(isNegative, this, br);    if (isNegative)      return subtractIgnoreNegatives(br, this);    return subtractIgnoreNegatives(this, br);  }   public BigDecimal toBigDecimal(int desiredPrecision, RoundingMode roundingMode)  {    BigDecimal bdNumerator = new BigDecimal(numerator);    BigDecimal bdDenominator = new BigDecimal(denominator);    int resultScale = bdNumerator.scale() - bdDenominator.scale() - bdNumerator.precision() + bdDenominator.precision() + desiredPrecision;    BigDecimal bigDecimalValue = bdNumerator.divide(bdDenominator, resultScale, roundingMode);    if (bigDecimalValue.precision() > desiredPrecision)      bigDecimalValue = bigDecimalValue.setScale(bigDecimalValue.scale() - 1, roundingMode);    if (isNegative)      bigDecimalValue = bigDecimalValue.negate();    return bigDecimalValue;  }   public BigDecimal toBigDecimalExact()  {    BigDecimal bigDecimalValue = new BigDecimal(numerator).divide(new BigDecimal(denominator));    if (isNegative)      bigDecimalValue = bigDecimalValue.negate();    return bigDecimalValue;  }   public String toString()  {    if (isWholeNumber())    {      if (isNegative)        return "-" + numerator.toString();      return numerator.toString();    }    if (isNegative)      return "-" + numerator.toString() + "/" + denominator.toString();    return numerator.toString() + "/" + denominator.toString();  }   private static int computeHashCode(boolean isNegative, BigInteger numerator, BigInteger denominator)  {    int c = numerator.hashCode() + denominator.hashCode();    if (isNegative)      c = ~c;    return c;  }   private static BigRational addIgnoreNegatives(boolean resultIsNegative, BigRational first, BigRational second)  {    first = first.abs();    second = second.abs();    BigInteger numerator = null;    BigInteger denominator = null;    if (first.denominator.equals(second.denominator))    {      numerator = first.numerator.add(second.numerator);      denominator = first.denominator;    }    else    {      numerator = first.numerator.multiply(second.denominator).add(second.numerator.multiply(first.denominator));      denominator = first.denominator.multiply(second.denominator);    }    return new BigRational(numerator, denominator, true, resultIsNegative);  }   private static BigRational subtractIgnoreNegatives(BigRational first, BigRational second)  {    first = first.abs();    second = second.abs();    BigInteger numerator = null;    BigInteger denominator = null;    if (first.denominator.equals(second.denominator))    {      numerator = first.numerator.subtract(second.numerator);      denominator = first.denominator;    }    else    {      numerator = first.numerator.multiply(second.denominator).subtract(second.numerator.multiply(first.denominator));      denominator = first.denominator.multiply(second.denominator);    }    return new BigRational(numerator, denominator);  }   public static BigRational valueOf(int integerValue)  {  return valueOf(integerValue, 1);  }   public static BigRational valueOf(int numerator, int denominator)  {  return new BigRational(BigInteger.valueOf(numerator), BigInteger.valueOf(denominator));  }   public static BigRational valueOf(long longValue)  {  return valueOf(longValue, 1);  }   public static BigRational valueOf(long numerator, long denominator)  {  return new BigRational(BigInteger.valueOf(numerator), BigInteger.valueOf(denominator));  }   public static BigRational valueOf(BigDecimal bigDecimalValue)  {    int scale = bigDecimalValue.scale();    BigInteger numerator = bigDecimalValue.unscaledValue();    if (scale <= 0)    {      if (scale < 0)        numerator = numerator.multiply(BigInteger.TEN.pow(-scale));      return new BigRational(numerator, BigInteger.ONE);    }    return new BigRational(numerator, BigInteger.TEN.pow(scale));  }   public static BigRational valueOf(BigDecimal numerator, BigDecimal denominator)  {  return valueOf(numerator).divide(valueOf(denominator));  }   public static BigRational valueOf(double doubleValue)  {  return valueOf(new BigDecimal(doubleValue));  }   public static BigRational valueOf(double numerator, double denominator)  {  return valueOf(numerator).divide(valueOf(denominator));  }   public static BigRational valueOf(String number)  {    int slashIndex = number.indexOf("/");    if (slashIndex > 0)      return valueOf(number.substring(0, slashIndex), number.substring(slashIndex + 1));    return valueOf(new BigDecimal(number));  }   public static BigRational valueOf(String numerator, String denominator)  {  return valueOf(numerator).divide(valueOf(denominator));  }   /* ******************** Testing ******************** */   public static boolean testEquals(String testName, Object obj, String str)  {  return testEquals(testName, obj, null, str);  }   public static boolean testEquals(String testName, Object obj1, Object obj2, String str)  {    // obj2 is optional    boolean objEquality = (obj2 == null) || obj1.equals(obj2);    String objStr = (obj1 instanceof BigDecimal) ? ((BigDecimal)obj1).toPlainString() : obj1.toString();    boolean strEquality = objStr.equals(str);    if (objEquality && strEquality)    {      System.out.println("PASS: " + testName);      return true;    }    String err = "FAIL: " + testName + ": " + obj1.toString() + " not equal to";    if (!objEquality)      err += " object " + obj2.toString();    if (!strEquality)    {      if (!objEquality)        err += " and";      err += " string " + str;    }    System.out.println(err);    return false;  }   public static boolean testTrue(String testName, boolean b)  {    System.out.println((b ? "PASS: " : "FAIL: ") + testName);    return b;  }   public static boolean testFeatures()  {    boolean passedAll = true;    passedAll &= testEquals("BaseConstructor-1", new BigRational(BigInteger.valueOf(30), BigInteger.valueOf(72)), new BigRational(BigInteger.valueOf(5), BigInteger.valueOf(12)), "5/12");    passedAll &= testEquals("BaseConstructor-2", new BigRational(BigInteger.valueOf(-30), BigInteger.valueOf(72)), new BigRational(BigInteger.valueOf(5), BigInteger.valueOf(-12)), "-5/12");    passedAll &= testEquals("BaseConstructor-3", new BigRational(BigInteger.valueOf(-30), BigInteger.valueOf(-72)), new BigRational(BigInteger.valueOf(-5), BigInteger.valueOf(-12)), "5/12");    passedAll &= testEquals("BaseConstructor-4", new BigRational(BigInteger.valueOf(0), BigInteger.valueOf(5)), new BigRational(BigInteger.valueOf(0), BigInteger.valueOf(-10)), "0");    passedAll &= testTrue("Inequality-1", !new BigRational(BigInteger.valueOf(5), BigInteger.valueOf(12)).equals(new BigRational(BigInteger.valueOf(-5), BigInteger.valueOf(12))));    passedAll &= testTrue("Inequality-2", !new BigRational(BigInteger.valueOf(5), BigInteger.valueOf(12)).equals(new BigRational(BigInteger.valueOf(4), BigInteger.valueOf(12))));    passedAll &= testEquals("IntegerConstructor-1", BigRational.valueOf(5), "5");    passedAll &= testEquals("IntegerConstructor-2", BigRational.valueOf(5, 12), new BigRational(BigInteger.valueOf(30), BigInteger.valueOf(72)), "5/12");    passedAll &= testEquals("LongConstructor-1", BigRational.valueOf(10000000000L), "10000000000");    passedAll &= testEquals("LongConstructor-2", BigRational.valueOf(50000000000L, 120000000000L), new BigRational(BigInteger.valueOf(300000000000L), BigInteger.valueOf(720000000000L)), "5/12");    passedAll &= testEquals("BigDecimalConstructor-1", BigRational.valueOf(new BigDecimal("7.3412359")), "73412359/10000000");    passedAll &= testEquals("BigDecimalConstructor-2", BigRational.valueOf(new BigDecimal("7.3412359"), new BigDecimal("2.6876980111")), BigRational.valueOf(15791000, 5781239), "15791000/5781239");    // Derived from BigDecimal documentation, 0.1 is exactly 0.1000000000000000055511151231257827021181583404541015625. Simplified via WolframAlpha    passedAll &= testEquals("DoubleConstructor-1", BigRational.valueOf(0.1), BigRational.valueOf(new BigDecimal(0.1)), "3602879701896397/36028797018963968");    passedAll &= testEquals("DoubleConstructor-2", BigRational.valueOf(0.1, 0.2), BigRational.valueOf(1, 2), "1/2");    passedAll &= testEquals("StringConstructor-1", BigRational.valueOf("18"), BigRational.valueOf(18), "18");    passedAll &= testEquals("StringConstructor-2", BigRational.valueOf("18/-4"), BigRational.valueOf(9, -2), "-9/2");    passedAll &= testEquals("StringConstructor-3", BigRational.valueOf("18", "-4"), BigRational.valueOf(9, -2), "-9/2");    passedAll &= testEquals("AbsoluteValue", BigRational.valueOf(-1, 2).abs(), BigRational.valueOf(1, 2), "1/2");    passedAll &= testEquals("Addition-1", BigRational.valueOf("2/5").add(BigRational.valueOf("3/7")), "29/35");    passedAll &= testEquals("Addition-2", BigRational.valueOf("2/5").add(BigRational.valueOf("-3/7")), "-1/35");    passedAll &= testEquals("Addition-3", BigRational.valueOf("-2/5").add(BigRational.valueOf("3/7")), "1/35");    passedAll &= testEquals("Addition-4", BigRational.valueOf("-2/5").add(BigRational.valueOf("-3/7")), "-29/35");    passedAll &= testEquals("Addition-5", BigRational.valueOf("2/5").add(BigRational.valueOf("4/5")), "6/5");    passedAll &= testEquals("Subtraction-1", BigRational.valueOf("2/5").subtract(BigRational.valueOf("3/7")), "-1/35");    passedAll &= testEquals("Subtraction-2", BigRational.valueOf("2/5").subtract(BigRational.valueOf("-3/7")), "29/35");    passedAll &= testEquals("Subtraction-3", BigRational.valueOf("-2/5").subtract(BigRational.valueOf("3/7")), "-29/35");    passedAll &= testEquals("Subtraction-4", BigRational.valueOf("-2/5").subtract(BigRational.valueOf("-3/7")), "1/35");    passedAll &= testEquals("Subtraction-5", BigRational.valueOf("2/7").subtract(BigRational.valueOf("3/7")), "-1/7");    passedAll &= testTrue("Comparison-1", BigRational.valueOf("2/5").compareTo(BigRational.valueOf("-2/5")) > 0);    passedAll &= testTrue("Comparison-2", BigRational.valueOf("-2/5").compareTo(BigRational.valueOf("2/5")) < 0);    passedAll &= testTrue("Comparison-3", BigRational.valueOf("2/5").compareTo(BigRational.valueOf("3/7")) < 0);    passedAll &= testTrue("Comparison-4", BigRational.valueOf("-2/5").compareTo(BigRational.valueOf("-3/7")) > 0);    passedAll &= testEquals("Increment-1", BigRational.valueOf("2/5").increment(), "7/5");    passedAll &= testEquals("Increment-2", BigRational.valueOf("-2/5").increment(), "3/5");    passedAll &= testEquals("Increment-3", BigRational.valueOf("-7/5").increment(), "-2/5");    passedAll &= testEquals("Decrement-1", BigRational.valueOf("7/5").decrement(), "2/5");    passedAll &= testEquals("Decrement-2", BigRational.valueOf("2/5").decrement(), "-3/5");    passedAll &= testEquals("Decrement-3", BigRational.valueOf("-2/5").decrement(), "-7/5");    passedAll &= testEquals("Divide-1", BigRational.valueOf("2/5").divide(BigRational.valueOf("3/5")), "2/3");    passedAll &= testEquals("Divide-2", BigRational.valueOf("-2/5").divide(BigRational.valueOf("3/5")), "-2/3");    passedAll &= testEquals("Divide-3", BigRational.valueOf("-2/5").divide(BigRational.valueOf("-3/5")), "2/3");    passedAll &= testTrue("DoubleValue-1", BigRational.valueOf("0.1").doubleValue() == 0.1);    passedAll &= testTrue("DoubleValue-2", BigRational.valueOf("1.1").doubleValue() == 1.1);    passedAll &= testTrue("IntValue-1", BigRational.valueOf("7/3").intValue() == 2);    passedAll &= testTrue("IntValue-2", BigRational.valueOf("8/3").intValue() == 2);    passedAll &= testTrue("IntValue-2", BigRational.valueOf("9/3").intValue() == 3);    passedAll &= testEquals("ToBigDecimal-1", BigRational.valueOf("0/3").toBigDecimal(10, RoundingMode.HALF_EVEN), "0.0000000000");    passedAll &= testEquals("ToBigDecimal-2", BigRational.valueOf("1/3").toBigDecimal(10, RoundingMode.HALF_EVEN), "0.3333333333");    passedAll &= testEquals("ToBigDecimal-3", BigRational.valueOf("2/3").toBigDecimal(10, RoundingMode.HALF_EVEN), "0.6666666667");    passedAll &= testEquals("ToBigDecimal-4", BigRational.valueOf("3/3").toBigDecimal(10, RoundingMode.HALF_EVEN), "1.000000000");    passedAll &= testEquals("ToBigDecimal-5", BigRational.valueOf("4/3").toBigDecimal(10, RoundingMode.HALF_EVEN), "1.333333333");    passedAll &= testEquals("ToBigDecimal-6", BigRational.valueOf("5/3").toBigDecimal(10, RoundingMode.HALF_EVEN), "1.666666667");    passedAll &= testEquals("ToBigDecimal-7", BigRational.valueOf("-1/3").toBigDecimal(10, RoundingMode.HALF_EVEN), "-0.3333333333");    passedAll &= testEquals("ToBigDecimal-8", BigRational.valueOf("-2/3").toBigDecimal(10, RoundingMode.HALF_EVEN), "-0.6666666667");    passedAll &= testEquals("ToBigDecimal-9", BigRational.valueOf("-3/3").toBigDecimal(10, RoundingMode.HALF_EVEN), "-1.000000000");    passedAll &= testEquals("Max-1", BigRational.valueOf("2/5").max(BigRational.valueOf("3/7")), "3/7");    passedAll &= testEquals("Max-2", BigRational.valueOf("2/5").max(BigRational.valueOf("-3/7")), "2/5");    passedAll &= testEquals("Max-3", BigRational.valueOf("-2/5").max(BigRational.valueOf("-3/7")), "-2/5");    passedAll &= testEquals("Min-1", BigRational.valueOf("2/5").min(BigRational.valueOf("3/7")), "2/5");    passedAll &= testEquals("Min-2", BigRational.valueOf("2/5").min(BigRational.valueOf("-3/7")), "-3/7");    passedAll &= testEquals("Min-3", BigRational.valueOf("-2/5").min(BigRational.valueOf("-3/7")), "-3/7");    Object[] mixedFraction1 = BigRational.valueOf("7/3").mixedFraction();    passedAll &= testEquals("MixedFraction-1", mixedFraction1[0], "2");    passedAll &= testEquals("MixedFraction-2", mixedFraction1[1], "1/3");    Object[] mixedFraction2 = BigRational.valueOf("-7/3").mixedFraction();    passedAll &= testEquals("MixedFraction-3", mixedFraction2[0], "-2");    passedAll &= testEquals("MixedFraction-4", mixedFraction2[1], "-1/3");    passedAll &= testEquals("Multiply-1", BigRational.valueOf("2/3").multiply(BigRational.valueOf("3/5")), "2/5");    passedAll &= testEquals("Multiply-2", BigRational.valueOf("-2/3").multiply(BigRational.valueOf("3/5")), "-2/5");    passedAll &= testEquals("Multiply-3", BigRational.valueOf("2/3").multiply(BigRational.valueOf("-3/5")), "-2/5");    passedAll &= testEquals("Multiply-4", BigRational.valueOf("-2/3").multiply(BigRational.valueOf("-3/5")), "2/5");    passedAll &= testEquals("Multiply-5", BigRational.valueOf("2/3").multiply(BigInteger.valueOf(4)), "8/3");    passedAll &= testEquals("Multiply-6", BigRational.valueOf("-2/3").multiply(BigInteger.valueOf(4)), "-8/3");    passedAll &= testEquals("Multiply-7", BigRational.valueOf("2/3").multiply(BigInteger.valueOf(-4)), "-8/3");    passedAll &= testEquals("Multiply-8", BigRational.valueOf("-2/3").multiply(BigInteger.valueOf(-4)), "8/3");    passedAll &= testEquals("Negate-1", BigRational.valueOf("2/3").negate(), "-2/3");    passedAll &= testEquals("Negate-2", BigRational.valueOf("-2/3").negate(), "2/3");    passedAll &= testEquals("Power-1", BigRational.valueOf("2/3").pow(3), "8/27");    passedAll &= testEquals("Power-2", BigRational.valueOf("-2/3").pow(3), "-8/27");    passedAll &= testEquals("Power-3", BigRational.valueOf("-2/3").pow(4), "16/81");    passedAll &= testEquals("Reciprocal-1", BigRational.valueOf("2/3").reciprocal(), "3/2");    passedAll &= testEquals("Reciprocal-2", BigRational.valueOf("-2/3").reciprocal(), "-3/2");    passedAll &= testEquals("Reciprocal-3", BigRational.valueOf("1/7").reciprocal(), "7");    passedAll &= testEquals("Reciprocal-4", BigRational.valueOf("-1/7").reciprocal(), "-7");    passedAll &= testTrue("Signum-1", BigRational.valueOf("1/7").signum() == 1);    passedAll &= testTrue("Signum-2", BigRational.valueOf("0/7").signum() == 0);    passedAll &= testTrue("Signum-3", BigRational.valueOf("-1/7").signum() == -1);    passedAll &= testEquals("Numerator-1", BigRational.valueOf("2/7").numerator(), "2");    passedAll &= testEquals("Numerator-2", BigRational.valueOf("-2/7").numerator(), "2");    passedAll &= testEquals("Denominator-1", BigRational.valueOf("2/7").denominator(), "7");    passedAll &= testEquals("Denominator-2", BigRational.valueOf("-2/7").denominator(), "7");     System.out.println(passedAll ? "Passed all tests" : "FAILURE: DID NOT PASS ALL TESTS");    return passedAll;  }   public static void main(String[] args)  {    testFeatures();    return;  } } `