ch.obermuhlner.math.big.BigRational Maven / Gradle / Ivy
Show all versions of big-math Show documentation
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.
*
* 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.
*
*
*
* 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()}.
*
* The following basic calculations have no loss of precision:
*
* - {@link #add(BigRational)}
* - {@link #subtract(BigRational)}
* - {@link #multiply(BigRational)}
* - {@link #divide(BigRational)}
* - {@link #pow(int)}
*
*
* The following calculations are special cases of the ones listed above and have no loss of precision:
*
* - {@link #negate()}
* - {@link #reciprocal()}
* - {@link #increment()}
* - {@link #decrement()}
*
*
* 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).
*/
public class BigRational implements Comparable {
/**
* 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.
*
* Guaranteed to not be 0.
* Guaranteed to be positive.
*
* @return the denominator as BigInteger
*/
public BigInteger getDenominatorBigInteger() {
return denominator.toBigInteger();
}
/**
* Returns the denominator of this rational number as BigDecimal.
*
* Guaranteed to not be 0.
* Guaranteed to be positive.
*
* @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.
*
* Examples:
*
* BigRational.valueOf(3.5).integerPart()
returns BigRational.valueOf(3)
*
*
* @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.
*
* Examples:
*
* BigRational.valueOf(3.5).integerPart()
returns BigRational.valueOf(0.5)
*
*
* @return the fraction part of this rational number
*/
public BigRational fractionPart() {
return of(numerator.remainder(denominator), denominator);
}
/**
* Negates this rational number (inverting the sign).
*
* The result has no loss of precision.
*
* Examples:
*
* BigRational.valueOf(3.5).negate()
returns BigRational.valueOf(-3.5)
*
*
* @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).
*
* The result has no loss of precision.
*
* Examples:
*
* BigRational.valueOf(0.5).reciprocal()
returns BigRational.valueOf(2)
* BigRational.valueOf(-2).reciprocal()
returns BigRational.valueOf(-0.5)
*
*
* @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.
*
* The result has no loss of precision.
*
* Examples:
*
* BigRational.valueOf(-2).abs()
returns BigRational.valueOf(2)
* BigRational.valueOf(2).abs()
returns BigRational.valueOf(2)
*
*
* @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).
*
* This is functionally identical to
* this.add(BigRational.ONE)
* but slightly faster.
*
* The result has no loss of precision.
*
* @return the incremented rational number
*/
public BigRational increment() {
return of(numerator.add(denominator), denominator);
}
/**
* Calculates the decrement of this rational number (- 1).
*
* This is functionally identical to
* this.subtract(BigRational.ONE)
* but slightly faster.
*
* The result has no loss of precision.
*
* @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.
*
* The result has no loss of precision.
*
* @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.
*
* This is functionally identical to
* this.add(BigRational.valueOf(value))
* but slightly faster.
*
* The result has no loss of precision.
*
* @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.
*
* This is functionally identical to
* this.add(BigRational.valueOf(value))
* but slightly faster.
*
* The result has no loss of precision.
*
* @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.
*
* The result has no loss of precision.
*
* @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.
*
* This is functionally identical to
* this.subtract(BigRational.valueOf(value))
* but slightly faster.
*
* The result has no loss of precision.
*
* @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.
*
* This is functionally identical to
* this.subtract(BigRational.valueOf(value))
* but slightly faster.
*
* The result has no loss of precision.
*
* @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.
*
* The result has no loss of precision.
*
* @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.
*
* This is functionally identical to
* this.multiply(BigRational.valueOf(value))
* but slightly faster.
*
* The result has no loss of precision.
*
* @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.
*
* This is functionally identical to
* this.multiply(BigRational.valueOf(value))
* but slightly faster.
*
* The result has no loss of precision.
*
* @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.
*
* The result has no loss of precision.
*
* @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.
*
* This is functionally identical to
* this.divide(BigRational.valueOf(value))
* but slightly faster.
*
* The result has no loss of precision.
*
* @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.
*
* This is functionally identical to
* this.divide(BigRational.valueOf(value))
* but slightly faster.
*
* The result has no loss of precision.
*
* @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 true
if this rational number is zero (0), false
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 true
if this rational number is an integer number, false
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.
*
* Will return false
if this number is not reduced to the integer representation yet (e.g. 4/4 or 4/2)
*
* @return true
if this rational number is an integer number, false
if it has a fraction part
* @see #isInteger()
*/
private boolean isIntegerInternal() {
return denominator.compareTo(BigDecimal.ONE) == 0;
}
/**
* Calculates this rational number to the power (xy) of the specified argument.
*
* The result has no loss of precision.
*
* @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 this
or the argument value
*/
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 this
or the argument value
*/
private BigRational max(BigRational value) {
return compareTo(value) >= 0 ? this : value;
}
/**
* Returns a rational number with approximatively this
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 this
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".
*
* The resulting string is a valid input of the {@link #valueOf(String)} method.
*
* Examples:
*
* BigRational.valueOf(0.5).toRationalString()
returns "1/2"
* BigRational.valueOf(2).toRationalString()
returns "2"
* BigRational.valueOf(4, 4).toRationalString()
returns "4/4"
(not reduced)
*
*
* @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".
*
* The integer part is omitted if it is 0 (when this absolute rational number is smaller than 1).
* The fraction part is omitted it it is 0 (when this rational number is an integer).
* If this rational number is 0, then "0" is returned.
*
* Example: BigRational.valueOf(3.5).toIntegerRationalString()
returns "3 1/2"
.
*
* @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.
*
* Useful to create numbers like 3 1/2 (= three and a half = 3.5) by calling
* BigRational.valueOf(3, 1, 2)
.
* 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 BigRational.valueOf(-3, 1, 2)
.
*
* @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.
*
* The accepted string representations are:
*
* - Output of {@link BigRational#toString()} : "integerPart.fractionPart"
* - Output of {@link BigRational#toRationalString()} : "numerator/denominator"
* - Output of
toString()
of {@link BigDecimal}, {@link BigInteger}, {@link Integer}, ...
* - Output of
toString()
of {@link Double}, {@link Float} - except "Infinity", "-Infinity" and "NaN"
*
*
* @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 bernoulliCache = new ArrayList<>();
/**
* Calculates the Bernoulli number for the specified index.
*
* This function calculates the first Bernoulli numbers and therefore bernoulli(1)
returns -0.5
* Note that bernoulli(x)
for all odd x > 1 returns 0
*
*
* @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);
}
}