aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ch/obermuhlner/math/big/BigRational.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/ch/obermuhlner/math/big/BigRational.java')
-rw-r--r--src/main/java/ch/obermuhlner/math/big/BigRational.java1103
1 files changed, 1103 insertions, 0 deletions
diff --git a/src/main/java/ch/obermuhlner/math/big/BigRational.java b/src/main/java/ch/obermuhlner/math/big/BigRational.java
new file mode 100644
index 0000000000..2d7389c7de
--- /dev/null
+++ b/src/main/java/ch/obermuhlner/math/big/BigRational.java
@@ -0,0 +1,1103 @@
+package ch.obermuhlner.math.big;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.MathContext;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.IntStream;
+
+/**
+ * A rational number represented as a quotient of two values.
+ *
+ * <p>Basic calculations with rational numbers (+ - * /) have no loss of precision.
+ * This allows to use {@link BigRational} as a replacement for {@link BigDecimal} if absolute accuracy is desired.</p>
+ *
+ * <p><a href="http://en.wikipedia.org/wiki/Rational_number">Wikipedia: Rational number</a></p>
+ *
+ * <p>The values are internally stored as {@link BigDecimal} (for performance optimizations) but represented
+ * as {@link BigInteger} (for mathematical correctness)
+ * when accessed with {@link #getNumeratorBigInteger()} and {@link #getDenominatorBigInteger()}.</p>
+ *
+ * <p>The following basic calculations have no loss of precision:</p>
+ * <ul>
+ * <li>{@link #add(BigRational)}</li>
+ * <li>{@link #subtract(BigRational)}</li>
+ * <li>{@link #multiply(BigRational)}</li>
+ * <li>{@link #divide(BigRational)}</li>
+ * <li>{@link #pow(int)}</li>
+ * </ul>
+ *
+ * <p>The following calculations are special cases of the ones listed above and have no loss of precision:</p>
+ * <ul>
+ * <li>{@link #negate()}</li>
+ * <li>{@link #reciprocal()}</li>
+ * <li>{@link #increment()}</li>
+ * <li>{@link #decrement()}</li>
+ * </ul>
+ *
+ * <p>Any {@link BigRational} value can be converted into an arbitrary {@link #withPrecision(int) precision} (number of significant digits)
+ * or {@link #withScale(int) scale} (number of digits after the decimal point).</p>
+ */
+public class BigRational implements Comparable<BigRational> {
+
+ /**
+ * The value 0 as {@link BigRational}.
+ */
+ public static final BigRational ZERO = new BigRational(0);
+ /**
+ * The value 1 as {@link BigRational}.
+ */
+ public static final BigRational ONE = new BigRational(1);
+ /**
+ * The value 2 as {@link BigRational}.
+ */
+ public static final BigRational TWO = new BigRational(2);
+ /**
+ * The value 10 as {@link BigRational}.
+ */
+ public static final BigRational TEN = new BigRational(10);
+
+ private final BigDecimal numerator;
+
+ private final BigDecimal denominator;
+
+ private BigRational(int value) {
+ this(BigDecimal.valueOf(value), BigDecimal.ONE);
+ }
+
+ private BigRational(BigDecimal num, BigDecimal denom) {
+ BigDecimal n = num;
+ BigDecimal d = denom;
+
+ if (d.signum() == 0) {
+ throw new ArithmeticException("Divide by zero");
+ }
+
+ if (d.signum() < 0) {
+ n = n.negate();
+ d = d.negate();
+ }
+
+ numerator = n;
+ denominator = d;
+ }
+
+ /**
+ * Returns the numerator of this rational number as BigInteger.
+ *
+ * @return the numerator as BigInteger
+ */
+ public BigInteger getNumeratorBigInteger() {
+ return numerator.toBigInteger();
+ }
+
+ /**
+ * Returns the numerator of this rational number as BigDecimal.
+ *
+ * @return the numerator as BigDecimal
+ */
+ public BigDecimal getNumerator() {
+ return numerator;
+ }
+
+ /**
+ * Returns the denominator of this rational number as BigInteger.
+ *
+ * <p>Guaranteed to not be 0.</p>
+ * <p>Guaranteed to be positive.</p>
+ *
+ * @return the denominator as BigInteger
+ */
+ public BigInteger getDenominatorBigInteger() {
+ return denominator.toBigInteger();
+ }
+
+ /**
+ * Returns the denominator of this rational number as BigDecimal.
+ *
+ * <p>Guaranteed to not be 0.</p>
+ * <p>Guaranteed to be positive.</p>
+ *
+ * @return the denominator as BigDecimal
+ */
+ public BigDecimal getDenominator() {
+ return denominator;
+ }
+
+ /**
+ * Reduces this rational number to the smallest numerator/denominator with the same value.
+ *
+ * @return the reduced rational number
+ */
+ public BigRational reduce() {
+ BigInteger n = numerator.toBigInteger();
+ BigInteger d = denominator.toBigInteger();
+
+ BigInteger gcd = n.gcd(d);
+ n = n.divide(gcd);
+ d = d.divide(gcd);
+
+ return valueOf(n, d);
+ }
+
+ /**
+ * Returns the integer part of this rational number.
+ *
+ * <p>Examples:</p>
+ * <ul>
+ * <li><code>BigRational.valueOf(3.5).integerPart()</code> returns <code>BigRational.valueOf(3)</code></li>
+ * </ul>
+ *
+ * @return the integer part of this rational number
+ */
+ public BigRational integerPart() {
+ return of(numerator.subtract(numerator.remainder(denominator)), denominator);
+ }
+
+ /**
+ * Returns the fraction part of this rational number.
+ *
+ * <p>Examples:</p>
+ * <ul>
+ * <li><code>BigRational.valueOf(3.5).integerPart()</code> returns <code>BigRational.valueOf(0.5)</code></li>
+ * </ul>
+ *
+ * @return the fraction part of this rational number
+ */
+ public BigRational fractionPart() {
+ return of(numerator.remainder(denominator), denominator);
+ }
+
+ /**
+ * Negates this rational number (inverting the sign).
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * <p>Examples:</p>
+ * <ul>
+ * <li><code>BigRational.valueOf(3.5).negate()</code> returns <code>BigRational.valueOf(-3.5)</code></li>
+ * </ul>
+ *
+ * @return the negated rational number
+ */
+ public BigRational negate() {
+ if (isZero()) {
+ return this;
+ }
+
+ return of(numerator.negate(), denominator);
+ }
+
+ /**
+ * Calculates the reciprocal of this rational number (1/x).
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * <p>Examples:</p>
+ * <ul>
+ * <li><code>BigRational.valueOf(0.5).reciprocal()</code> returns <code>BigRational.valueOf(2)</code></li>
+ * <li><code>BigRational.valueOf(-2).reciprocal()</code> returns <code>BigRational.valueOf(-0.5)</code></li>
+ * </ul>
+ *
+ * @return the reciprocal rational number
+ * @throws ArithmeticException if this number is 0 (division by zero)
+ */
+ public BigRational reciprocal() {
+ return of(denominator, numerator);
+ }
+
+ /**
+ * Returns the absolute value of this rational number.
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * <p>Examples:</p>
+ * <ul>
+ * <li><code>BigRational.valueOf(-2).abs()</code> returns <code>BigRational.valueOf(2)</code></li>
+ * <li><code>BigRational.valueOf(2).abs()</code> returns <code>BigRational.valueOf(2)</code></li>
+ * </ul>
+ *
+ * @return the absolute rational number (positive, or 0 if this rational is 0)
+ */
+ public BigRational abs() {
+ return isPositive() ? this : negate();
+ }
+
+ /**
+ * Returns the signum function of this rational number.
+ *
+ * @return -1, 0 or 1 as the value of this rational number is negative, zero or positive.
+ */
+ public int signum() {
+ return numerator.signum();
+ }
+
+ /**
+ * Calculates the increment of this rational number (+ 1).
+ *
+ * <p>This is functionally identical to
+ * <code>this.add(BigRational.ONE)</code>
+ * but slightly faster.</p>
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @return the incremented rational number
+ */
+ public BigRational increment() {
+ return of(numerator.add(denominator), denominator);
+ }
+
+ /**
+ * Calculates the decrement of this rational number (- 1).
+ *
+ * <p>This is functionally identical to
+ * <code>this.subtract(BigRational.ONE)</code>
+ * but slightly faster.</p>
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @return the decremented rational number
+ */
+ public BigRational decrement() {
+ return of(numerator.subtract(denominator), denominator);
+ }
+
+ /**
+ * Calculates the addition (+) of this rational number and the specified argument.
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the rational number to add
+ * @return the resulting rational number
+ */
+ public BigRational add(BigRational value) {
+ if (denominator.equals(value.denominator)) {
+ return of(numerator.add(value.numerator), denominator);
+ }
+
+ BigDecimal n = numerator.multiply(value.denominator).add(value.numerator.multiply(denominator));
+ BigDecimal d = denominator.multiply(value.denominator);
+ return of(n, d);
+ }
+
+ private BigRational add(BigDecimal value) {
+ return of(numerator.add(value.multiply(denominator)), denominator);
+ }
+
+ /**
+ * Calculates the addition (+) of this rational number and the specified argument.
+ *
+ * <p>This is functionally identical to
+ * <code>this.add(BigRational.valueOf(value))</code>
+ * but slightly faster.</p>
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the {@link BigInteger} to add
+ * @return the resulting rational number
+ */
+ public BigRational add(BigInteger value) {
+ if (value.equals(BigInteger.ZERO)) {
+ return this;
+ }
+ return add(new BigDecimal(value));
+ }
+
+ /**
+ * Calculates the addition (+) of this rational number and the specified argument.
+ *
+ * <p>This is functionally identical to
+ * <code>this.add(BigRational.valueOf(value))</code>
+ * but slightly faster.</p>
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the int value to add
+ * @return the resulting rational number
+ */
+ public BigRational add(int value) {
+ if (value == 0) {
+ return this;
+ }
+ return add(BigInteger.valueOf(value));
+ }
+
+ /**
+ * Calculates the subtraction (-) of this rational number and the specified argument.
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the rational number to subtract
+ * @return the resulting rational number
+ */
+ public BigRational subtract(BigRational value) {
+ if (denominator.equals(value.denominator)) {
+ return of(numerator.subtract(value.numerator), denominator);
+ }
+
+ BigDecimal n = numerator.multiply(value.denominator).subtract(value.numerator.multiply(denominator));
+ BigDecimal d = denominator.multiply(value.denominator);
+ return of(n, d);
+ }
+
+ private BigRational subtract(BigDecimal value) {
+ return of(numerator.subtract(value.multiply(denominator)), denominator);
+ }
+
+ /**
+ * Calculates the subtraction (-) of this rational number and the specified argument.
+ *
+ * <p>This is functionally identical to
+ * <code>this.subtract(BigRational.valueOf(value))</code>
+ * but slightly faster.</p>
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the {@link BigInteger} to subtract
+ * @return the resulting rational number
+ */
+ public BigRational subtract(BigInteger value) {
+ if (value.equals(BigInteger.ZERO)) {
+ return this;
+ }
+ return subtract(new BigDecimal(value));
+ }
+
+ /**
+ * Calculates the subtraction (-) of this rational number and the specified argument.
+ *
+ * <p>This is functionally identical to
+ * <code>this.subtract(BigRational.valueOf(value))</code>
+ * but slightly faster.</p>
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the int value to subtract
+ * @return the resulting rational number
+ */
+ public BigRational subtract(int value) {
+ if (value == 0) {
+ return this;
+ }
+ return subtract(BigInteger.valueOf(value));
+ }
+
+ /**
+ * Calculates the multiplication (*) of this rational number and the specified argument.
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the rational number to multiply
+ * @return the resulting rational number
+ */
+ public BigRational multiply(BigRational value) {
+ if (isZero() || value.isZero()) {
+ return ZERO;
+ }
+ if (equals(ONE)) {
+ return value;
+ }
+ if (value.equals(ONE)) {
+ return this;
+ }
+
+ BigDecimal n = numerator.multiply(value.numerator);
+ BigDecimal d = denominator.multiply(value.denominator);
+ return of(n, d);
+ }
+
+ // private, because we want to hide that we use BigDecimal internally
+ private BigRational multiply(BigDecimal value) {
+ BigDecimal n = numerator.multiply(value);
+ BigDecimal d = denominator;
+ return of(n, d);
+ }
+
+ /**
+ * Calculates the multiplication (*) of this rational number and the specified argument.
+ *
+ * <p>This is functionally identical to
+ * <code>this.multiply(BigRational.valueOf(value))</code>
+ * but slightly faster.</p>
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the {@link BigInteger} to multiply
+ * @return the resulting rational number
+ */
+ public BigRational multiply(BigInteger value) {
+ if (isZero() || value.signum() == 0) {
+ return ZERO;
+ }
+ if (equals(ONE)) {
+ return valueOf(value);
+ }
+ if (value.equals(BigInteger.ONE)) {
+ return this;
+ }
+
+ return multiply(new BigDecimal(value));
+ }
+
+ /**
+ * Calculates the multiplication (*) of this rational number and the specified argument.
+ *
+ * <p>This is functionally identical to
+ * <code>this.multiply(BigRational.valueOf(value))</code>
+ * but slightly faster.</p>
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the int value to multiply
+ * @return the resulting rational number
+ */
+ public BigRational multiply(int value) {
+ return multiply(BigInteger.valueOf(value));
+ }
+
+ /**
+ * Calculates the division (/) of this rational number and the specified argument.
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the rational number to divide (0 is not allowed)
+ * @return the resulting rational number
+ * @throws ArithmeticException if the argument is 0 (division by zero)
+ */
+ public BigRational divide(BigRational value) {
+ if (value.equals(ONE)) {
+ return this;
+ }
+
+ BigDecimal n = numerator.multiply(value.denominator);
+ BigDecimal d = denominator.multiply(value.numerator);
+ return of(n, d);
+ }
+
+ private BigRational divide(BigDecimal value) {
+ BigDecimal n = numerator;
+ BigDecimal d = denominator.multiply(value);
+ return of(n, d);
+ }
+
+ /**
+ * Calculates the division (/) of this rational number and the specified argument.
+ *
+ * <p>This is functionally identical to
+ * <code>this.divide(BigRational.valueOf(value))</code>
+ * but slightly faster.</p>
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the {@link BigInteger} to divide (0 is not allowed)
+ * @return the resulting rational number
+ * @throws ArithmeticException if the argument is 0 (division by zero)
+ */
+ public BigRational divide(BigInteger value) {
+ if (value.equals(BigInteger.ONE)) {
+ return this;
+ }
+
+ return divide(new BigDecimal(value));
+ }
+
+ /**
+ * Calculates the division (/) of this rational number and the specified argument.
+ *
+ * <p>This is functionally identical to
+ * <code>this.divide(BigRational.valueOf(value))</code>
+ * but slightly faster.</p>
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param value the int value to divide (0 is not allowed)
+ * @return the resulting rational number
+ * @throws ArithmeticException if the argument is 0 (division by zero)
+ */
+ public BigRational divide(int value) {
+ return divide(BigInteger.valueOf(value));
+ }
+
+ /**
+ * Returns whether this rational number is zero.
+ *
+ * @return <code>true</code> if this rational number is zero (0), <code>false</code> if it is not zero
+ */
+ public boolean isZero() {
+ return numerator.signum() == 0;
+ }
+
+ private boolean isPositive() {
+ return numerator.signum() > 0;
+ }
+
+ /**
+ * Returns whether this rational number is an integer number without fraction part.
+ *
+ * @return <code>true</code> if this rational number is an integer number, <code>false</code> if it has a fraction part
+ */
+ public boolean isInteger() {
+ return isIntegerInternal() || reduce().isIntegerInternal();
+ }
+
+ /**
+ * Returns whether this rational number is an integer number without fraction part.
+ *
+ * <p>Will return <code>false</code> if this number is not reduced to the integer representation yet (e.g. 4/4 or 4/2)</p>
+ *
+ * @return <code>true</code> if this rational number is an integer number, <code>false</code> if it has a fraction part
+ * @see #isInteger()
+ */
+ private boolean isIntegerInternal() {
+ return denominator.compareTo(BigDecimal.ONE) == 0;
+ }
+
+ /**
+ * Calculates this rational number to the power (x<sup>y</sup>) of the specified argument.
+ *
+ * <p>The result has no loss of precision.</p>
+ *
+ * @param exponent exponent to which this rational number is to be raised
+ * @return the resulting rational number
+ */
+ public BigRational pow(int exponent) {
+ if (exponent == 0) {
+ return ONE;
+ }
+ if (exponent == 1) {
+ return this;
+ }
+
+ final BigInteger n;
+ final BigInteger d;
+ if (exponent > 0) {
+ n = numerator.toBigInteger().pow(exponent);
+ d = denominator.toBigInteger().pow(exponent);
+ }
+ else {
+ n = denominator.toBigInteger().pow(-exponent);
+ d = numerator.toBigInteger().pow(-exponent);
+ }
+ return valueOf(n, d);
+ }
+
+ /**
+ * Finds the minimum (smaller) of two rational numbers.
+ *
+ * @param value the rational number to compare with
+ * @return the minimum rational number, either <code>this</code> or the argument <code>value</code>
+ */
+ private BigRational min(BigRational value) {
+ return compareTo(value) <= 0 ? this : value;
+ }
+
+ /**
+ * Finds the maximum (larger) of two rational numbers.
+ *
+ * @param value the rational number to compare with
+ * @return the minimum rational number, either <code>this</code> or the argument <code>value</code>
+ */
+ private BigRational max(BigRational value) {
+ return compareTo(value) >= 0 ? this : value;
+ }
+
+ /**
+ * Returns a rational number with approximatively <code>this</code> value and the specified precision.
+ *
+ * @param precision the precision (number of significant digits) of the calculated result, or 0 for unlimited precision
+ * @return the calculated rational number with the specified precision
+ */
+ public BigRational withPrecision(int precision) {
+ return valueOf(toBigDecimal(new MathContext(precision)));
+ }
+
+ /**
+ * Returns a rational number with approximatively <code>this</code> value and the specified scale.
+ *
+ * @param scale the scale (number of digits after the decimal point) of the calculated result
+ * @return the calculated rational number with the specified scale
+ */
+ public BigRational withScale(int scale) {
+ return valueOf(toBigDecimal().setScale(scale, RoundingMode.HALF_UP));
+ }
+
+ private static int countDigits(BigInteger number) {
+ double factor = Math.log(2) / Math.log(10);
+ int digitCount = (int) (factor * number.bitLength() + 1);
+ if (BigInteger.TEN.pow(digitCount - 1).compareTo(number) > 0) {
+ return digitCount - 1;
+ }
+ return digitCount;
+ }
+
+ // TODO what is precision of a rational?
+ private int precision() {
+ return countDigits(numerator.toBigInteger()) + countDigits(denominator.toBigInteger());
+ }
+
+ /**
+ * Returns this rational number as a double value.
+ *
+ * @return the double value
+ */
+ public double toDouble() {
+ // TODO best accuracy or maybe bigDecimalValue().doubleValue() is better?
+ return numerator.doubleValue() / denominator.doubleValue();
+ }
+
+ /**
+ * Returns this rational number as a float value.
+ *
+ * @return the float value
+ */
+ public float toFloat() {
+ return numerator.floatValue() / denominator.floatValue();
+ }
+
+ /**
+ * Returns this rational number as a {@link BigDecimal}.
+ *
+ * @return the {@link BigDecimal} value
+ */
+ public BigDecimal toBigDecimal() {
+ int precision = Math.max(precision(), MathContext.DECIMAL128.getPrecision());
+ return toBigDecimal(new MathContext(precision));
+ }
+
+ /**
+ * Returns this rational number as a {@link BigDecimal} with the precision specified by the {@link MathContext}.
+ *
+ * @param mc the {@link MathContext} specifying the precision of the calculated result
+ * @return the {@link BigDecimal}
+ */
+ public BigDecimal toBigDecimal(MathContext mc) {
+ return numerator.divide(denominator, mc);
+ }
+
+ @Override
+ public int compareTo(BigRational other) {
+ if (this == other) {
+ return 0;
+ }
+ return numerator.multiply(other.denominator).compareTo(denominator.multiply(other.numerator));
+ }
+
+ @Override
+ public int hashCode() {
+ if (isZero()) {
+ return 0;
+ }
+ return numerator.hashCode() + denominator.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof BigRational)) {
+ return false;
+ }
+
+ BigRational other = (BigRational) obj;
+ if (!numerator.equals(other.numerator)) {
+ return false;
+ }
+ return denominator.equals(other.denominator);
+ }
+
+ @Override
+ public String toString() {
+ if (isZero()) {
+ return "0";
+ }
+ if (isIntegerInternal()) {
+ return numerator.toString();
+ }
+ return toBigDecimal().toString();
+ }
+
+ /**
+ * Returns a plain string representation of this rational number without any exponent.
+ *
+ * @return the plain string representation
+ * @see BigDecimal#toPlainString()
+ */
+ public String toPlainString() {
+ if (isZero()) {
+ return "0";
+ }
+ if (isIntegerInternal()) {
+ return numerator.toPlainString();
+ }
+ return toBigDecimal().toPlainString();
+ }
+
+ /**
+ * Returns the string representation of this rational number in the form "numerator/denominator".
+ *
+ * <p>The resulting string is a valid input of the {@link #valueOf(String)} method.</p>
+ *
+ * <p>Examples:</p>
+ * <ul>
+ * <li><code>BigRational.valueOf(0.5).toRationalString()</code> returns <code>"1/2"</code></li>
+ * <li><code>BigRational.valueOf(2).toRationalString()</code> returns <code>"2"</code></li>
+ * <li><code>BigRational.valueOf(4, 4).toRationalString()</code> returns <code>"4/4"</code> (not reduced)</li>
+ * </ul>
+ *
+ * @return the rational number string representation in the form "numerator/denominator", or "0" if the rational number is 0.
+ * @see #valueOf(String)
+ * @see #valueOf(int, int)
+ */
+ public String toRationalString() {
+ if (isZero()) {
+ return "0";
+ }
+ if (isIntegerInternal()) {
+ return numerator.toString();
+ }
+ return numerator + "/" + denominator;
+ }
+
+ /**
+ * Returns the string representation of this rational number as integer and fraction parts in the form "integerPart fractionNominator/fractionDenominator".
+ *
+ * <p>The integer part is omitted if it is 0 (when this absolute rational number is smaller than 1).</p>
+ * <p>The fraction part is omitted it it is 0 (when this rational number is an integer).</p>
+ * <p>If this rational number is 0, then "0" is returned.</p>
+ *
+ * <p>Example: <code>BigRational.valueOf(3.5).toIntegerRationalString()</code> returns <code>"3 1/2"</code>.</p>
+ *
+ * @return the integer and fraction rational string representation
+ * @see #valueOf(int, int, int)
+ */
+ public String toIntegerRationalString() {
+ BigDecimal fractionNumerator = numerator.remainder(denominator);
+ BigDecimal integerNumerator = numerator.subtract(fractionNumerator);
+ BigDecimal integerPart = integerNumerator.divide(denominator);
+
+ StringBuilder result = new StringBuilder();
+ if (integerPart.signum() != 0) {
+ result.append(integerPart);
+ }
+ if (fractionNumerator.signum() != 0) {
+ if (result.length() > 0) {
+ result.append(' ');
+ }
+ result.append(fractionNumerator.abs());
+ result.append('/');
+ result.append(denominator);
+ }
+ if (result.length() == 0) {
+ result.append('0');
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Creates a rational number of the specified int value.
+ *
+ * @param value the int value
+ * @return the rational number
+ */
+ public static BigRational valueOf(int value) {
+ if (value == 0) {
+ return ZERO;
+ }
+ if (value == 1) {
+ return ONE;
+ }
+ return new BigRational(value);
+ }
+
+ /**
+ * Creates a rational number of the specified numerator/denominator int values.
+ *
+ * @param numerator the numerator int value
+ * @param denominator the denominator int value (0 not allowed)
+ * @return the rational number
+ * @throws ArithmeticException if the denominator is 0 (division by zero)
+ */
+ public static BigRational valueOf(int numerator, int denominator) {
+ return of(BigDecimal.valueOf(numerator), BigDecimal.valueOf(denominator));
+ }
+
+ /**
+ * Creates a rational number of the specified integer and fraction parts.
+ *
+ * <p>Useful to create numbers like 3 1/2 (= three and a half = 3.5) by calling
+ * <code>BigRational.valueOf(3, 1, 2)</code>.</p>
+ * <p>To create a negative rational only the integer part argument is allowed to be negative:
+ * to create -3 1/2 (= minus three and a half = -3.5) call <code>BigRational.valueOf(-3, 1, 2)</code>.</p>
+ *
+ * @param integer the integer part int value
+ * @param fractionNumerator the fraction part numerator int value (negative not allowed)
+ * @param fractionDenominator the fraction part denominator int value (0 or negative not allowed)
+ * @return the rational number
+ * @throws ArithmeticException if the fraction part denominator is 0 (division by zero),
+ * or if the fraction part numerator or denominator is negative
+ */
+ public static BigRational valueOf(int integer, int fractionNumerator, int fractionDenominator) {
+ if (fractionNumerator < 0 || fractionDenominator < 0) {
+ throw new ArithmeticException("Negative value");
+ }
+
+ BigRational integerPart = valueOf(integer);
+ BigRational fractionPart = valueOf(fractionNumerator, fractionDenominator);
+ return integerPart.isPositive() ? integerPart.add(fractionPart) : integerPart.subtract(fractionPart);
+ }
+
+ /**
+ * Creates a rational number of the specified numerator/denominator BigInteger values.
+ *
+ * @param numerator the numerator {@link BigInteger} value
+ * @param denominator the denominator {@link BigInteger} value (0 not allowed)
+ * @return the rational number
+ * @throws ArithmeticException if the denominator is 0 (division by zero)
+ */
+ public static BigRational valueOf(BigInteger numerator, BigInteger denominator) {
+ return of(new BigDecimal(numerator), new BigDecimal(denominator));
+ }
+
+ /**
+ * Creates a rational number of the specified {@link BigInteger} value.
+ *
+ * @param value the {@link BigInteger} value
+ * @return the rational number
+ */
+ public static BigRational valueOf(BigInteger value) {
+ if (value.compareTo(BigInteger.ZERO) == 0) {
+ return ZERO;
+ }
+ if (value.compareTo(BigInteger.ONE) == 0) {
+ return ONE;
+ }
+ return valueOf(value, BigInteger.ONE);
+ }
+
+ /**
+ * Creates a rational number of the specified double value.
+ *
+ * @param value the double value
+ * @return the rational number
+ * @throws NumberFormatException if the double value is Infinite or NaN.
+ */
+ public static BigRational valueOf(double value) {
+ if (value == 0.0) {
+ return ZERO;
+ }
+ if (value == 1.0) {
+ return ONE;
+ }
+ if (Double.isInfinite(value)) {
+ throw new NumberFormatException("Infinite");
+ }
+ if (Double.isNaN(value)) {
+ throw new NumberFormatException("NaN");
+ }
+ return valueOf(new BigDecimal(String.valueOf(value)));
+ }
+
+ /**
+ * Creates a rational number of the specified {@link BigDecimal} value.
+ *
+ * @param value the double value
+ * @return the rational number
+ */
+ public static BigRational valueOf(BigDecimal value) {
+ if (value.compareTo(BigDecimal.ZERO) == 0) {
+ return ZERO;
+ }
+ if (value.compareTo(BigDecimal.ONE) == 0) {
+ return ONE;
+ }
+
+ int scale = value.scale();
+ if (scale == 0) {
+ return new BigRational(value, BigDecimal.ONE);
+ } else if (scale < 0) {
+ BigDecimal n = new BigDecimal(value.unscaledValue()).multiply(BigDecimal.ONE.movePointLeft(value.scale()));
+ return new BigRational(n, BigDecimal.ONE);
+ }
+ else {
+ BigDecimal n = new BigDecimal(value.unscaledValue());
+ BigDecimal d = BigDecimal.ONE.movePointRight(value.scale());
+ return new BigRational(n, d);
+ }
+ }
+
+ /**
+ * Creates a rational number of the specified string representation.
+ *
+ * <p>The accepted string representations are:</p>
+ * <ul>
+ * <li>Output of {@link BigRational#toString()} : "integerPart.fractionPart"</li>
+ * <li>Output of {@link BigRational#toRationalString()} : "numerator/denominator"</li>
+ * <li>Output of <code>toString()</code> of {@link BigDecimal}, {@link BigInteger}, {@link Integer}, ...</li>
+ * <li>Output of <code>toString()</code> of {@link Double}, {@link Float} - except "Infinity", "-Infinity" and "NaN"</li>
+ * </ul>
+ *
+ * @param string the string representation to convert
+ * @return the rational number
+ * @throws ArithmeticException if the denominator is 0 (division by zero)
+ */
+ public static BigRational valueOf(String string) {
+ String[] strings = string.split("/");
+ BigRational result = valueOfSimple(strings[0]);
+ for (int i = 1; i < strings.length; i++) {
+ result = result.divide(valueOfSimple(strings[i]));
+ }
+ return result;
+ }
+
+ private static BigRational valueOfSimple(String string) {
+ return valueOf(new BigDecimal(string));
+ }
+
+ public static BigRational valueOf(boolean positive, String integerPart, String fractionPart, String fractionRepeatPart, String exponentPart) {
+ BigRational result = ZERO;
+
+ if (fractionRepeatPart != null && fractionRepeatPart.length() > 0) {
+ BigInteger lotsOfNines = BigInteger.TEN.pow(fractionRepeatPart.length()).subtract(BigInteger.ONE);
+ result = valueOf(new BigInteger(fractionRepeatPart), lotsOfNines);
+ }
+
+ if (fractionPart != null && fractionPart.length() > 0) {
+ result = result.add(valueOf(new BigInteger(fractionPart)));
+ result = result.divide(BigInteger.TEN.pow(fractionPart.length()));
+ }
+
+ if (integerPart != null && integerPart.length() > 0) {
+ result = result.add(new BigInteger(integerPart));
+ }
+
+ if (exponentPart != null && exponentPart.length() > 0) {
+ int exponent = Integer.parseInt(exponentPart);
+ BigInteger powerOfTen = BigInteger.TEN.pow(Math.abs(exponent));
+ result = exponent >= 0 ? result.multiply(powerOfTen) : result.divide(powerOfTen);
+ }
+
+ if (!positive) {
+ result = result.negate();
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates a rational number of the specified numerator/denominator BigDecimal values.
+ *
+ * @param numerator the numerator {@link BigDecimal} value
+ * @param denominator the denominator {@link BigDecimal} value (0 not allowed)
+ * @return the rational number
+ * @throws ArithmeticException if the denominator is 0 (division by zero)
+ */
+ public static BigRational valueOf(BigDecimal numerator, BigDecimal denominator) {
+ return valueOf(numerator).divide(valueOf(denominator));
+ }
+
+ private static BigRational of(BigDecimal numerator, BigDecimal denominator) {
+ if (numerator.signum() == 0 && denominator.signum() != 0) {
+ return ZERO;
+ }
+ if (numerator.compareTo(BigDecimal.ONE) == 0 && denominator.compareTo(BigDecimal.ONE) == 0) {
+ return ONE;
+ }
+ return new BigRational(numerator, denominator);
+ }
+
+ /**
+ * Returns the smallest of the specified rational numbers.
+ *
+ * @param values the rational numbers to compare
+ * @return the smallest rational number, 0 if no numbers are specified
+ */
+ public static BigRational min(BigRational... values) {
+ if (values.length == 0) {
+ return BigRational.ZERO;
+ }
+ BigRational result = values[0];
+ for (int i = 1; i < values.length; i++) {
+ result = result.min(values[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Returns the largest of the specified rational numbers.
+ *
+ * @param values the rational numbers to compare
+ * @return the largest rational number, 0 if no numbers are specified
+ * @see #max(BigRational)
+ */
+ public static BigRational max(BigRational... values) {
+ if (values.length == 0) {
+ return BigRational.ZERO;
+ }
+ BigRational result = values[0];
+ for (int i = 1; i < values.length; i++) {
+ result = result.max(values[i]);
+ }
+ return result;
+ }
+
+ private static List<BigRational> bernoulliCache = new ArrayList<>();
+
+ /**
+ * Calculates the Bernoulli number for the specified index.
+ *
+ * <p>This function calculates the <strong>first Bernoulli numbers</strong> and therefore <code>bernoulli(1)</code> returns -0.5</p>
+ * <p>Note that <code>bernoulli(x)</code> for all odd x &gt; 1 returns 0</p>
+ * <p>See: <a href="https://en.wikipedia.org/wiki/Bernoulli_number">Wikipedia: Bernoulli number</a></p>
+ *
+ * @param n the index of the Bernoulli number to be calculated (starting at 0)
+ * @return the Bernoulli number for the specified index
+ * @throws ArithmeticException if x is lesser than 0
+ */
+ public static BigRational bernoulli(int n) {
+ if (n < 0) {
+ throw new ArithmeticException("Illegal bernoulli(n) for n < 0: n = " + n);
+ }
+ if (n == 1) {
+ return valueOf(-1, 2);
+ } else if (n % 2 == 1) {
+ return ZERO;
+ }
+
+ synchronized (bernoulliCache) {
+ int index = n / 2;
+
+ if (bernoulliCache.size() <= index) {
+ for (int i = bernoulliCache.size(); i <= index; i++) {
+ BigRational b = calculateBernoulli(i * 2);
+ bernoulliCache.add(b);
+ }
+ }
+
+ return bernoulliCache.get(index);
+ }
+ }
+
+ private static BigRational calculateBernoulli(int n) {
+ return IntStream.rangeClosed(0, n).parallel().mapToObj(k -> {
+ BigRational jSum = ZERO ;
+ BigRational bin = ONE ;
+ for(int j=0 ; j <= k ; j++) {
+ BigRational jPowN = valueOf(j).pow(n);
+ if (j % 2 == 0) {
+ jSum = jSum.add(bin.multiply(jPowN)) ;
+ } else {
+ jSum = jSum.subtract(bin.multiply(jPowN)) ;
+ }
+
+ bin = bin.multiply(valueOf(k-j).divide(valueOf(j+1)));
+ }
+ return jSum.divide(valueOf(k+1));
+ }).reduce(ZERO, BigRational::add);
+ }
+
+}