diff options
author | Tec <daniel112092@gmail.com> | 2020-07-18 22:49:35 +0200 |
---|---|---|
committer | Tec <daniel112092@gmail.com> | 2020-07-18 22:49:35 +0200 |
commit | 11e50eb56f6750d6bdfe15b986eef55e27452211 (patch) | |
tree | f6d4e90967e76c2fa2f988a823340fa85b9a604b | |
parent | 5c68b296e1b499ca83d78f24837768ac3a75df62 (diff) | |
download | GT5-Unofficial-11e50eb56f6750d6bdfe15b986eef55e27452211.tar.gz GT5-Unofficial-11e50eb56f6750d6bdfe15b986eef55e27452211.tar.bz2 GT5-Unofficial-11e50eb56f6750d6bdfe15b986eef55e27452211.zip |
Implement big float, transition to molarity
33 files changed, 7540 insertions, 49 deletions
diff --git a/src/main/java/ch/obermuhlner/math/big/BigComplex.java b/src/main/java/ch/obermuhlner/math/big/BigComplex.java new file mode 100644 index 0000000000..a4620ff53b --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/BigComplex.java @@ -0,0 +1,556 @@ +package ch.obermuhlner.math.big; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.Objects; + +/** + * Represents a complex number consisting of a real and an imaginary {@link BigDecimal} part in the form {@code a + bi}. + * + * <p>It generally follows the design of {@link BigDecimal} with some convenience improvements like overloaded operator methods.</p> + * + * <p>The biggest difference to {@link BigDecimal} is that {@link BigComplex#equals(Object) BigComplex.equals(Object)} implements the <strong>mathematical</strong> equality + * and <strong>not</strong> the strict technical equality. + * This was a difficult decision because it means that {@code BigComplex} behaves slightly different than {@link BigDecimal} + * but considering that the strange equality of {@link BigDecimal} is a major source of bugs we + * decided it was worth the slight inconsistency. + * If you need the strict equality use {@link BigComplex#strictEquals(Object)}`.</p> + * + * <p>This class is immutable and therefore inherently thread safe.</p> + */ +public final class BigComplex { + + /** + * Zero represented as complex number. + */ + public static final BigComplex ZERO = new BigComplex(BigDecimal.ZERO, BigDecimal.ZERO); + + /** + * Real 1 represented as complex number. + */ + public static final BigComplex ONE = new BigComplex(BigDecimal.ONE, BigDecimal.ZERO); + + /** + * Imaginary 1 represented as complex number. + */ + public static final BigComplex I = new BigComplex(BigDecimal.ZERO, BigDecimal.ONE); + + /** + * The real {@link BigDecimal} part of this complex number. + */ + public final BigDecimal re; + + /** + * The imaginary {@link BigDecimal} part of this complex number. + */ + public final BigDecimal im; + + private BigComplex(BigDecimal re, BigDecimal im) { + this.re = re; + this.im = im; + } + + /** + * Calculates the addition of the given complex value to this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the {@link BigComplex} value to add + * @return the calculated {@link BigComplex} result + */ + public BigComplex add(BigComplex value) { + return valueOf( + re.add(value.re), + im.add(value.im)); + } + + /** + * Calculates the addition of the given complex value to this complex number using the specified {@link MathContext}. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the {@link BigComplex} value to add + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + */ + public BigComplex add(BigComplex value, MathContext mathContext) { + return valueOf( + re.add(value.re, mathContext), + im.add(value.im, mathContext)); + } + + /** + * Calculates the addition of the given real {@link BigDecimal} value to this complex number using the specified {@link MathContext}. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the real {@link BigDecimal} value to add + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + */ + public BigComplex add(BigDecimal value, MathContext mathContext) { + return valueOf( + re.add(value, mathContext), + im); + } + + /** + * Calculates the addition of the given real {@link BigDecimal} value to this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the real {@link BigDecimal} value to add + * @return the calculated {@link BigComplex} result + */ + public BigComplex add(BigDecimal value) { + return valueOf( + re.add(value), + im); + } + + /** + * Calculates the addition of the given real {@code double} value to this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the real {@code double} value to add + * @return the calculated {@link BigComplex} result + */ + public BigComplex add(double value) { + return add(BigDecimal.valueOf(value)); + } + + /** + * Calculates the subtraction of the given complex value from this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the {@link BigComplex} value to subtract + * @return the calculated {@link BigComplex} result + */ + public BigComplex subtract(BigComplex value) { + return valueOf( + re.subtract(value.re), + im.subtract(value.im)); + } + + /** + * Calculates the subtraction of the given complex value from this complex number using the specified {@link MathContext}. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the {@link BigComplex} value to subtract + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + */ + public BigComplex subtract(BigComplex value, MathContext mathContext) { + return valueOf( + re.subtract(value.re, mathContext), + im.subtract(value.im, mathContext)); + } + + /** + * Calculates the subtraction of the given real {@link BigDecimal} value from this complex number using the specified {@link MathContext}. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the real {@link BigDecimal} value to add + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + */ + public BigComplex subtract(BigDecimal value, MathContext mathContext) { + return valueOf( + re.subtract(value, mathContext), + im); + } + + /** + * Calculates the subtraction of the given real {@link BigDecimal} value from this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the real {@link BigDecimal} value to subtract + * @return the calculated {@link BigComplex} result + */ + public BigComplex subtract(BigDecimal value) { + return valueOf( + re.subtract(value), + im); + } + + /** + * Calculates the subtraction of the given real {@code double} value from this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the real {@code double} value to subtract + * @return the calculated {@link BigComplex} result + */ + public BigComplex subtract(double value) { + return subtract(BigDecimal.valueOf(value)); + } + + /** + * Calculates the multiplication of the given complex value to this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the {@link BigComplex} value to multiply + * @return the calculated {@link BigComplex} result + */ + public BigComplex multiply(BigComplex value) { + return valueOf( + re.multiply(value.re).subtract(im.multiply(value.im)), + re.multiply(value.im).add(im.multiply(value.re))); + } + + /** + * Calculates the multiplication of the given complex value with this complex number using the specified {@link MathContext}. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the {@link BigComplex} value to multiply + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + */ + public BigComplex multiply(BigComplex value, MathContext mathContext) { + return valueOf( + re.multiply(value.re, mathContext).subtract(im.multiply(value.im, mathContext), mathContext), + re.multiply(value.im, mathContext).add(im.multiply(value.re, mathContext), mathContext)); + } + + /** + * Calculates the multiplication of the given real {@link BigDecimal} value with this complex number using the specified {@link MathContext}. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the real {@link BigDecimal} value to multiply + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + */ + public BigComplex multiply(BigDecimal value, MathContext mathContext) { + return valueOf( + re.multiply(value, mathContext), + im.multiply(value, mathContext)); + } + + /** + * Calculates the multiplication of the given real {@link BigDecimal} value with this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the real {@link BigDecimal} value to multiply + * @return the calculated {@link BigComplex} result + */ + public BigComplex multiply(BigDecimal value) { + return valueOf( + re.multiply(value), + im.multiply(value)); + } + + /** + * Calculates the multiplication of the given real {@code double} value with this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the real {@code double} value to multiply + * @return the calculated {@link BigComplex} result + */ + public BigComplex multiply(double value) { + return multiply(BigDecimal.valueOf(value)); + } + + /** + * Calculates this complex number divided by the given complex value using the specified {@link MathContext}. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the {@link BigComplex} value to divide by + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + */ + public BigComplex divide(BigComplex value, MathContext mathContext) { + return multiply(value.reciprocal(mathContext), mathContext); + } + + /** + * Calculates this complex number divided by the given real {@link BigDecimal} value using the specified {@link MathContext}. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the {@link BigDecimal} value to divide by + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + */ + public BigComplex divide(BigDecimal value, MathContext mathContext) { + return valueOf( + re.divide(value, mathContext), + im.divide(value, mathContext)); + } + + /** + * Calculates this complex number divided by the given real {@code double} value using the specified {@link MathContext}. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param value the {@code double} value to divide by + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + */ + public BigComplex divide(double value, MathContext mathContext) { + return divide(BigDecimal.valueOf(value), mathContext); + } + + /** + * Calculates the reciprocal of this complex number using the specified {@link MathContext}. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + */ + public BigComplex reciprocal(MathContext mathContext) { + BigDecimal scale = absSquare(mathContext); + return valueOf( + re.divide(scale, mathContext), + im.negate().divide(scale, mathContext)); + } + + /** + * Calculates the conjugate {@code a - bi} of this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @return the calculated {@link BigComplex} result + */ + public BigComplex conjugate() { + return valueOf(re, im.negate()); + } + + /** + * Calculates the negation {@code -a - bi} of this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @return the calculated {@link BigComplex} result + */ + public BigComplex negate() { + return valueOf(re.negate(), im.negate()); + } + + /** + * Calculates the absolute value (also known as magnitude, length or radius) of this complex number. + * + * <p>This method is slower than {@link #absSquare(MathContext)} since it needs to calculate the {@link BigDecimalMath#sqrt(BigDecimal, MathContext)}.</p> + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + * @see #absSquare(MathContext) + */ + public BigDecimal abs(MathContext mathContext) { + return BigDecimalMath.sqrt(absSquare(mathContext), mathContext); + } + + /** + * Calculates the angle in radians (also known as argument) of this complex number. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + */ + public BigDecimal angle(MathContext mathContext) { + return BigDecimalMath.atan2(im, re, mathContext); + } + + /** + * Calculates the square of the absolute value of this complex number. + * + * <p>This method is faster than {@link #abs(MathContext)} since it does not need to calculate the {@link BigDecimalMath#sqrt(BigDecimal, MathContext)}.</p> + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + * @see #abs(MathContext) + */ + public BigDecimal absSquare(MathContext mathContext) { + return re.multiply(re, mathContext).add(im.multiply(im, mathContext), mathContext); + } + + /** + * Returns whether this complex number only has a real part (the imaginary part is 0). + * + * @return {@code true} if this complex number only has a real part, {@code false} if the imaginary part is not 0 + */ + public boolean isReal() { + return im.signum() == 0; + } + + /** + * Returns the real part of this complex number as {@link BigComplex} number. + * + * @return the real part as as {@link BigComplex} number + */ + public BigComplex re() { + return valueOf(re, BigDecimal.ZERO); + } + + /** + * Returns the imaginary part of this complex number as {@link BigComplex} number. + * + * @return the imaginary part as as {@link BigComplex} number + */ + public BigComplex im() { + return valueOf(BigDecimal.ZERO, im); + } + + /** + * Returns this complex nuber rounded to the specified precision. + * + * <p>This methods <strong>does not</strong> modify this instance.</p> + * + * @param mathContext the {@link MathContext} used to calculate the result + * @return the rounded {@link BigComplex} result + */ + public BigComplex round(MathContext mathContext) { + return valueOf(re.round(mathContext), im.round(mathContext)); + } + + @Override + public int hashCode() { + return Objects.hash(re, im); + } + + /** + * {@inheritDoc} + * + * <p>Contrary to {@link BigDecimal#equals(Object)} this method implements <strong>mathematical</strong> equality + * (by calling {@link BigDecimal#compareTo(BigDecimal)} on the real and imaginary parts) + * instead of strict equality.</p> + * + * @see #strictEquals(Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BigComplex other = (BigComplex) obj; + + return re.compareTo(other.re) == 0 && im.compareTo(other.im) == 0; + } + + /** + * Returns whether the real and imaginary parts of this complex number are strictly equal. + * + * <p>This method uses the strict equality as defined by {@link BigDecimal#equals(Object)} on the real and imaginary parts.</p> + * <p>Please note that {@link #equals(Object) BigComplex.equals(Object)} implements <strong>mathematical</strong> equality instead + * (by calling {@link BigDecimal#compareTo(BigDecimal) on the real and imaginary parts}).</p> + * + * @param obj the object to compare for strict equality + * @return {@code true} if the specified object is strictly equal to this complex number + * @see #equals(Object) + */ + public boolean strictEquals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BigComplex other = (BigComplex) obj; + + return re.equals(other.re) && im.equals(other.im); + } + + @Override + public String toString() { + if (im.signum() >= 0) { + return "(" + re + " + " + im + " i)"; + } else { + return "(" + re + " - " + im.negate() + " i)"; + } + } + + /** + * Returns a complex number with the specified real {@link BigDecimal} part. + * + * @param real the real {@link BigDecimal} part + * @return the complex number + */ + public static BigComplex valueOf(BigDecimal real) { + return valueOf(real, BigDecimal.ZERO); + } + + /** + * Returns a complex number with the specified real {@code double} part. + * + * @param real the real {@code double} part + * @return the complex number + */ + public static BigComplex valueOf(double real) { + return valueOf(BigDecimal.valueOf(real), BigDecimal.ZERO); + } + + /** + * Returns a complex number with the specified real and imaginary {@code double} parts. + * + * @param real the real {@code double} part + * @param imaginary the imaginary {@code double} part + * @return the complex number + */ + public static BigComplex valueOf(double real, double imaginary) { + return valueOf(BigDecimal.valueOf(real), BigDecimal.valueOf(imaginary)); + } + + /** + * Returns a complex number with the specified real and imaginary {@link BigDecimal} parts. + * + * @param real the real {@link BigDecimal} part + * @param imaginary the imaginary {@link BigDecimal} part + * @return the complex number + */ + public static BigComplex valueOf(BigDecimal real, BigDecimal imaginary) { + if (real.signum() == 0) { + if (imaginary.signum() == 0) { + return ZERO; + } + if (imaginary.compareTo(BigDecimal.ONE) == 0) { + return I; + } + } + if (imaginary.signum() == 0 && real.compareTo(BigDecimal.ONE) == 0) { + return ONE; + } + + return new BigComplex(real, imaginary); + } + + /** + * Returns a complex number with the specified polar {@link BigDecimal} radius and angle using the specified {@link MathContext}. + * + * @param radius the {@link BigDecimal} radius of the polar representation + * @param angle the {@link BigDecimal} angle in radians of the polar representation + * @param mathContext the {@link MathContext} used to calculate the result + * @return the complex number + */ + public static BigComplex valueOfPolar(BigDecimal radius, BigDecimal angle, MathContext mathContext) { + if (radius.signum() == 0) { + return ZERO; + } + + return valueOf( + radius.multiply(BigDecimalMath.cos(angle, mathContext), mathContext), + radius.multiply(BigDecimalMath.sin(angle, mathContext), mathContext)); + } + + public static BigComplex valueOfPolar(double radius, double angle, MathContext mathContext) { + return valueOfPolar(BigDecimal.valueOf(radius), BigDecimal.valueOf(angle), mathContext); + } +} diff --git a/src/main/java/ch/obermuhlner/math/big/BigComplexMath.java b/src/main/java/ch/obermuhlner/math/big/BigComplexMath.java new file mode 100644 index 0000000000..a73d9bccdd --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/BigComplexMath.java @@ -0,0 +1,413 @@ +package ch.obermuhlner.math.big; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.List; + +import static ch.obermuhlner.math.big.BigComplex.I; + +/** + * Provides advanced functions operating on {@link BigComplex}s. + */ +public class BigComplexMath { + + private static final BigDecimal TWO = BigDecimal.valueOf(2); + + /** + * Calculates the reciprocal of the given complex number using the specified {@link MathContext}. + * + * @param x the complex number to calculate the reciprocal + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + * @see BigComplex#reciprocal(MathContext) + */ + public static BigComplex reciprocal(BigComplex x, MathContext mathContext) { + return x.reciprocal(mathContext); + } + + /** + * Calculates the conjugate of the given complex number using the specified {@link MathContext}. + * + * @param x the complex number to calculate the conjugate + * @return the calculated {@link BigComplex} result + * @see BigComplex#conjugate() + */ + public static BigComplex conjugate(BigComplex x) { + return x.conjugate(); + } + + /** + * Calculates the absolute value (also known as magnitude, length or radius) of the given complex number using the specified {@link MathContext}. + * + * @param x the complex number to calculate the absolute value + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + * @see BigComplex#abs(MathContext) + */ + public static BigDecimal abs(BigComplex x, MathContext mathContext) { + return x.abs(mathContext); + } + + /** + * Calculates the square of the absolute value (also known as magnitude, length or radius) of the given complex number using the specified {@link MathContext}. + * + * @param x the complex number to calculate the square of the absolute value + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} result + * @see BigComplex#absSquare(MathContext) + */ + public static BigDecimal absSquare(BigComplex x, MathContext mathContext) { + return x.absSquare(mathContext); + } + + /** + * Calculates the angle in radians of the given complex number using the specified {@link MathContext}. + * + * @param x the complex number to calculate the angle + * @param mathContext the {@link MathContext} used to calculate the result + * @return the calculated {@link BigComplex} angle in radians + * @see BigComplex#angle(MathContext) + */ + public static BigDecimal angle(BigComplex x, MathContext mathContext) { + return x.angle(mathContext); + } + + /** + * Calculates the factorial of the specified {@link BigComplex}. + * + * <p>This implementation uses + * <a href="https://en.wikipedia.org/wiki/Spouge%27s_approximation">Spouge's approximation</a> + * to calculate the factorial for non-integer values.</p> + * + * <p>This involves calculating a series of constants that depend on the desired precision. + * Since this constant calculation is quite expensive (especially for higher precisions), + * the constants for a specific precision will be cached + * and subsequent calls to this method with the same precision will be much faster.</p> + * + * <p>It is therefore recommended to do one call to this method with the standard precision of your application during the startup phase + * and to avoid calling it with many different precisions.</p> + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Factorial#Extension_of_factorial_to_non-integer_values_of_argument">Wikipedia: Factorial - Extension of factorial to non-integer values of argument</a></p> + * + * @param x the {@link BigComplex} + * @param mathContext the {@link MathContext} used for the result + * @return the factorial {@link BigComplex} + * @throws ArithmeticException if x is a negative integer value (-1, -2, -3, ...) + * @see BigDecimalMath#factorial(BigDecimal, MathContext) + * @see #gamma(BigComplex, MathContext) + */ + public static BigComplex factorial(BigComplex x, MathContext mathContext) { + if (x.isReal() && BigDecimalMath.isIntValue(x.re)) { + return BigComplex.valueOf(BigDecimalMath.factorial(x.re.intValueExact()).round(mathContext)); + } + + // https://en.wikipedia.org/wiki/Spouge%27s_approximation + MathContext mc = new MathContext(mathContext.getPrecision() * 2, mathContext.getRoundingMode()); + + int a = mathContext.getPrecision() * 13 / 10; + List<BigDecimal> constants = BigDecimalMath.getSpougeFactorialConstants(a); + + BigDecimal bigA = BigDecimal.valueOf(a); + + boolean negative = false; + BigComplex factor = BigComplex.valueOf(constants.get(0)); + for (int k = 1; k < a; k++) { + BigDecimal bigK = BigDecimal.valueOf(k); + factor = factor.add(BigComplex.valueOf(constants.get(k)).divide(x.add(bigK), mc), mc); + negative = !negative; + } + + BigComplex result = pow(x.add(bigA, mc), x.add(BigDecimal.valueOf(0.5), mc), mc); + result = result.multiply(exp(x.negate().subtract(bigA, mc), mc), mc); + result = result.multiply(factor, mc); + + return result.round(mathContext); + } + + /** + * Calculates the gamma function of the specified {@link BigComplex}. + * + * <p>This implementation uses {@link #factorial(BigComplex, MathContext)} internally, + * therefore the performance implications described there apply also for this method. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Gamma_function">Wikipedia: Gamma function</a></p> + * + * @param x the {@link BigComplex} + * @param mathContext the {@link MathContext} used for the result + * @return the gamma {@link BigComplex} + * @throws ArithmeticException if x-1 is a negative integer value (-1, -2, -3, ...) + * @see BigDecimalMath#gamma(BigDecimal, MathContext) + * @see #factorial(BigComplex, MathContext) + */ + public static BigComplex gamma(BigComplex x, MathContext mathContext) { + return factorial(x.subtract(BigComplex.ONE), mathContext); + } + + + /** + * Calculates the natural exponent of {@link BigComplex} x (e<sup>x</sup>) in the complex domain. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Exponential_function#Complex_plane">Wikipedia: Exponent (Complex plane)</a></p> + * + * @param x the {@link BigComplex} to calculate the exponent for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated exponent {@link BigComplex} with the precision specified in the <code>mathContext</code> + */ + public static BigComplex exp(BigComplex x, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + BigDecimal expRe = BigDecimalMath.exp(x.re, mc); + return BigComplex.valueOf( + expRe.multiply(BigDecimalMath.cos(x.im, mc), mc).round(mathContext), + expRe.multiply(BigDecimalMath.sin(x.im, mc), mc)).round(mathContext); + } + + /** + * Calculates the sine (sinus) of {@link BigComplex} x in the complex domain. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Sine#Sine_with_a_complex_argument">Wikipedia: Sine (Sine with a complex argument)</a></p> + * + * @param x the {@link BigComplex} to calculate the sine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated sine {@link BigComplex} with the precision specified in the <code>mathContext</code> + */ + public static BigComplex sin(BigComplex x, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + return BigComplex.valueOf( + BigDecimalMath.sin(x.re, mc).multiply(BigDecimalMath.cosh(x.im, mc), mc).round(mathContext), + BigDecimalMath.cos(x.re, mc).multiply(BigDecimalMath.sinh(x.im, mc), mc).round(mathContext)); + } + + /** + * Calculates the cosine (cosinus) of {@link BigComplex} x in the complex domain. + * + * @param x the {@link BigComplex} to calculate the cosine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated cosine {@link BigComplex} with the precision specified in the <code>mathContext</code> + */ + public static BigComplex cos(BigComplex x, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + return BigComplex.valueOf( + BigDecimalMath.cos(x.re, mc).multiply(BigDecimalMath.cosh(x.im, mc), mc).round(mathContext), + BigDecimalMath.sin(x.re, mc).multiply(BigDecimalMath.sinh(x.im, mc), mc).negate().round(mathContext)); + } + + // + // http://scipp.ucsc.edu/~haber/archives/physics116A10/arc_10.pdf + + /** + * Calculates the tangens of {@link BigComplex} x in the complex domain. + * + * @param x the {@link BigComplex} to calculate the tangens for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated tangens {@link BigComplex} with the precision specified in the <code>mathContext</code> + */ + public static BigComplex tan(BigComplex x, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + return sin(x, mc).divide(cos(x, mc), mc).round(mathContext); + } + + /** + * Calculates the arc tangens (inverted tangens) of {@link BigComplex} x in the complex domain. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Extension_to_complex_plane">Wikipedia: Inverse trigonometric functions (Extension to complex plane)</a></p> + * + * @param x the {@link BigComplex} to calculate the arc tangens for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc tangens {@link BigComplex} with the precision specified in the <code>mathContext</code> + */ + public static BigComplex atan(BigComplex x, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + return log(I.subtract(x, mc).divide(I.add(x, mc), mc), mc).divide(I, mc).divide(TWO, mc).round(mathContext); + } + + /** + * Calculates the arc cotangens (inverted cotangens) of {@link BigComplex} x in the complex domain. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Extension_to_complex_plane">Wikipedia: Inverse trigonometric functions (Extension to complex plane)</a></p> + * + * @param x the {@link BigComplex} to calculate the arc cotangens for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc cotangens {@link BigComplex} with the precision specified in the <code>mathContext</code> + */ + public static BigComplex acot(BigComplex x, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + return log(x.add(I, mc).divide(x.subtract(I, mc), mc), mc).divide(I, mc).divide(TWO, mc).round(mathContext); + } + + /** + * Calculates the arc sine (inverted sine) of {@link BigComplex} x in the complex domain. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Extension_to_complex_plane">Wikipedia: Inverse trigonometric functions (Extension to complex plane)</a></p> + * + * @param x the {@link BigComplex} to calculate the arc sine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc sine {@link BigComplex} with the precision specified in the <code>mathContext</code> + */ + public static BigComplex asin(BigComplex x, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + return I.negate().multiply(log(I.multiply(x, mc).add(sqrt(BigComplex.ONE.subtract(x.multiply(x, mc), mc), mc), mc), mc), mc).round(mathContext); + } + + /** + * Calculates the arc cosine (inverted cosine) of {@link BigComplex} x in the complex domain. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Extension_to_complex_plane">Wikipedia: Inverse trigonometric functions (Extension to complex plane)</a></p> + * + * @param x the {@link BigComplex} to calculate the arc cosine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc cosine {@link BigComplex} with the precision specified in the <code>mathContext</code> + */ + public static BigComplex acos(BigComplex x, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + return I.negate().multiply(log(x.add(sqrt(x.multiply(x, mc).subtract(BigComplex.ONE, mc), mc), mc), mc), mc).round(mathContext); + } + + /** + * Calculates the square root of {@link BigComplex} x in the complex domain (sqrt x). + * + * <p>See <a href="https://en.wikipedia.org/wiki/Square_root#Square_root_of_an_imaginary_number">Wikipedia: Square root (Square root of an imaginary number)</a></p> + * + * @param x the {@link BigComplex} to calculate the square root for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated square root {@link BigComplex} with the precision specified in the <code>mathContext</code> + */ + public static BigComplex sqrt(BigComplex x, MathContext mathContext) { + // https://math.stackexchange.com/questions/44406/how-do-i-get-the-square-root-of-a-complex-number + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + BigDecimal magnitude = x.abs(mc); + + BigComplex a = x.add(magnitude, mc); + return a.divide(a.abs(mc), mc).multiply(BigDecimalMath.sqrt(magnitude, mc), mc).round(mathContext); + } + + /** + * Calculates the natural logarithm of {@link BigComplex} x in the complex domain. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Complex_logarithm">Wikipedia: Complex logarithm</a></p> + * + * @param x the {@link BigComplex} to calculate the natural logarithm for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated natural logarithm {@link BigComplex} with the precision specified in the <code>mathContext</code> + */ + public static BigComplex log(BigComplex x, MathContext mathContext) { + // https://en.wikipedia.org/wiki/Complex_logarithm + MathContext mc1 = new MathContext(mathContext.getPrecision() + 20, mathContext.getRoundingMode()); + MathContext mc2 = new MathContext(mathContext.getPrecision() + 5, mathContext.getRoundingMode()); + + return BigComplex.valueOf( + BigDecimalMath.log(x.abs(mc1), mc1).round(mathContext), + x.angle(mc2)).round(mathContext); + } + + /** + * Calculates {@link BigComplex} x to the power of <code>long</code> y (x<sup>y</sup>). + * + * <p>The implementation tries to minimize the number of multiplications of {@link BigComplex x} (using squares whenever possible).</p> + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Exponentiation#Efficient_computation_with_integer_exponents">Wikipedia: Exponentiation - efficient computation</a></p> + * + * @param x the {@link BigComplex} value to take to the power + * @param y the <code>long</code> value to serve as exponent + * @param mathContext the {@link MathContext} used for the result + * @return the calculated x to the power of y with the precision specified in the <code>mathContext</code> + */ + public static BigComplex pow(BigComplex x, long y, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 10, mathContext.getRoundingMode()); + + if (y < 0) { + return BigComplex.ONE.divide(pow(x, -y, mc), mc).round(mathContext); + } + + BigComplex result = BigComplex.ONE; + while (y > 0) { + if ((y & 1) == 1) { + // odd exponent -> multiply result with x + result = result.multiply(x, mc); + y -= 1; + } + + if (y > 0) { + // even exponent -> square x + x = x.multiply(x, mc); + } + + y >>= 1; + } + + return result.round(mathContext); + } + + /** + * Calculates {@link BigComplex} x to the power of {@link BigDecimal} y (x<sup>y</sup>). + * + * @param x the {@link BigComplex} value to take to the power + * @param y the {@link BigDecimal} value to serve as exponent + * @param mathContext the {@link MathContext} used for the result + * @return the calculated x to the power of y with the precision specified in the <code>mathContext</code> + */ + public static BigComplex pow(BigComplex x, BigDecimal y, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + BigDecimal angleTimesN = x.angle(mc).multiply(y, mc); + return BigComplex.valueOf( + BigDecimalMath.cos(angleTimesN, mc), + BigDecimalMath.sin(angleTimesN, mc)).multiply(BigDecimalMath.pow(x.abs(mc), y, mc), mc).round(mathContext); + } + + /** + * Calculates {@link BigComplex} x to the power of {@link BigComplex} y (x<sup>y</sup>). + * + * @param x the {@link BigComplex} value to take to the power + * @param y the {@link BigComplex} value to serve as exponent + * @param mathContext the {@link MathContext} used for the result + * @return the calculated x to the power of y with the precision specified in the <code>mathContext</code> + */ + public static BigComplex pow(BigComplex x, BigComplex y, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + return exp(y.multiply(log(x, mc), mc), mc).round(mathContext); + } + + /** + * Calculates the {@link BigDecimal} n'th root of {@link BigComplex} x (<sup>n</sup>sqrt x). + * + * <p>See <a href="http://en.wikipedia.org/wiki/Square_root">Wikipedia: Square root</a></p> + * @param x the {@link BigComplex} value to calculate the n'th root + * @param n the {@link BigDecimal} defining the root + * @param mathContext the {@link MathContext} used for the result + * + * @return the calculated n'th root of x with the precision specified in the <code>mathContext</code> + */ + public static BigComplex root(BigComplex x, BigDecimal n, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + return pow(x, BigDecimal.ONE.divide(n, mc), mc).round(mathContext); + } + + /** + * Calculates the {@link BigComplex} n'th root of {@link BigComplex} x (<sup>n</sup>sqrt x). + * + * <p>See <a href="http://en.wikipedia.org/wiki/Square_root">Wikipedia: Square root</a></p> + * @param x the {@link BigComplex} value to calculate the n'th root + * @param n the {@link BigComplex} defining the root + * @param mathContext the {@link MathContext} used for the result + * + * @return the calculated n'th root of x with the precision specified in the <code>mathContext</code> + */ + public static BigComplex root(BigComplex x, BigComplex n, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + return pow(x, BigComplex.ONE.divide(n, mc), mc).round(mathContext); + } + + // TODO add root() for the k'th root - https://math.stackexchange.com/questions/322481/principal-nth-root-of-a-complex-number +} diff --git a/src/main/java/ch/obermuhlner/math/big/BigDecimalMath.java b/src/main/java/ch/obermuhlner/math/big/BigDecimalMath.java new file mode 100644 index 0000000000..552331f3b4 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/BigDecimalMath.java @@ -0,0 +1,1671 @@ +package ch.obermuhlner.math.big; + +import static java.math.BigDecimal.ONE; +import static java.math.BigDecimal.TEN; +import static java.math.BigDecimal.ZERO; +import static java.math.BigDecimal.valueOf; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.*; + +import ch.obermuhlner.math.big.internal.AsinCalculator; +import ch.obermuhlner.math.big.internal.CosCalculator; +import ch.obermuhlner.math.big.internal.CoshCalculator; +import ch.obermuhlner.math.big.internal.ExpCalculator; +import ch.obermuhlner.math.big.internal.SinCalculator; +import ch.obermuhlner.math.big.internal.SinhCalculator; + +/** + * Provides advanced functions operating on {@link BigDecimal}s. + */ +public class BigDecimalMath { + + private static final BigDecimal TWO = valueOf(2); + private static final BigDecimal THREE = valueOf(3); + private static final BigDecimal MINUS_ONE = valueOf(-1); + private static final BigDecimal ONE_HALF = valueOf(0.5); + + private static final BigDecimal DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE); + + private static volatile BigDecimal log2Cache; + private static final Object log2CacheLock = new Object(); + + private static volatile BigDecimal log3Cache; + private static final Object log3CacheLock = new Object(); + + private static volatile BigDecimal log10Cache; + private static final Object log10CacheLock = new Object(); + + private static volatile BigDecimal piCache; + private static final Object piCacheLock = new Object(); + + private static volatile BigDecimal eCache; + private static final Object eCacheLock = new Object(); + + private static final BigDecimal ROUGHLY_TWO_PI = new BigDecimal("3.141592653589793").multiply(TWO); + + private static final int EXPECTED_INITIAL_PRECISION = 15; + + private static BigDecimal[] factorialCache = new BigDecimal[100]; + + static { + BigDecimal result = ONE; + factorialCache[0] = result; + for (int i = 1; i < factorialCache.length; i++) { + result = result.multiply(valueOf(i)); + factorialCache[i] = result; + } + } + + private static final Map<Integer, List<BigDecimal>> spougeFactorialConstantsCache = new HashMap<>(); + private static final Object spougeFactorialConstantsCacheLock = new Object(); + + private BigDecimalMath() { + // prevent instances + } + + /** + * Creates a {@link BigDecimal} from the specified <code>String</code> representation. + * + * <p>This method is equivalent to the String constructor {@link BigDecimal#BigDecimal(String)} + * but has been optimized for large strings (several thousand digits).</p> + * + * @param string the String representation + * @return the created {@link BigDecimal} + * @throws NumberFormatException if <code>string</code> is not a valid representation of a {@link BigDecimal} + * @see BigDecimal#BigDecimal(String) + * @see #toBigDecimal(String, MathContext) + */ + public static BigDecimal toBigDecimal(String string) { + return toBigDecimal(string, MathContext.UNLIMITED); + } + + /** + * Creates a {@link BigDecimal} from the specified <code>String</code> representation. + * + * <p>This method is equivalent to the String constructor {@link BigDecimal#BigDecimal(String, MathContext)} + * but has been optimized for large strings (several thousand digits).</p> + * + * @param string the string representation + * @param mathContext the {@link MathContext} used for the result + * @return the created {@link BigDecimal} + * @throws NumberFormatException if <code>string</code> is not a valid representation of a {@link BigDecimal} + * @throws ArithmeticException if the result is inexact but the rounding mode is {@code UNNECESSARY} + * @see BigDecimal#BigDecimal(String, MathContext) + * @see #toBigDecimal(String) + */ + public static BigDecimal toBigDecimal(String string, MathContext mathContext) { + int len = string.length(); + if (len < 600) { + return new BigDecimal(string, mathContext); + } + + int splitLength = len / (len >= 10000 ? 8 : 5); + return toBigDecimal(string, mathContext, splitLength); + } + + static BigDecimal toBigDecimal(String string, MathContext mathContext, int splitLength) { + int len = string.length(); + + if (len < splitLength) { + return new BigDecimal(string, mathContext); + } + + char[] chars = string.toCharArray(); + + boolean numberHasSign = false; + boolean negative = false; + int numberIndex = 0; + int dotIndex = -1; + int expIndex = -1; + boolean expHasSign = false; + int scale = 0; + + for (int i = 0; i < len; i++) { + char c = chars[i]; + switch (c) { + case '+': + if (expIndex >= 0) { + if (expHasSign) { + throw new NumberFormatException("Multiple signs in exponent"); + } + expHasSign = true; + } else { + if (numberHasSign) { + throw new NumberFormatException("Multiple signs in number"); + } + numberHasSign = true; + numberIndex = i + 1; + } + break; + case '-': + if (expIndex >= 0) { + if (expHasSign) { + throw new NumberFormatException("Multiple signs in exponent"); + } + expHasSign = true; + } else { + if (numberHasSign) { + throw new NumberFormatException("Multiple signs in number"); + } + numberHasSign = true; + negative = true; + numberIndex = i + 1; + } + break; + case 'e': + case 'E': + if (expIndex >= 0) { + throw new NumberFormatException("Multiple exponent markers"); + } + expIndex = i; + break; + case '.': + if (dotIndex >= 0) { + throw new NumberFormatException("Multiple decimal points"); + } + dotIndex = i; + break; + default: + if (dotIndex >= 0 && expIndex == -1) { + scale++; + } + } + } + + int numberEndIndex; + int exp = 0; + if (expIndex >= 0) { + numberEndIndex = expIndex; + String expString = new String(chars, expIndex + 1, len - expIndex - 1); + exp = Integer.parseInt(expString); + scale = adjustScale(scale, exp); + } else { + numberEndIndex = len; + } + + BigDecimal result; + + if (dotIndex >= 0) { + int leftLength = dotIndex - numberIndex; + BigDecimal bigDecimalLeft = toBigDecimalRecursive(chars, numberIndex, leftLength, exp, splitLength); + int rightLength = numberEndIndex - dotIndex - 1; + BigDecimal bigDecimalRight = toBigDecimalRecursive(chars, dotIndex + 1, rightLength, exp-rightLength, splitLength); + result = bigDecimalLeft.add(bigDecimalRight); + } else { + result = toBigDecimalRecursive(chars, numberIndex, numberEndIndex - numberIndex, exp, splitLength); + } + + if (scale != 0) { + result = result.setScale(scale); + } + + if (negative) { + result = result.negate(); + } + + if (mathContext.getPrecision() != 0) { + result = result.round(mathContext); + } + + return result; + } + + private static int adjustScale(int scale, long exp) { + long adjustedScale = scale - exp; + if (adjustedScale > Integer.MAX_VALUE || adjustedScale < Integer.MIN_VALUE) + throw new NumberFormatException("Scale out of range: " + adjustedScale + " while adjusting scale " + scale + " to exponent " + exp); + return (int) adjustedScale; + } + + private static BigDecimal toBigDecimalRecursive(char[] chars, int offset, int length, int scale, int splitLength) { + if (length > splitLength) { + int mid = length / 2; + BigDecimal bigDecimalLeft = toBigDecimalRecursive(chars, offset, mid, scale + length - mid, splitLength); + BigDecimal bigDecimalRight = toBigDecimalRecursive(chars, offset + mid, length - mid, scale, splitLength); + return bigDecimalLeft.add(bigDecimalRight); + } + if (length == 0) { + return BigDecimal.ZERO; + } + return new BigDecimal(chars, offset, length).movePointRight(scale); + } + + /** + * Returns whether the specified {@link BigDecimal} value can be represented as <code>int</code>. + * + * <p>If this returns <code>true</code> you can call {@link BigDecimal#intValueExact()} without fear of an {@link ArithmeticException}.</p> + * + * @param value the {@link BigDecimal} to check + * @return <code>true</code> if the value can be represented as <code>int</code> value + */ + public static boolean isIntValue(BigDecimal value) { + // TODO impl isIntValue() without exceptions + try { + value.intValueExact(); + return true; + } catch (ArithmeticException ex) { + // ignored + } + return false; + } + + /** + * Returns whether the specified {@link BigDecimal} value can be represented as <code>long</code>. + * + * <p>If this returns <code>true</code> you can call {@link BigDecimal#longValueExact()} without fear of an {@link ArithmeticException}.</p> + * + * @param value the {@link BigDecimal} to check + * @return <code>true</code> if the value can be represented as <code>long</code> value + */ + public static boolean isLongValue(BigDecimal value) { + // TODO impl isLongValue() without exceptions + try { + value.longValueExact(); + return true; + } catch (ArithmeticException ex) { + // ignored + } + return false; + } + + /** + * Returns whether the specified {@link BigDecimal} value can be represented as <code>double</code>. + * + * <p>If this returns <code>true</code> you can call {@link BigDecimal#doubleValue()} + * without fear of getting {@link Double#POSITIVE_INFINITY} or {@link Double#NEGATIVE_INFINITY} as result.</p> + * + * <p>Example: <code>BigDecimalMath.isDoubleValue(new BigDecimal("1E309"))</code> returns <code>false</code>, + * because <code>new BigDecimal("1E309").doubleValue()</code> returns <code>Infinity</code>.</p> + * + * <p>Note: This method does <strong>not</strong> check for possible loss of precision.</p> + * + * <p>For example <code>BigDecimalMath.isDoubleValue(new BigDecimal("1.23400000000000000000000000000000001"))</code> will return <code>true</code>, + * because <code>new BigDecimal("1.23400000000000000000000000000000001").doubleValue()</code> returns a valid double value, + * although it loses precision and returns <code>1.234</code>.</p> + * + * <p><code>BigDecimalMath.isDoubleValue(new BigDecimal("1E-325"))</code> will return <code>true</code> + * although this value is smaller than {@link Double#MIN_VALUE} (and therefore outside the range of values that can be represented as <code>double</code>) + * because <code>new BigDecimal("1E-325").doubleValue()</code> returns <code>0</code> which is a legal value with loss of precision.</p> + * + * @param value the {@link BigDecimal} to check + * @return <code>true</code> if the value can be represented as <code>double</code> value + */ + public static boolean isDoubleValue(BigDecimal value) { + if (value.compareTo(DOUBLE_MAX_VALUE) > 0) { + return false; + } + if (value.compareTo(DOUBLE_MAX_VALUE.negate()) < 0) { + return false; + } + + return true; + } + + /** + * Returns the mantissa of the specified {@link BigDecimal} written as <em>mantissa * 10<sup>exponent</sup></em>. + * + * <p>The mantissa is defined as having exactly 1 digit before the decimal point.</p> + * + * @param value the {@link BigDecimal} + * @return the mantissa + * @see #exponent(BigDecimal) + */ + public static BigDecimal mantissa(BigDecimal value) { + int exponent = exponent(value); + if (exponent == 0) { + return value; + } + + return value.movePointLeft(exponent); + } + + /** + * Returns the exponent of the specified {@link BigDecimal} written as <em>mantissa * 10<sup>exponent</sup></em>. + * + * <p>The mantissa is defined as having exactly 1 digit before the decimal point.</p> + * + * @param value the {@link BigDecimal} + * @return the exponent + * @see #mantissa(BigDecimal) + */ + public static int exponent(BigDecimal value) { + return value.precision() - value.scale() - 1; + } + + /** + * Returns the number of significant digits of the specified {@link BigDecimal}. + * + * <p>The result contains the number of all digits before the decimal point and + * all digits after the decimal point excluding trailing zeroes.</p> + * + * <p>Examples:</p> + * <ul> + * <li><code>significantDigits(new BigDecimal("12300.00"))</code> returns 5</li> + * <li><code>significantDigits(new BigDecimal("1.23000"))</code> returns 3</li> + * <li><code>significantDigits(new BigDecimal("0.00012300"))</code> returns 3</li> + * <li><code>significantDigits(new BigDecimal("12300.4500"))</code> returns 7</li> + * </ul> + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Significant_figures">Wikipedia: Significant figures</a></p> + * + * @param value the {@link BigDecimal} + * @return the number of significant digits + * @see BigDecimal#stripTrailingZeros() + * @see BigDecimal#precision() + */ + public static int significantDigits(BigDecimal value) { + BigDecimal stripped = value.stripTrailingZeros(); + if (stripped.scale() >= 0) { + return stripped.precision(); + } else { + return stripped.precision() - stripped.scale(); + } + } + + /** + * Returns the integral part of the specified {@link BigDecimal} (left of the decimal point). + * + * @param value the {@link BigDecimal} + * @return the integral part + * @see #fractionalPart(BigDecimal) + */ + public static BigDecimal integralPart(BigDecimal value) { + return value.setScale(0, BigDecimal.ROUND_DOWN); + } + + /** + * Returns the fractional part of the specified {@link BigDecimal} (right of the decimal point). + * + * @param value the {@link BigDecimal} + * @return the fractional part + * @see #integralPart(BigDecimal) + */ + public static BigDecimal fractionalPart(BigDecimal value) { + return value.subtract(integralPart(value)); + } + + /** + * Rounds the specified {@link BigDecimal} to the precision of the specified {@link MathContext}. + * + * <p>This method calls {@link BigDecimal#round(MathContext)}.</p> + * + * @param value the {@link BigDecimal} to round + * @param mathContext the {@link MathContext} used for the result + * @return the rounded {@link BigDecimal} value + * @see BigDecimal#round(MathContext) + * @see BigDecimalMath#roundWithTrailingZeroes(BigDecimal, MathContext) + */ + public static BigDecimal round(BigDecimal value, MathContext mathContext) { + return value.round(mathContext); + } + + /** + * Rounds the specified {@link BigDecimal} to the precision of the specified {@link MathContext} including trailing zeroes. + * + * <p>This method is similar to {@link BigDecimal#round(MathContext)} but does <strong>not</strong> remove the trailing zeroes.</p> + * + * <p>Example:</p> +<pre> +MathContext mc = new MathContext(5); +System.out.println(BigDecimalMath.roundWithTrailingZeroes(new BigDecimal("1.234567"), mc)); // 1.2346 +System.out.println(BigDecimalMath.roundWithTrailingZeroes(new BigDecimal("123.4567"), mc)); // 123.46 +System.out.println(BigDecimalMath.roundWithTrailingZeroes(new BigDecimal("0.001234567"), mc)); // 0.0012346 +System.out.println(BigDecimalMath.roundWithTrailingZeroes(new BigDecimal("1.23"), mc)); // 1.2300 +System.out.println(BigDecimalMath.roundWithTrailingZeroes(new BigDecimal("1.230000"), mc)); // 1.2300 +System.out.println(BigDecimalMath.roundWithTrailingZeroes(new BigDecimal("0.00123"), mc)); // 0.0012300 +System.out.println(BigDecimalMath.roundWithTrailingZeroes(new BigDecimal("0"), mc)); // 0.0000 +System.out.println(BigDecimalMath.roundWithTrailingZeroes(new BigDecimal("0.00000000"), mc)); // 0.0000 +</pre> + * + * @param value the {@link BigDecimal} to round + * @param mathContext the {@link MathContext} used for the result + * @return the rounded {@link BigDecimal} value including trailing zeroes + * @see BigDecimal#round(MathContext) + * @see BigDecimalMath#round(BigDecimal, MathContext) + */ + public static BigDecimal roundWithTrailingZeroes(BigDecimal value, MathContext mathContext) { + if (value.precision() == mathContext.getPrecision()) { + return value; + } + if (value.signum() == 0) { + return BigDecimal.ZERO.setScale(mathContext.getPrecision() - 1); + } + + try { + BigDecimal stripped = value.stripTrailingZeros(); + int exponentStripped = exponent(stripped); // value.precision() - value.scale() - 1; + + BigDecimal zero; + if (exponentStripped < -1) { + zero = BigDecimal.ZERO.setScale(mathContext.getPrecision() - exponentStripped); + } else { + zero = BigDecimal.ZERO.setScale(mathContext.getPrecision() + exponentStripped + 1); + } + return stripped.add(zero, mathContext); + } catch (ArithmeticException ex) { + return value.round(mathContext); + } + } + + /** + * Calculates the reciprocal of the specified {@link BigDecimal}. + * + * @param x the {@link BigDecimal} + * @param mathContext the {@link MathContext} used for the result + * @return the reciprocal {@link BigDecimal} + * @throws ArithmeticException if x = 0 + * @throws ArithmeticException if the result is inexact but the + * rounding mode is {@code UNNECESSARY} or + * {@code mc.precision == 0} and the quotient has a + * non-terminating decimal expansion. + */ + public static BigDecimal reciprocal(BigDecimal x, MathContext mathContext) { + return BigDecimal.ONE.divide(x, mathContext); + } + + /** + * Calculates the factorial of the specified integer argument. + * + * <p>factorial = 1 * 2 * 3 * ... n</p> + * + * @param n the {@link BigDecimal} + * @return the factorial {@link BigDecimal} + * @throws ArithmeticException if x < 0 + */ + public static BigDecimal factorial(int n) { + if (n < 0) { + throw new ArithmeticException("Illegal factorial(n) for n < 0: n = " + n); + } + if (n < factorialCache.length) { + return factorialCache[n]; + } + + BigDecimal result = factorialCache[factorialCache.length - 1]; + return result.multiply(factorialRecursion(factorialCache.length, n)); + } + + private static BigDecimal factorialLoop(int n1, final int n2) { + final long limit = Long.MAX_VALUE / n2; + long accu = 1; + BigDecimal result = BigDecimal.ONE; + while (n1 <= n2) { + if (accu <= limit) { + accu *= n1; + } else { + result = result.multiply(BigDecimal.valueOf(accu)); + accu = n1; + } + n1++; + } + return result.multiply(BigDecimal.valueOf(accu)); + } + + private static BigDecimal factorialRecursion(final int n1, final int n2) { + int threshold = n1 > 200 ? 80 : 150; + if (n2 - n1 < threshold) { + return factorialLoop(n1, n2); + } + final int mid = (n1 + n2) >> 1; + return factorialRecursion(mid + 1, n2).multiply(factorialRecursion(n1, mid)); + } + + /** + * Calculates the factorial of the specified {@link BigDecimal}. + * + * <p>This implementation uses + * <a href="https://en.wikipedia.org/wiki/Spouge%27s_approximation">Spouge's approximation</a> + * to calculate the factorial for non-integer values.</p> + * + * <p>This involves calculating a series of constants that depend on the desired precision. + * Since this constant calculation is quite expensive (especially for higher precisions), + * the constants for a specific precision will be cached + * and subsequent calls to this method with the same precision will be much faster.</p> + * + * <p>It is therefore recommended to do one call to this method with the standard precision of your application during the startup phase + * and to avoid calling it with many different precisions.</p> + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Factorial#Extension_of_factorial_to_non-integer_values_of_argument">Wikipedia: Factorial - Extension of factorial to non-integer values of argument</a></p> + * + * @param x the {@link BigDecimal} + * @param mathContext the {@link MathContext} used for the result + * @return the factorial {@link BigDecimal} + * @throws ArithmeticException if x is a negative integer value (-1, -2, -3, ...) + * @throws UnsupportedOperationException if x is a non-integer value and the {@link MathContext} has unlimited precision + * @see #factorial(int) + * @see #gamma(BigDecimal, MathContext) + */ + public static BigDecimal factorial(BigDecimal x, MathContext mathContext) { + if (isIntValue(x)) { + return round(factorial(x.intValueExact()), mathContext); + } + + // https://en.wikipedia.org/wiki/Spouge%27s_approximation + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() << 1, mathContext.getRoundingMode()); + + int a = mathContext.getPrecision() * 13 / 10; + List<BigDecimal> constants = getSpougeFactorialConstants(a); + + BigDecimal bigA = BigDecimal.valueOf(a); + + boolean negative = false; + BigDecimal factor = constants.get(0); + for (int k = 1; k < a; k++) { + BigDecimal bigK = BigDecimal.valueOf(k); + factor = factor.add(constants.get(k).divide(x.add(bigK), mc)); + negative = !negative; + } + + BigDecimal result = pow(x.add(bigA), x.add(BigDecimal.valueOf(0.5)), mc); + result = result.multiply(exp(x.negate().subtract(bigA), mc)); + result = result.multiply(factor); + + return round(result, mathContext); + } + + static List<BigDecimal> getSpougeFactorialConstants(int a) { + synchronized (spougeFactorialConstantsCacheLock) { + return spougeFactorialConstantsCache.computeIfAbsent(a, key -> { + List<BigDecimal> constants = new ArrayList<>(a); + MathContext mc = new MathContext(a * 15 / 10); + + BigDecimal c0 = sqrt(pi(mc).multiply(TWO, mc), mc); + constants.add(c0); + + boolean negative = false; + for (int k = 1; k < a; k++) { + BigDecimal bigK = BigDecimal.valueOf(k); + long deltaAK = (long)a - k; + BigDecimal ck = pow(BigDecimal.valueOf(deltaAK), bigK.subtract(ONE_HALF), mc); + ck = ck.multiply(exp(BigDecimal.valueOf(deltaAK), mc), mc); + ck = ck.divide(factorial(k - 1), mc); + if (negative) { + ck = ck.negate(); + } + constants.add(ck); + + negative = !negative; + } + + return Collections.unmodifiableList(constants); + }); + } + } + + /** + * Calculates the gamma function of the specified {@link BigDecimal}. + * + * <p>This implementation uses {@link #factorial(BigDecimal, MathContext)} internally, + * therefore the performance implications described there apply also for this method. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Gamma_function">Wikipedia: Gamma function</a></p> + * + * @param x the {@link BigDecimal} + * @param mathContext the {@link MathContext} used for the result + * @return the gamma {@link BigDecimal} + * @throws ArithmeticException if x-1 is a negative integer value (-1, -2, -3, ...) + * @throws UnsupportedOperationException if x is a non-integer value and the {@link MathContext} has unlimited precision + * @see #factorial(BigDecimal, MathContext) + */ + public static BigDecimal gamma(BigDecimal x, MathContext mathContext) { + return factorial(x.subtract(ONE), mathContext); + } + + /** + * 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 > 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) + * @param mathContext the {@link MathContext} used for the result + * @return the Bernoulli number for the specified index + * @throws ArithmeticException if x < 0 + * @throws ArithmeticException if the result is inexact but the + * rounding mode is {@code UNNECESSARY} or + * {@code mc.precision == 0} and the quotient has a + * non-terminating decimal expansion. + */ + public static BigDecimal bernoulli(int n, MathContext mathContext) { + if (n < 0) { + throw new ArithmeticException("Illegal bernoulli(n) for n < 0: n = " + n); + } + + BigRational b = BigRational.bernoulli(n); + return b.toBigDecimal(mathContext); + } + + /** + * Calculates {@link BigDecimal} x to the power of {@link BigDecimal} y (x<sup>y</sup>). + * + * @param x the {@link BigDecimal} value to take to the power + * @param y the {@link BigDecimal} value to serve as exponent + * @param mathContext the {@link MathContext} used for the result + * @return the calculated x to the power of y with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + * @see #pow(BigDecimal, long, MathContext) + */ + public static BigDecimal pow(BigDecimal x, BigDecimal y, MathContext mathContext) { + checkMathContext(mathContext); + if (x.signum() == 0) { + switch (y.signum()) { + case 0 : return round(ONE, mathContext); + case 1 : return round(ZERO, mathContext); + } + } + + // TODO optimize y=0, y=1, y=10^k, y=-1, y=-10^k + + try { + long longValue = y.longValueExact(); + return pow(x, longValue, mathContext); + } catch (ArithmeticException ex) { + // ignored + } + + if (fractionalPart(y).signum() == 0) { + return powInteger(x, y, mathContext); + } + + // x^y = exp(y*log(x)) + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + BigDecimal result = exp(y.multiply(log(x, mc), mc), mc); + + return round(result, mathContext); + } + + /** + * Calculates {@link BigDecimal} x to the power of <code>long</code> y (x<sup>y</sup>). + * + * <p>The implementation tries to minimize the number of multiplications of {@link BigDecimal x} (using squares whenever possible).</p> + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Exponentiation#Efficient_computation_with_integer_exponents">Wikipedia: Exponentiation - efficient computation</a></p> + * + * @param x the {@link BigDecimal} value to take to the power + * @param y the <code>long</code> value to serve as exponent + * @param mathContext the {@link MathContext} used for the result + * @return the calculated x to the power of y with the precision specified in the <code>mathContext</code> + * @throws ArithmeticException if y is negative and the result is inexact but the + * rounding mode is {@code UNNECESSARY} or + * {@code mc.precision == 0} and the quotient has a + * non-terminating decimal expansion. + * @throws ArithmeticException if the rounding mode is + * {@code UNNECESSARY} and the + * {@code BigDecimal} operation would require rounding. + */ + public static BigDecimal pow(BigDecimal x, long y, MathContext mathContext) { + MathContext mc = mathContext.getPrecision() == 0 ? mathContext : new MathContext(mathContext.getPrecision() + 10, mathContext.getRoundingMode()); + + // TODO optimize y=0, y=1, y=10^k, y=-1, y=-10^k + + if (y < 0) { + BigDecimal value = reciprocal(pow(x, -y, mc), mc); + return round(value, mathContext); + } + + BigDecimal result = ONE; + while (y > 0) { + if ((y & 1) == 1) { + // odd exponent -> multiply result with x + result = result.multiply(x, mc); + y -= 1; + } + + if (y > 0) { + // even exponent -> square x + x = x.multiply(x, mc); + } + + y >>= 1; + } + + return round(result, mathContext); + } + + /** + * Calculates {@link BigDecimal} x to the power of the integer value y (x<sup>y</sup>). + * + * <p>The value y MUST be an integer value.</p> + * + * @param x the {@link BigDecimal} value to take to the power + * @param integerY the {@link BigDecimal} <strong>integer</strong> value to serve as exponent + * @param mathContext the {@link MathContext} used for the result + * @return the calculated x to the power of y with the precision specified in the <code>mathContext</code> + * @see #pow(BigDecimal, long, MathContext) + */ + private static BigDecimal powInteger(BigDecimal x, BigDecimal integerY, MathContext mathContext) { + if (fractionalPart(integerY).signum() != 0) { + throw new IllegalArgumentException("Not integer value: " + integerY); + } + + if (integerY.signum() < 0) { + return ONE.divide(powInteger(x, integerY.negate(), mathContext), mathContext); + } + + MathContext mc = new MathContext(Math.max(mathContext.getPrecision(), -integerY.scale()) + 30, mathContext.getRoundingMode()); + + BigDecimal result = ONE; + while (integerY.signum() > 0) { + BigDecimal halfY = integerY.divide(TWO, mc); + + if (fractionalPart(halfY).signum() != 0) { + // odd exponent -> multiply result with x + result = result.multiply(x, mc); + integerY = integerY.subtract(ONE); + halfY = integerY.divide(TWO, mc); + } + + if (halfY.signum() > 0) { + // even exponent -> square x + x = x.multiply(x, mc); + } + + integerY = halfY; + } + + return round(result, mathContext); + } + + /** + * Calculates the square root of {@link BigDecimal} x. + * + * <p>See <a href="http://en.wikipedia.org/wiki/Square_root">Wikipedia: Square root</a></p> + * + * @param x the {@link BigDecimal} value to calculate the square root + * @param mathContext the {@link MathContext} used for the result + * @return the calculated square root of x with the precision specified in the <code>mathContext</code> + * @throws ArithmeticException if x < 0 + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal sqrt(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + switch (x.signum()) { + case 0: + return ZERO; + case -1: + throw new ArithmeticException("Illegal sqrt(x) for x < 0: x = " + x); + } + + int maxPrecision = mathContext.getPrecision() + 6; + BigDecimal acceptableError = ONE.movePointLeft(mathContext.getPrecision() + 1); + + BigDecimal result; + int adaptivePrecision; + if (isDoubleValue(x)) { + result = BigDecimal.valueOf(Math.sqrt(x.doubleValue())); + adaptivePrecision = EXPECTED_INITIAL_PRECISION; + } else { + result = x.multiply(ONE_HALF, mathContext); + adaptivePrecision = 1; + } + + BigDecimal last; + + if (adaptivePrecision < maxPrecision) { + if (result.multiply(result).compareTo(x) == 0) { + return round(result, mathContext); // early exit if x is a square number + } + + do { + last = result; + adaptivePrecision <<= 1; + if (adaptivePrecision > maxPrecision) { + adaptivePrecision = maxPrecision; + } + MathContext mc = new MathContext(adaptivePrecision, mathContext.getRoundingMode()); + result = x.divide(result, mc).add(last).multiply(ONE_HALF, mc); + } + while (adaptivePrecision < maxPrecision || result.subtract(last).abs().compareTo(acceptableError) > 0); + } + + return round(result, mathContext); + } + + /** + * Calculates the n'th root of {@link BigDecimal} x. + * + * <p>See <a href="http://en.wikipedia.org/wiki/Square_root">Wikipedia: Square root</a></p> + * @param x the {@link BigDecimal} value to calculate the n'th root + * @param n the {@link BigDecimal} defining the root + * @param mathContext the {@link MathContext} used for the result + * + * @return the calculated n'th root of x with the precision specified in the <code>mathContext</code> + * @throws ArithmeticException if x < 0 + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal root(BigDecimal x, BigDecimal n, MathContext mathContext) { + checkMathContext(mathContext); + switch (x.signum()) { + case 0: + return ZERO; + case -1: + throw new ArithmeticException("Illegal root(x) for x < 0: x = " + x); + } + + if (n.compareTo(BigDecimal.ONE) <= 0) { + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + return pow(x, BigDecimal.ONE.divide(n, mc), mathContext); + } + + int maxPrecision = mathContext.getPrecision() + 4; + BigDecimal acceptableError = ONE.movePointLeft(mathContext.getPrecision() + 1); + + BigDecimal nMinus1 = n.subtract(ONE); + BigDecimal result = x.divide(TWO, MathContext.DECIMAL32); + int adaptivePrecision = 2; // first approximation has really bad precision + BigDecimal step; + + do { + adaptivePrecision *= 3; + if (adaptivePrecision > maxPrecision) { + adaptivePrecision = maxPrecision; + } + MathContext mc = new MathContext(adaptivePrecision, mathContext.getRoundingMode()); + + step = x.divide(pow(result, nMinus1, mc), mc).subtract(result).divide(n, mc); + result = result.add(step); + } while (adaptivePrecision < maxPrecision || step.abs().compareTo(acceptableError) > 0); + + return round(result, mathContext); + } + + /** + * Calculates the natural logarithm of {@link BigDecimal} x. + * + * <p>See: <a href="http://en.wikipedia.org/wiki/Natural_logarithm">Wikipedia: Natural logarithm</a></p> + * + * @param x the {@link BigDecimal} to calculate the natural logarithm for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated natural logarithm {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws ArithmeticException if x <= 0 + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal log(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + if (x.signum() <= 0) { + throw new ArithmeticException("Illegal log(x) for x <= 0: x = " + x); + } + if (x.compareTo(ONE) == 0) { + return ZERO; + } + + BigDecimal result; + switch (x.compareTo(TEN)) { + case 0: + result = logTen(mathContext); + break; + case 1: + result = logUsingExponent(x, mathContext); + break; + default : + result = logUsingTwoThree(x, mathContext); + } + + return round(result, mathContext); + } + + /** + * Calculates the logarithm of {@link BigDecimal} x to the base 2. + * + * @param x the {@link BigDecimal} to calculate the logarithm base 2 for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated natural logarithm {@link BigDecimal} to the base 2 with the precision specified in the <code>mathContext</code> + * @throws ArithmeticException if x <= 0 + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal log2(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + + BigDecimal result = log(x, mc).divide(logTwo(mc), mc); + return round(result, mathContext); + } + + /** + * Calculates the logarithm of {@link BigDecimal} x to the base 10. + * + * @param x the {@link BigDecimal} to calculate the logarithm base 10 for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated natural logarithm {@link BigDecimal} to the base 10 with the precision specified in the <code>mathContext</code> + * @throws ArithmeticException if x <= 0 + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal log10(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 2, mathContext.getRoundingMode()); + + BigDecimal result = log(x, mc).divide(logTen(mc), mc); + return round(result, mathContext); + } + + private static BigDecimal logUsingNewton(BigDecimal x, MathContext mathContext) { + // https://en.wikipedia.org/wiki/Natural_logarithm in chapter 'High Precision' + // y = y + 2 * (x-exp(y)) / (x+exp(y)) + + int maxPrecision = mathContext.getPrecision() + 20; + BigDecimal acceptableError = ONE.movePointLeft(mathContext.getPrecision() + 1); + //System.out.println("logUsingNewton(" + x + " " + mathContext + ") precision " + maxPrecision); + + BigDecimal result; + int adaptivePrecision; + double doubleX = x.doubleValue(); + if (doubleX > 0.0 && isDoubleValue(x)) { + result = BigDecimal.valueOf(Math.log(doubleX)); + adaptivePrecision = EXPECTED_INITIAL_PRECISION; + } else { + result = x.divide(TWO, mathContext); + adaptivePrecision = 1; + } + + BigDecimal step; + + do { + adaptivePrecision *= 3; + if (adaptivePrecision > maxPrecision) { + adaptivePrecision = maxPrecision; + } + MathContext mc = new MathContext(adaptivePrecision, mathContext.getRoundingMode()); + + BigDecimal expY = BigDecimalMath.exp(result, mc); + step = TWO.multiply(x.subtract(expY)).divide(x.add(expY), mc); + //System.out.println(" step " + step + " adaptivePrecision=" + adaptivePrecision); + result = result.add(step); + } while (adaptivePrecision < maxPrecision || step.abs().compareTo(acceptableError) > 0); + + return result; + } + + private static BigDecimal logUsingExponent(BigDecimal x, MathContext mathContext) { + MathContext mcDouble = new MathContext(mathContext.getPrecision() << 1, mathContext.getRoundingMode()); + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + //System.out.println("logUsingExponent(" + x + " " + mathContext + ") precision " + mc); + + int exponent = exponent(x); + BigDecimal mantissa = mantissa(x); + + BigDecimal result = logUsingTwoThree(mantissa, mc); + if (exponent != 0) { + result = result.add(valueOf(exponent).multiply(logTen(mcDouble), mc)); + } + return result; + } + + private static BigDecimal logUsingTwoThree(BigDecimal x, MathContext mathContext) { + MathContext mcDouble = new MathContext(mathContext.getPrecision() << 1, mathContext.getRoundingMode()); + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + //System.out.println("logUsingTwoThree(" + x + " " + mathContext + ") precision " + mc); + + int factorOfTwo = 0; + int powerOfTwo = 1; + int factorOfThree = 0; + int powerOfThree = 1; + + double value = x.doubleValue(); + if (value < 0.01) { + // do nothing + } else if (value < 0.1) { // never happens when called by logUsingExponent() + while (value < 0.6) { + value *= 2; + factorOfTwo--; + powerOfTwo <<= 1; + } + } + else if (value < 0.115) { // (0.1 - 0.11111 - 0.115) -> (0.9 - 1.0 - 1.035) + factorOfThree = -2; + powerOfThree = 9; + } + else if (value < 0.14) { // (0.115 - 0.125 - 0.14) -> (0.92 - 1.0 - 1.12) + factorOfTwo = -3; + powerOfTwo = 8; + } + else if (value < 0.2) { // (0.14 - 0.16667 - 0.2) - (0.84 - 1.0 - 1.2) + factorOfTwo = -1; + powerOfTwo = 2; + factorOfThree = -1; + powerOfThree = 3; + } + else if (value < 0.3) { // (0.2 - 0.25 - 0.3) -> (0.8 - 1.0 - 1.2) + factorOfTwo = -2; + powerOfTwo = 4; + } + else if (value < 0.42) { // (0.3 - 0.33333 - 0.42) -> (0.9 - 1.0 - 1.26) + factorOfThree = -1; + powerOfThree = 3; + } + else if (value < 0.7) { // (0.42 - 0.5 - 0.7) -> (0.84 - 1.0 - 1.4) + factorOfTwo = -1; + powerOfTwo = 2; + } + else if (value < 1.4) { // (0.7 - 1.0 - 1.4) -> (0.7 - 1.0 - 1.4) + // do nothing + } + else if (value < 2.5) { // (1.4 - 2.0 - 2.5) -> (0.7 - 1.0 - 1.25) + factorOfTwo = 1; + powerOfTwo = 2; + } + else if (value < 3.5) { // (2.5 - 3.0 - 3.5) -> (0.833333 - 1.0 - 1.166667) + factorOfThree = 1; + powerOfThree = 3; + } + else if (value < 5.0) { // (3.5 - 4.0 - 5.0) -> (0.875 - 1.0 - 1.25) + factorOfTwo = 2; + powerOfTwo = 4; + } + else if (value < 7.0) { // (5.0 - 6.0 - 7.0) -> (0.833333 - 1.0 - 1.166667) + factorOfThree = 1; + powerOfThree = 3; + factorOfTwo = 1; + powerOfTwo = 2; + } + else if (value < 8.5) { // (7.0 - 8.0 - 8.5) -> (0.875 - 1.0 - 1.0625) + factorOfTwo = 3; + powerOfTwo = 8; + } + else if (value < 10.0) { // (8.5 - 9.0 - 10.0) -> (0.94444 - 1.0 - 1.11111) + factorOfThree = 2; + powerOfThree = 9; + } + else { + while (value > 1.4) { // never happens when called by logUsingExponent() + value /= 2; + factorOfTwo++; + powerOfTwo <<= 1; + } + } + + BigDecimal correctedX = x; + BigDecimal result = ZERO; + + if (factorOfTwo > 0) { + correctedX = correctedX.divide(valueOf(powerOfTwo), mc); + result = result.add(logTwo(mcDouble).multiply(valueOf(factorOfTwo), mc)); + } + else if (factorOfTwo < 0) { + correctedX = correctedX.multiply(valueOf(powerOfTwo), mc); + result = result.subtract(logTwo(mcDouble).multiply(valueOf(-factorOfTwo), mc)); + } + + if (factorOfThree > 0) { + correctedX = correctedX.divide(valueOf(powerOfThree), mc); + result = result.add(logThree(mcDouble).multiply(valueOf(factorOfThree), mc)); + } + else if (factorOfThree < 0) { + correctedX = correctedX.multiply(valueOf(powerOfThree), mc); + result = result.subtract(logThree(mcDouble).multiply(valueOf(-factorOfThree), mc)); + } + + if (x == correctedX && result == ZERO) { + return logUsingNewton(x, mathContext); + } + + result = result.add(logUsingNewton(correctedX, mc), mc); + + return result; + } + + /** + * Returns the number pi. + * + * <p>See <a href="https://en.wikipedia.org/wiki/Pi">Wikipedia: Pi</a></p> + * + * @param mathContext the {@link MathContext} used for the result + * @return the number pi with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal pi(MathContext mathContext) { + checkMathContext(mathContext); + BigDecimal result = null; + + synchronized (piCacheLock) { + if (piCache != null && mathContext.getPrecision() <= piCache.precision()) { + result = piCache; + } else { + piCache = piChudnovski(mathContext); + return piCache; + } + } + + return round(result, mathContext); + } + + private static BigDecimal piChudnovski(MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 10, mathContext.getRoundingMode()); + + final BigDecimal value24 = BigDecimal.valueOf(24); + final BigDecimal value640320 = BigDecimal.valueOf(640320); + final BigDecimal value13591409 = BigDecimal.valueOf(13591409); + final BigDecimal value545140134 = BigDecimal.valueOf(545140134); + final BigDecimal valueDivisor = value640320.pow(3).divide(value24, mc); + + BigDecimal sumA = BigDecimal.ONE; + BigDecimal sumB = BigDecimal.ZERO; + + BigDecimal a = BigDecimal.ONE; + long dividendTerm1 = 5; // -(6*k - 5) + long dividendTerm2 = -1; // 2*k - 1 + long dividendTerm3 = -1; // 6*k - 1 + BigDecimal kPower3 = BigDecimal.ZERO; + + long iterationCount = (mc.getPrecision()+13) / 14; + for (long k = 1; k <= iterationCount; k++) { + BigDecimal valueK = BigDecimal.valueOf(k); + dividendTerm1 += -6; + dividendTerm2 += 2; + dividendTerm3 += 6; + BigDecimal dividend = BigDecimal.valueOf(dividendTerm1).multiply(BigDecimal.valueOf(dividendTerm2)).multiply(BigDecimal.valueOf(dividendTerm3)); + kPower3 = valueK.pow(3); + BigDecimal divisor = kPower3.multiply(valueDivisor, mc); + a = a.multiply(dividend).divide(divisor, mc); + BigDecimal b = valueK.multiply(a, mc); + + sumA = sumA.add(a); + sumB = sumB.add(b); + } + + final BigDecimal value426880 = BigDecimal.valueOf(426880); + final BigDecimal value10005 = BigDecimal.valueOf(10005); + final BigDecimal factor = value426880.multiply(sqrt(value10005, mc)); + BigDecimal pi = factor.divide(value13591409.multiply(sumA, mc).add(value545140134.multiply(sumB, mc)), mc); + + return round(pi, mathContext); + } + + /** + * Returns the number e. + * + * <p>See <a href="https://en.wikipedia.org/wiki/E_(mathematical_constant)">Wikipedia: E (mathematical_constant)</a></p> + * + * @param mathContext the {@link MathContext} used for the result + * @return the number e with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal e(MathContext mathContext) { + checkMathContext(mathContext); + BigDecimal result = null; + + synchronized (eCacheLock) { + if (eCache != null && mathContext.getPrecision() <= eCache.precision()) { + result = eCache; + } else { + eCache = exp(ONE, mathContext); + return eCache; + } + } + + return round(result, mathContext); + } + + private static BigDecimal logTen(MathContext mathContext) { + BigDecimal result = null; + + synchronized (log10CacheLock) { + if (log10Cache != null && mathContext.getPrecision() <= log10Cache.precision()) { + result = log10Cache; + } else { + log10Cache = logUsingNewton(BigDecimal.TEN, mathContext); + return log10Cache; + } + } + + return round(result, mathContext); + } + + private static BigDecimal logTwo(MathContext mathContext) { + BigDecimal result = null; + + synchronized (log2CacheLock) { + if (log2Cache != null && mathContext.getPrecision() <= log2Cache.precision()) { + result = log2Cache; + } else { + log2Cache = logUsingNewton(TWO, mathContext); + return log2Cache; + } + } + + return round(result, mathContext); + } + + private static BigDecimal logThree(MathContext mathContext) { + BigDecimal result = null; + + synchronized (log3CacheLock) { + if (log3Cache != null && mathContext.getPrecision() <= log3Cache.precision()) { + result = log3Cache; + } else { + log3Cache = logUsingNewton(THREE, mathContext); + return log3Cache; + } + } + + return round(result, mathContext); + } + + /** + * Calculates the natural exponent of {@link BigDecimal} x (e<sup>x</sup>). + * + * <p>See: <a href="http://en.wikipedia.org/wiki/Exponent">Wikipedia: Exponent</a></p> + * + * @param x the {@link BigDecimal} to calculate the exponent for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated exponent {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal exp(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + if (x.signum() == 0) { + return ONE; + } + + return expIntegralFractional(x, mathContext); + } + + private static BigDecimal expIntegralFractional(BigDecimal x, MathContext mathContext) { + BigDecimal integralPart = integralPart(x); + + if (integralPart.signum() == 0) { + return expTaylor(x, mathContext); + } + + BigDecimal fractionalPart = x.subtract(integralPart); + + MathContext mc = new MathContext(mathContext.getPrecision() + 10, mathContext.getRoundingMode()); + + BigDecimal z = ONE.add(fractionalPart.divide(integralPart, mc)); + BigDecimal t = expTaylor(z, mc); + + BigDecimal result = pow(t, integralPart.intValueExact(), mc); + + return round(result, mathContext); + } + + private static BigDecimal expTaylor(BigDecimal x, MathContext mathContext) { + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + + x = x.divide(valueOf(256), mc); + + BigDecimal result = ExpCalculator.INSTANCE.calculate(x, mc); + result = BigDecimalMath.pow(result, 256, mc); + return round(result, mathContext); + } + + /** + * Calculates the sine (sinus) of {@link BigDecimal} x. + * + * <p>See: <a href="http://en.wikipedia.org/wiki/Sine">Wikipedia: Sine</a></p> + * + * @param x the {@link BigDecimal} to calculate the sine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated sine {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal sin(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + + if (x.abs().compareTo(ROUGHLY_TWO_PI) > 0) { + MathContext mc2 = new MathContext(mc.getPrecision() + 4, mathContext.getRoundingMode()); + BigDecimal twoPi = TWO.multiply(pi(mc2)); + x = x.remainder(twoPi, mc2); + } + + BigDecimal result = SinCalculator.INSTANCE.calculate(x, mc); + return round(result, mathContext); + } + + /** + * Calculates the arc sine (inverted sine) of {@link BigDecimal} x. + * + * <p>See: <a href="http://en.wikipedia.org/wiki/Arcsine">Wikipedia: Arcsine</a></p> + * + * @param x the {@link BigDecimal} to calculate the arc sine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc sine {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws ArithmeticException if x > 1 or x < -1 + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal asin(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + if (x.compareTo(ONE) > 0) { + throw new ArithmeticException("Illegal asin(x) for x > 1: x = " + x); + } + if (x.compareTo(MINUS_ONE) < 0) { + throw new ArithmeticException("Illegal asin(x) for x < -1: x = " + x); + } + + if (x.signum() == -1) { + return asin(x.negate(), mathContext).negate(); + } + + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + + if (x.compareTo(BigDecimal.valueOf(0.707107)) >= 0) { + BigDecimal xTransformed = sqrt(ONE.subtract(x.multiply(x)), mc); + return acos(xTransformed, mathContext); + } + + BigDecimal result = AsinCalculator.INSTANCE.calculate(x, mc); + return round(result, mathContext); + } + + /** + * Calculates the cosine (cosinus) of {@link BigDecimal} x. + * + * <p>See: <a href="http://en.wikipedia.org/wiki/Cosine">Wikipedia: Cosine</a></p> + * + * @param x the {@link BigDecimal} to calculate the cosine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated cosine {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal cos(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + + if (x.abs().compareTo(ROUGHLY_TWO_PI) > 0) { + MathContext mc2 = new MathContext(mc.getPrecision() + 4, mathContext.getRoundingMode()); + BigDecimal twoPi = TWO.multiply(pi(mc2), mc2); + x = x.remainder(twoPi, mc2); + } + + BigDecimal result = CosCalculator.INSTANCE.calculate(x, mc); + return round(result, mathContext); + } + + /** + * Calculates the arc cosine (inverted cosine) of {@link BigDecimal} x. + * + * <p>See: <a href="http://en.wikipedia.org/wiki/Arccosine">Wikipedia: Arccosine</a></p> + * + * @param x the {@link BigDecimal} to calculate the arc cosine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc sine {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws ArithmeticException if x > 1 or x < -1 + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal acos(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + if (x.compareTo(ONE) > 0) { + throw new ArithmeticException("Illegal acos(x) for x > 1: x = " + x); + } + if (x.compareTo(MINUS_ONE) < 0) { + throw new ArithmeticException("Illegal acos(x) for x < -1: x = " + x); + } + + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + + BigDecimal result = pi(mc).divide(TWO, mc).subtract(asin(x, mc)); + return round(result, mathContext); + } + + /** + * Calculates the tangens of {@link BigDecimal} x. + * + * <p>See: <a href="http://en.wikipedia.org/wiki/Tangens">Wikipedia: Tangens</a></p> + * + * @param x the {@link BigDecimal} to calculate the tangens for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated tangens {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal tan(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + if (x.signum() == 0) { + return ZERO; + } + + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + BigDecimal result = sin(x, mc).divide(cos(x, mc), mc); + return round(result, mathContext); + } + + /** + * Calculates the arc tangens (inverted tangens) of {@link BigDecimal} x. + * + * <p>See: <a href="http://en.wikipedia.org/wiki/Arctangens">Wikipedia: Arctangens</a></p> + * + * @param x the {@link BigDecimal} to calculate the arc tangens for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc tangens {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal atan(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + + x = x.divide(sqrt(ONE.add(x.multiply(x, mc)), mc), mc); + + BigDecimal result = asin(x, mc); + return round(result, mathContext); + } + + /** + * Calculates the arc tangens (inverted tangens) of {@link BigDecimal} y / x in the range -<i>pi</i> to <i>pi</i>. + * + * <p>This is useful to calculate the angle <i>theta</i> from the conversion of rectangular + * coordinates (<code>x</code>, <code>y</code>) to polar coordinates (r, <i>theta</i>).</p> + * + * <p>See: <a href="http://en.wikipedia.org/wiki/Atan2">Wikipedia: Atan2</a></p> + * + * @param y the {@link BigDecimal} + * @param x the {@link BigDecimal} + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc tangens {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws ArithmeticException if x = 0 and y = 0 + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal atan2(BigDecimal y, BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 3, mathContext.getRoundingMode()); + + if (x.signum() > 0) { // x > 0 + return atan(y.divide(x, mc), mathContext); + } else if (x.signum() < 0) { + if (y.signum() > 0) { // x < 0 && y > 0 + return atan(y.divide(x, mc), mc).add(pi(mc), mathContext); + } else if (y.signum() < 0) { // x < 0 && y < 0 + return atan(y.divide(x, mc), mc).subtract(pi(mc), mathContext); + } else { // x < 0 && y = 0 + return pi(mathContext); + } + } else { + if (y.signum() > 0) { // x == 0 && y > 0 + return pi(mc).divide(TWO, mathContext); + } else if (y.signum() < 0) { // x == 0 && y < 0 + return pi(mc).divide(TWO, mathContext).negate(); + } else { + throw new ArithmeticException("Illegal atan2(y, x) for x = 0; y = 0"); + } + } + } + + /** + * Calculates the cotangens of {@link BigDecimal} x. + * + * <p>See: <a href="http://en.wikipedia.org/wiki/Cotangens">Wikipedia: Cotangens</a></p> + * + * @param x the {@link BigDecimal} to calculate the cotangens for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated cotanges {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws ArithmeticException if x = 0 + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal cot(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + if (x.signum() == 0) { + throw new ArithmeticException("Illegal cot(x) for x = 0"); + } + + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + BigDecimal result = cos(x, mc).divide(sin(x, mc), mc); + return round(result, mathContext); + } + + /** + * Calculates the inverse cotangens (arc cotangens) of {@link BigDecimal} x. + * + * <p>See: <a href="http://en.wikipedia.org/wiki/Arccotangens">Wikipedia: Arccotangens</a></p> + * + * @param x the {@link BigDecimal} to calculate the arc cotangens for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc cotangens {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal acot(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + BigDecimal result = pi(mc).divide(TWO, mc).subtract(atan(x, mc)); + return round(result, mathContext); + } + + /** + * Calculates the hyperbolic sine of {@link BigDecimal} x. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Hyperbolic_function">Wikipedia: Hyperbolic function</a></p> + * + * @param x the {@link BigDecimal} to calculate the hyperbolic sine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated hyperbolic sine {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal sinh(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + BigDecimal result = SinhCalculator.INSTANCE.calculate(x, mc); + return round(result, mathContext); + } + + /** + * Calculates the hyperbolic cosine of {@link BigDecimal} x. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Hyperbolic_function">Wikipedia: Hyperbolic function</a></p> + * + * @param x the {@link BigDecimal} to calculate the hyperbolic cosine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated hyperbolic cosine {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal cosh(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 4, mathContext.getRoundingMode()); + BigDecimal result = CoshCalculator.INSTANCE.calculate(x, mc); + return round(result, mathContext); + } + + /** + * Calculates the hyperbolic tangens of {@link BigDecimal} x. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Hyperbolic_function">Wikipedia: Hyperbolic function</a></p> + * + * @param x the {@link BigDecimal} to calculate the hyperbolic tangens for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated hyperbolic tangens {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal tanh(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + BigDecimal result = sinh(x, mc).divide(cosh(x, mc), mc); + return round(result, mathContext); + } + + /** + * Calculates the hyperbolic cotangens of {@link BigDecimal} x. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Hyperbolic_function">Wikipedia: Hyperbolic function</a></p> + * + * @param x the {@link BigDecimal} to calculate the hyperbolic cotangens for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated hyperbolic cotangens {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal coth(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + BigDecimal result = cosh(x, mc).divide(sinh(x, mc), mc); + return round(result, mathContext); + } + + /** + * Calculates the arc hyperbolic sine (inverse hyperbolic sine) of {@link BigDecimal} x. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Hyperbolic_function">Wikipedia: Hyperbolic function</a></p> + * + * @param x the {@link BigDecimal} to calculate the arc hyperbolic sine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc hyperbolic sine {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal asinh(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 10, mathContext.getRoundingMode()); + BigDecimal result = log(x.add(sqrt(x.multiply(x, mc).add(ONE, mc), mc)), mc); + return round(result, mathContext); + } + + /** + * Calculates the arc hyperbolic cosine (inverse hyperbolic cosine) of {@link BigDecimal} x. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Hyperbolic_function">Wikipedia: Hyperbolic function</a></p> + * + * @param x the {@link BigDecimal} to calculate the arc hyperbolic cosine for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc hyperbolic cosine {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal acosh(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + BigDecimal result = log(x.add(sqrt(x.multiply(x).subtract(ONE), mc)), mc); + return round(result, mathContext); + } + + /** + * Calculates the arc hyperbolic tangens (inverse hyperbolic tangens) of {@link BigDecimal} x. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Hyperbolic_function">Wikipedia: Hyperbolic function</a></p> + * + * @param x the {@link BigDecimal} to calculate the arc hyperbolic tangens for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc hyperbolic tangens {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal atanh(BigDecimal x, MathContext mathContext) { + if (x.compareTo(BigDecimal.ONE) >= 0) { + throw new ArithmeticException("Illegal atanh(x) for x >= 1: x = " + x); + } + if (x.compareTo(MINUS_ONE) <= 0) { + throw new ArithmeticException("Illegal atanh(x) for x <= -1: x = " + x); + } + + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + BigDecimal result = log(ONE.add(x).divide(ONE.subtract(x), mc), mc).multiply(ONE_HALF); + return round(result, mathContext); + } + + /** + * Calculates the arc hyperbolic cotangens (inverse hyperbolic cotangens) of {@link BigDecimal} x. + * + * <p>See: <a href="https://en.wikipedia.org/wiki/Hyperbolic_function">Wikipedia: Hyperbolic function</a></p> + * + * @param x the {@link BigDecimal} to calculate the arc hyperbolic cotangens for + * @param mathContext the {@link MathContext} used for the result + * @return the calculated arc hyperbolic cotangens {@link BigDecimal} with the precision specified in the <code>mathContext</code> + * @throws UnsupportedOperationException if the {@link MathContext} has unlimited precision + */ + public static BigDecimal acoth(BigDecimal x, MathContext mathContext) { + checkMathContext(mathContext); + MathContext mc = new MathContext(mathContext.getPrecision() + 6, mathContext.getRoundingMode()); + BigDecimal result = log(x.add(ONE).divide(x.subtract(ONE), mc), mc).multiply(ONE_HALF); + return round(result, mathContext); + } + + private static void checkMathContext (MathContext mathContext) { + if (mathContext.getPrecision() == 0) { + throw new UnsupportedOperationException("Unlimited MathContext not supported"); + } + } +} diff --git a/src/main/java/ch/obermuhlner/math/big/BigFloat.java b/src/main/java/ch/obermuhlner/math/big/BigFloat.java new file mode 100644 index 0000000000..eb8944f2c4 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/BigFloat.java @@ -0,0 +1,1947 @@ +package ch.obermuhlner.math.big; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.Objects; + +/** + * A wrapper around {@link BigDecimal} which simplifies the consistent usage of the {@link MathContext} + * and provides a simpler API for calculations. + * + * <h1>Overview</h1> + * + * <p>Every {@link BigFloat} instance has a reference to a {@link Context} that specifies the {@link MathContext} to be used for all calculations and values.</p> + * + * <p>The API for calculations is simplified and more consistent with the typical mathematical usage.</p> + * <ul> + * <li>Factory methods for values: + * <ul> + * <li><code>valueOf(BigFloat)</code></li> + * <li><code>valueOf(BigDecimal)</code></li> + * <li><code>valueOf(int)</code></li> + * <li><code>valueOf(long)</code></li> + * <li><code>valueOf(double)</code></li> + * <li><code>valueOf(String)</code></li> + * <li><code>pi()</code></li> + * <li><code>e()</code></li> + * </ul> + * </li> + * <li>All standard operators: + * <ul> + * <li><code>add(x)</code></li> + * <li><code>subtract(x)</code></li> + * <li><code>multiply(x)</code></li> + * <li><code>remainder(x)</code></li> + * <li><code>pow(y)</code></li> + * <li><code>root(y)</code></li> + * </ul> + * </li> + * <li>Calculation methods are overloaded for different value types: + * <ul> + * <li><code>add(BigFloat)</code></li> + * <li><code>add(BigDecimal)</code></li> + * <li><code>add(int)</code></li> + * <li><code>add(long)</code></li> + * <li><code>add(double)</code></li> + * <li>...</li> + * </ul> + * </li> + * <li>Mathematical functions are written as they are traditionally are written: + * <ul> + * <li><code>abs(x)</code></li> + * <li><code>log(x)</code></li> + * <li><code>sin(x)</code></li> + * <li><code>min(x1, x2, ...)</code></li> + * <li><code>max(x1, x2, ...)</code></li> + * <li>...</li> + * </ul> + * </li> + * <li>Support for advanced mathematical functions: + * <ul> + * <li><code>sqrt(x)</code></li> + * <li><code>log(x)</code></li> + * <li><code>exp(x)</code></li> + * <li><code>sin(x)</code></li> + * <li><code>cos(x)</code></li> + * <li><code>tan(x)</code></li> + * <li>...</li> + * </ul> + * </li> + * <li>Methods to access parts of a value: + * <ul> + * <li><code>getMantissa()</code></li> + * <li><code>getExponent()</code></li> + * <li><code>getIntegralPart()</code></li> + * <li><code>getFractionalPart()</code></li> + * </ul> + * </li> + * <li>Equals and Hashcode methods: + * <ul> + * <li><code>equals(Object)</code> that returns whether two <code>BigFloat</code> values are mathematically the same</li> + * <li><code>hashCode()</code> consistent with <code>equals(Object)</code></li> + * </ul> + * </li> + * <li>Comparison methods: + * <ul> + * <li><code>isEqual(BigFloat)</code></li> + * <li><code>isLessThan(BigFloat)</code></li> + * <li><code>isLessThanOrEqual(BigFloat)</code></li> + * <li><code>isGreaterThan(BigFloat)</code></li> + * <li><code>isGreaterThanOrEqual(BigFloat)</code></li> + * </ul> + * </li> + * </ul> + * + * <h1>Usage</h1> + * + * <p>Before doing any calculations you need to create a <code>Context</code> specifying the precision used for all calculations.</p> + * <pre> + * Context context = BigFloat.context(100); // precision of 100 digits + * Context anotherContext = BigFloat.context(new MathContext(10, RoundingMode.HALF_UP); // precision of 10 digits, rounding half up + * </pre> + * + * <p>The <code>Context</code> can then be used to create the first value of the calculation:</p> + * <pre> + * BigFloat value1 = context.valueOf(640320); + * </pre> + * + * <p>The <code>BigFloat</code> instance holds a reference to the <code>Context</code>. This context is then passed from calculation to calculation.</p> + * <pre> + * BigFloat value2 = context.valueOf(640320).pow(3).divide(24); + * BigFloat value3 = BigFloat.sin(value2); + * </pre> + * + * <p>The <code>BigFloat</code> result can be converted to other numerical types:</p> + * <pre> + * BigDecimal bigDecimalValue = value3.toBigDecimal(); + * double doubleValue = value3.toDouble(); + * long longValue = value3.toLong(); + * int intValue = value3.toInt(); + * </pre> + */ +@SuppressWarnings("WeakerAccess") +public class BigFloat implements Comparable<BigFloat>, Serializable { + private static final long serialVersionUID = -7323679117445486894L; + + /** + * Represents a value that is not a number. + * @see Double#NaN + */ + public static final BigFloat NaN = new SpecialBigFloat(SpecialBigFloat.Type.NaN); + + /** + * Represents the positive infinity. + * @see Double#POSITIVE_INFINITY + */ + public static final BigFloat POSITIVE_INFINITY = new SpecialBigFloat(SpecialBigFloat.Type.POSITIVE_INFINITY); + + /** + * Represents the positive infinity. + * @see Double#NEGATIVE_INFINITY + */ + public static final BigFloat NEGATIVE_INFINITY = new SpecialBigFloat(SpecialBigFloat.Type.NEGATIVE_INFINITY); + + private final BigDecimal value; + private final Context context; + + private BigFloat(BigDecimal value, Context context) { + this.value = value; + this.context = context; + } + + /** + * Creates a {@link Context} with the specified precision and {@link RoundingMode#HALF_UP} rounding. + * + * @param precision the precision + * + * @return the {@link Context} + */ + public static Context context(int precision) { + return new Context(new MathContext(precision)); + } + + /** + * Creates a {@link Context} with the specified {@link MathContext}. + * + * @param mathContext the {@link MathContext} + * + * @return the {@link Context} + */ + public static Context context(MathContext mathContext) { + return new Context(mathContext); + } + + /** + * Returns the {@link BigFloat} that is <code>this + x</code>. + * + * <p>If the two values do not have the same {@link Context}, the result will contain the {@link Context} with the larger precision.</p> + * + * @param x the value to add + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#add(BigDecimal, MathContext) + */ + public BigFloat add(BigFloat x) { + if (x.isSpecial()) + return x.add(this); + Context c = max(context, x.context); + return c.valueOf(value.add(x.value, c.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>this + x</code>. + * + * @param x the value to add + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#add(BigDecimal, MathContext) + */ + public BigFloat add(BigDecimal x) { + return add(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this + x</code>. + * + * @param x the value to add + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#add(BigDecimal, MathContext) + */ + public BigFloat add(int x) { + return add(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this + x</code>. + * + * @param x the value to add + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#add(BigDecimal, MathContext) + */ + public BigFloat add(long x) { + return add(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this + x</code>. + * + * @param x the value to add + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#add(BigDecimal, MathContext) + */ + public BigFloat add(double x) { + return add(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this - x</code>. + * + * <p>If the two values do not have the same {@link Context}, the result will contain the {@link Context} with the larger precision.</p> + * + * @param x the value to subtract + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#subtract(BigDecimal, MathContext) + */ + public BigFloat subtract(BigFloat x) { + if (x.isSpecial()) + return negate(x).add(this); + Context c = max(context, x.context); + return c.valueOf(value.subtract(x.value, c.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>this - x</code>. + * + * @param x the value to subtract + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#subtract(BigDecimal, MathContext) + */ + public BigFloat subtract(BigDecimal x) { + return subtract(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this - x</code>. + * + * @param x the value to subtract + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#subtract(BigDecimal, MathContext) + */ + public BigFloat subtract(int x) { + return subtract(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this - x</code>. + * + * @param x the value to subtract + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#subtract(BigDecimal, MathContext) + */ + public BigFloat subtract(long x) { + return subtract(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this - x</code>. + * + * @param x the value to subtract + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#subtract(BigDecimal, MathContext) + */ + public BigFloat subtract(double x) { + return subtract(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this * x</code>. + * + * <p>If the two values do not have the same {@link Context}, the result will contain the {@link Context} with the larger precision.</p> + * + * @param x the value to multiply + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#multiply(BigDecimal, MathContext) + */ + public BigFloat multiply(BigFloat x) { + if (x.isSpecial()) + return x.multiply(this); + Context c = max(context, x.context); + return c.valueOf(value.multiply(x.value, c.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>this * x</code>. + * + * @param x the value to multiply + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#multiply(BigDecimal, MathContext) + */ + public BigFloat multiply(BigDecimal x) { + return multiply(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this * x</code>. + * + * @param x the value to multiply + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#multiply(BigDecimal, MathContext) + */ + public BigFloat multiply(int x) { + return multiply(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this * x</code>. + * + * @param x the value to multiply + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#multiply(BigDecimal, MathContext) + */ + public BigFloat multiply(long x) { + return multiply(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this * x</code>. + * + * @param x the value to multiply + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#multiply(BigDecimal, MathContext) + */ + public BigFloat multiply(double x) { + return multiply(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this / x</code>. + * + * <p>If the two values do not have the same {@link Context}, + * the result will contain the {@link Context} with the larger precision.</p> + * + * @param x the value to divide with + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#divide(BigDecimal, MathContext) + */ + public BigFloat divide(BigFloat x) { + if (x.isSpecial()) { + if (x == NaN) { + return NaN; + } else { + return context.valueOf(0); + } + } + if (this.isZero() && !x.isZero()) { + return context.valueOf(0); + } + if (x.isZero()) { + if (this.isZero()) { + return NaN; // 0 or -0 / 0 = NaN + } else if (this.isNegative()) { + return NEGATIVE_INFINITY;// -N / 0 = -INF + } else { + return POSITIVE_INFINITY;// N / 0 = +INF + } + } + + Context c = max(context, x.context); + return c.valueOf(value.divide(x.value, c.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>this / x</code>. + * + * @param x the value to divide with + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#divide(BigDecimal, MathContext) + */ + public BigFloat divide(BigDecimal x) { + return divide(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this / x</code>. + * + * @param x the value to divide with + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#divide(BigDecimal, MathContext) + */ + public BigFloat divide(int x) { + return divide(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this / x</code>. + * + * @param x the value to divide with + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#divide(BigDecimal, MathContext) + */ + public BigFloat divide(long x) { + return divide(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this / x</code>. + * + * @param x the value to divide with + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#divide(BigDecimal, MathContext) + */ + public BigFloat divide(double x) { + return divide(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is the remainder when dividing <code>this</code> by <code>x</code>. + * + * <p>If the two values do not have the same {@link Context}, the result will contain the {@link Context} with the larger precision.</p> + * + * @param x the value to divide with + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#remainder(BigDecimal, MathContext) + */ + public BigFloat remainder(BigFloat x) { + if (x.isSpecial()) { + if (x == NaN) { + return NaN; + } else { + return this; + } + } + if (this.isZero() && !x.isZero()) { + return context.valueOf(0); + } + if (x.isZero()) { + return NaN; + } + + Context c = max(context, x.context); + return c.valueOf(value.remainder(x.value, c.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is the remainder when dividing <code>this</code> by <code>x</code>. + * + * @param x the value to divide with + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#remainder(BigDecimal, MathContext) + */ + public BigFloat remainder(BigDecimal x) { + return remainder(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is the remainder when dividing <code>this</code> by <code>x</code>. + * + * @param x the value to divide with + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#remainder(BigDecimal, MathContext) + */ + public BigFloat remainder(int x) { + return remainder(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is the remainder when dividing <code>this</code> by <code>x</code>. + * + * @param x the value to divide with + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#remainder(BigDecimal, MathContext) + */ + public BigFloat remainder(long x) { + return remainder(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is the remainder when dividing <code>this</code> by <code>x</code>. + * + * @param x the value to divide with + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#remainder(BigDecimal, MathContext) + */ + public BigFloat remainder(double x) { + return remainder(context.valueOf(x)); + } + + /** + * Returns the {@link BigFloat} that is <code>this</code> to the power of <code>y</code>. + * + * <p>If the two values do not have the same {@link Context}, the result will contain the {@link Context} with the larger precision.</p> + * + * @param y the value of the power + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#pow(BigDecimal, BigDecimal, MathContext) + */ + public BigFloat pow(BigFloat y) { + if (y.isSpecial()) { + if (this.isZero()) { + if (y == POSITIVE_INFINITY) { + return this; + } + if (y == NEGATIVE_INFINITY) { + return POSITIVE_INFINITY; + } + } + if (y == NEGATIVE_INFINITY) { + return context.ZERO; + } + return y; + } + if (this.isZero()) { + if (y.isNegative()) { + return POSITIVE_INFINITY; + } + } + + Context c = max(context, y.context); + return c.valueOf(BigDecimalMath.pow(this.value, y.value, c.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>this</code> to the power of <code>y</code>. + * + * @param y the value of the power + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#pow(BigDecimal, BigDecimal, MathContext) + */ + public BigFloat pow(BigDecimal y) { + return pow(context.valueOf(y)); + } + + /** + * Returns the {@link BigFloat} that is <code>this</code> to the power of <code>y</code>. + * + * @param y the value of the power + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#pow(BigDecimal, BigDecimal, MathContext) + */ + public BigFloat pow(int y) { + return pow(context.valueOf(y)); + } + + /** + * Returns the {@link BigFloat} that is <code>this</code> to the power of <code>y</code>. + * + * @param y the value of the power + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#pow(BigDecimal, BigDecimal, MathContext) + */ + public BigFloat pow(long y) { + return pow(context.valueOf(y)); + } + + /** + * Returns the {@link BigFloat} that is <code>this</code> to the power of <code>y</code>. + * + * @param y the value of the power + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#pow(BigDecimal, BigDecimal, MathContext) + */ + public BigFloat pow(double y) { + return pow(context.valueOf(y)); + } + + /** + * Returns the {@link BigFloat} that is the <code>y</code>th root of <code>this</code>. + * + * <p>If the two values do not have the same {@link Context}, the result will contain the {@link Context} with the larger precision.</p> + * + * @param y the value of the root + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#root(BigDecimal, BigDecimal, MathContext) + */ + public BigFloat root(BigFloat y) { + if (y.isSpecial()) + return y; + Context c = max(context, y.context); + return c.valueOf(BigDecimalMath.root(this.value, y.value, c.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is the <code>y</code>th root of <code>this</code>. + * + * @param y the value of the root + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#root(BigDecimal, BigDecimal, MathContext) + */ + public BigFloat root(BigDecimal y) { + return root(context.valueOf(y)); + } + + /** + * Returns the {@link BigFloat} that is the <code>y</code>th root of <code>this</code>. + * + * @param y the value of the root + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#root(BigDecimal, BigDecimal, MathContext) + */ + public BigFloat root(int y) { + return root(context.valueOf(y)); + } + + /** + * Returns the {@link BigFloat} that is the <code>y</code>th root of <code>this</code>. + * + * @param y the value of the root + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#root(BigDecimal, BigDecimal, MathContext) + */ + public BigFloat root(long y) { + return root(context.valueOf(y)); + } + + /** + * Returns the {@link BigFloat} that is the <code>y</code>th root of <code>this</code>. + * + * @param y the value of the root + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#root(BigDecimal, BigDecimal, MathContext) + */ + public BigFloat root(double y) { + return root(context.valueOf(y)); + } + + @Override + public int hashCode() { + return value.stripTrailingZeros().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BigFloat other = (BigFloat) obj; + + return value.compareTo(other.value) == 0; + //return Objects.equals(value, other.value) && Objects.equals(context, other.context); + } + + /** + * Returns the signum function of this {@link BigFloat}. + * + * @return -1, 0, or 1 as the value of this {@link BigDecimal} is negative, zero, or positive. + */ + public int signum() { + return value.signum(); + } + + /** + * Returns whether this {@link BigFloat} is negative. + * + * @return <code>true</code> if negative, <code>false</code> if 0 or positive + */ + public boolean isNegative() { + return value.signum() < 0; + } + + /** + * Returns whether this {@link BigFloat} is 0. + * + * @return <code>true</code> if 0, <code>false</code> if negative or positive + */ + public boolean isZero() { + return value.signum() == 0; + } + + /** + * Returns whether this {@link BigFloat} is positive. + * + * @return <code>true</code> if positive, <code>false</code> if 0 or negative + */ + public boolean isPositive() { + return value.signum() > 0; + } + + @Override + public int compareTo(BigFloat other) { + if (other.isSpecial()) { + return -other.compareTo(this); + } + return value.compareTo(other.value); + } + + /** + * Returns whether <code>this</code> value is mathematically equal to the <code>other</code> value. + * + * @param other the other {@link BigFloat} to compare with + * + * @return <code>true</code> if both values are mathematically equal (equivalent to <code>this.compareTo(other) == 0</code> + * + * @see #compareTo(BigFloat) + */ + public boolean isEqual(BigFloat other) { + if (this == NaN || other == NaN) { + return false; + } + + return compareTo(other) == 0; + } + + /** + * Returns whether <code>this</code> value is mathematically less than to the <code>other</code> value. + * + * @param other the other {@link BigFloat} to compare with + * + * @return <code>true</code> <code>this</code> value is mathematically less than to the <code>other</code> value (equivalent to <code>this.compareTo(other) < 0</code> + * + * @see #compareTo(BigFloat) + */ + public boolean isLessThan(BigFloat other) { + if (this == NaN || other == NaN) { + return false; + } + + return compareTo(other) < 0; + } + + /** + * Returns whether <code>this</code> value is mathematically greater than to the <code>other</code> value. + * + * @param other the other {@link BigFloat} to compare with + * + * @return <code>true</code> <code>this</code> value is mathematically greater than to the <code>other</code> value (equivalent to <code>this.compareTo(other) > 0</code> + * + * @see #compareTo(BigFloat) + */ + public boolean isGreaterThan(BigFloat other) { + if (this == NaN || other == NaN) { + return false; + } + + return compareTo(other) > 0; + } + + /** + * Returns whether <code>this</code> value is mathematically less than or equal to the <code>other</code> value. + * + * @param other the other {@link BigFloat} to compare with + * + * @return <code>true</code> <code>this</code> value is mathematically less than or equal to the <code>other</code> value (equivalent to <code>this.compareTo(other) <= 0</code> + * + * @see #compareTo(BigFloat) + * @see #isLessThan(BigFloat) + * @see #isEqual(BigFloat) + */ + public boolean isLessThanOrEqual(BigFloat other) { + if (this == NaN || other == NaN) { + return false; + } + + return compareTo(other) <= 0; + } + + /** + * Returns whether <code>this</code> value is mathematically greater than or equal to the <code>other</code> value. + * + * @param other the other {@link BigFloat} to compare with + * + * @return <code>true</code> <code>this</code> value is mathematically greater than or equal to the <code>other</code> value (equivalent to <code>this.compareTo(other) >= 0</code> + * + * @see #compareTo(BigFloat) + * @see #isGreaterThan(BigFloat) + * @see #isEqual(BigFloat) + */ + public boolean isGreaterThanOrEqual(BigFloat other) { + if (this == NaN || other == NaN) { + return false; + } + + return compareTo(other) >= 0; + } + + /** + * Returns whether <code>this</code> value can be represented as <code>int</code>. + * + * @return <code>true</code> if the value can be represented as <code>int</code> value + * + * @see BigDecimalMath#isIntValue(BigDecimal) + */ + public boolean isIntValue() { + return BigDecimalMath.isIntValue(value); + } + + /** + * Returns whether <code>this</code> specified {@link BigDecimal} value can be represented as <code>double</code>. + * + * @return <code>true</code> if the value can be represented as <code>double</code> value + * + * @see BigDecimalMath#isDoubleValue(BigDecimal) + */ + public boolean isDoubleValue() { + return BigDecimalMath.isDoubleValue(value); + } + + /** + * Returns the mantissa of <code>this</code> value written as <em>mantissa * 10<sup>exponent</sup></em>. + * + * <p>The mantissa is defined as having exactly 1 digit before the decimal point.</p> + * + * @return the mantissa + * + * @see #getExponent() + * @see BigDecimalMath#mantissa(BigDecimal) + */ + public BigFloat getMantissa() { + return context.valueOf(BigDecimalMath.mantissa(value)); + } + + /** + * Returns the exponent of <code>this</code> value written as <em>mantissa * 10<sup>exponent</sup></em>. + * + * <p>The mantissa is defined as having exactly 1 digit before the decimal point.</p> + * + * @return the exponent + * + * @see #getMantissa() + * @see BigDecimalMath#exponent(BigDecimal) + */ + public BigFloat getExponent() { + return context.valueOf(BigDecimalMath.exponent(value)); + } + + /** + * Returns the integral part of <code>this</code> value (left of the decimal point). + * + * @return the integral part + * + * @see #getFractionalPart() + * @see BigDecimalMath#fractionalPart(BigDecimal) + */ + public BigFloat getIntegralPart() { + return context.valueOf(BigDecimalMath.integralPart(value)); + } + + /** + * Returns the fractional part of <code>this</code> value (right of the decimal point). + * + * @return the fractional part + * + * @see #getIntegralPart() + * @see BigDecimalMath#fractionalPart(BigDecimal) + */ + public BigFloat getFractionalPart() { + return context.valueOf(BigDecimalMath.fractionalPart(value)); + } + + /** + * Returns the {@link Context} of <code>this</code> value. + * + * @return the {@link Context} + */ + public Context getContext() { + return context; + } + + /** + * Returns <code>this</code> value as a {@link BigDecimal} value. + * + * @return the {@link BigDecimal} value + */ + public BigDecimal toBigDecimal() { + return value; + } + + /** + * Returns <code>this</code> value as a <code>double</code> value. + * + * @return the <code>double</code> value + * + * @see BigDecimal#doubleValue() + */ + public double toDouble() { + return value.doubleValue(); + } + + /** + * Returns <code>this</code> value as a <code>long</code> value. + * + * @return the <code>long</code> value + * + * @see BigDecimal#longValue() + */ + public long toLong() { + return value.longValue(); + } + + /** + * Returns <code>this</code> value as a <code>int</code> value. + * + * @return the <code>int</code> value + * + * @see BigDecimal#intValue() + */ + public int toInt() { + return value.intValue(); + } + + @Override + public String toString() { + return value.toString(); + } + + protected boolean isSpecial() { + return false; + } + + /** + * return special type of a value + * @return {@link SpecialBigFloat.Type} + */ + protected SpecialBigFloat.Type type() { + return SpecialBigFloat.Type.NORMAL; + } + + public boolean isNaN() { + return this == NaN; + } + + public boolean isInfinity() { + return this == POSITIVE_INFINITY || this == NEGATIVE_INFINITY; + } + + /** + * this class handle unrepresentable value in floating-point arithmetic + * + * @author Wireless4024 + */ + private static final class SpecialBigFloat extends BigFloat { + + private static final Context DUMMY_CONTEXT = BigFloat.context(MathContext.DECIMAL32); + + private final Type type; + + private SpecialBigFloat(Type type) { + super(null, DUMMY_CONTEXT); + this.type = type; + } + + @Override + protected boolean isSpecial() { + return true; + } + + @Override + protected Type type() { + return type; + } + + @Override + public BigFloat add(BigFloat x) { + if (!x.isSpecial()) { + return this; + } + if (this == POSITIVE_INFINITY && x == POSITIVE_INFINITY) { + return POSITIVE_INFINITY; + } + if (this == NEGATIVE_INFINITY && x == NEGATIVE_INFINITY) { + return NEGATIVE_INFINITY; + } + return NaN; + } + + @Override + public BigFloat subtract(BigFloat x) { + if (!x.isSpecial()) { + return this; + } + if (this == POSITIVE_INFINITY && x == NEGATIVE_INFINITY) { + return POSITIVE_INFINITY; + } + if (this == NEGATIVE_INFINITY && x == POSITIVE_INFINITY) { + return NEGATIVE_INFINITY; + } + return NaN; + } + + @Override + public BigFloat subtract(BigDecimal x) { + return this; + } + + @Override + public BigFloat multiply(BigFloat x) { + if (x.isZero() || x == NaN) { + return NaN; + } else if (x.isNegative()) { + return negate(this); + } else { + return this; + } + } + + @Override + public BigFloat divide(BigFloat x) { + if (x == NaN || (this.isInfinity() && x.isInfinity())) { + return NaN; + } else if (x.isNegative()) { + return negate(this); + } else { + return this; + } + } + + @Override + public BigFloat remainder(BigFloat x) { + return NaN; + } + + @Override + public BigFloat pow(BigFloat y) { + if (y.isZero()) { + return y.context.ONE; + } + if (y == NaN) { + return NaN; + } + if (this.isInfinity() && y.isNegative()) { + return y.context.ZERO; + } + if (this == NEGATIVE_INFINITY && y.isPositive()) { + return POSITIVE_INFINITY; + } + return this; + } + + @Override + public BigFloat root(BigFloat y) { + return this; + } + + @Override + public int hashCode() { + return type.hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + return obj instanceof BigFloat && ((BigFloat) obj).isSpecial() && ((BigFloat) obj).type() == this.type; + } + + @Override + public int signum() { + return type == Type.POSITIVE_INFINITY ? 1 : -1; + } + + @Override + public boolean isNegative() { + return signum() < 0; + } + + @Override + public boolean isZero() { + return false;//nan or infinity is not a zero + } + + @Override + public boolean isPositive() { + return signum() > 0; + } + + @Override + public int compareTo(BigFloat other) { + return Type.compare(type, other.type()); + } + + @Override + public boolean isIntValue() { + return false; + } + + @Override + public boolean isDoubleValue() { + return false; + } + + @Override + public BigFloat getMantissa() { + return this; + } + + @Override + public BigFloat getExponent() { + return this; + } + + @Override + public BigFloat getIntegralPart() { + return this; + } + + @Override + public BigFloat getFractionalPart() { + return this; + } + + @Override + public Context getContext() { + throw new UnsupportedOperationException(type + " has no context"); + } + + @Override + public BigDecimal toBigDecimal() { + throw new UnsupportedOperationException(type + " has no corresponding BigDecimal representation"); + } + + @Override + public double toDouble() { + return type.toDouble(); + } + + @Override + public long toLong() { + return (long) toDouble(); + } + + @Override + public int toInt() { + return (int) toDouble(); + } + + @Override + public String toString() { + return type.toString(); + } + + //optional static + enum Type { + NaN(Objects.hashCode(Double.NaN)), + POSITIVE_INFINITY(Objects.hashCode(Double.POSITIVE_INFINITY)), + NORMAL(Objects.hashCode(0)), + NEGATIVE_INFINITY(Objects.hashCode(Double.NEGATIVE_INFINITY)); + + final int hashCode; + + Type(int hashCode){ + this.hashCode=hashCode; + } + + public static int compare(Type a, Type b) { + //we can use double to compare + //if (a == NaN && b == NaN) + // return 0;//cuz NaN equals nothing even itself + return Double.compare(a.toDouble(),b.toDouble()); + } + + /** + * convert type to double + * @return double value that equivalent to {@link Type} + */ + public double toDouble() { + switch (this) { + case POSITIVE_INFINITY: + return Double.POSITIVE_INFINITY; + case NEGATIVE_INFINITY: + return Double.NEGATIVE_INFINITY; + case NaN: + return Double.NaN; + default: + return 0; + } + } + } + } + + /** + * Manages the {@link MathContext} and provides factory methods for {@link BigFloat} values. + */ + public static class Context implements Serializable{ + private static final long serialVersionUID = -5787473786808803161L; + public final BigFloat NEGATIVE_ONE; + public final BigFloat ZERO; + public final BigFloat ONE; + + private final MathContext mathContext; + + private Context(MathContext mathContext) { + this.mathContext = mathContext; + NEGATIVE_ONE = this.valueOf(-1); + ZERO = this.valueOf(0); + ONE = this.valueOf(1); + } + + /** + * Returns the {@link MathContext} of this context. + * + * @return the {@link MathContext} + */ + public MathContext getMathContext() { + return mathContext; + } + + /** + * Returns the precision of this context. + * <p> + * This is equivalent to calling <code>getMathContext().getPrecision()</code>. + * + * @return the precision + */ + public int getPrecision() { + return mathContext.getPrecision(); + } + + /** + * Returns the {@link RoundingMode} of this context. + * <p> + * This is equivalent to calling <code>getMathContext().getRoundingMode()</code>. + * + * @return the {@link RoundingMode} + */ + public RoundingMode getRoundingMode() { + return mathContext.getRoundingMode(); + } + + /** + * Creates a {@link BigFloat} value with this context. + * + * @param value the source {@link BigFloat} value + * + * @return the {@link BigFloat} value with this context (rounded to the precision of this context) + */ + public BigFloat valueOf(BigFloat value) { + return value.isSpecial() ? value : new BigFloat(value.value.round(mathContext), this);//they are final + } + + /** + * Creates a {@link BigFloat} value with this context. + * + * @param value the source {@link BigDecimal} value + * + * @return the {@link BigFloat} value with this context (rounded to the precision of this context) + */ + public BigFloat valueOf(BigDecimal value) { + return new BigFloat(value.round(mathContext), this); + } + + /** + * Creates a {@link BigFloat} value with this context. + * + * @param value the source int value + * + * @return the {@link BigFloat} value with this context (rounded to the precision of this context) + */ + public BigFloat valueOf(int value) { + return new BigFloat(new BigDecimal(value, mathContext), this); + } + + /** + * parse unsigned value with this logic <pre><code>value & 4294967295</code></pre> + * @param value an int value + * @param unsigned if true value will parse as unsigned integer + * @return the {@link BigFloat} value with this context (rounded to the precision of this context) + */ + public BigFloat valueOf(int value, boolean unsigned) { + if (!unsigned) { + return new BigFloat(new BigDecimal(value, mathContext), this); + } else { + if (value > -1) + return valueOf(value, false); + return new BigFloat(new BigDecimal(Integer.MAX_VALUE) + .add(new BigDecimal(value & Integer.MAX_VALUE)) + .add(BigDecimal.ONE), this); + } + } + + /** + * Creates a {@link BigFloat} value with this context. + * + * @param value the source long value + * + * @return the {@link BigFloat} value with this context (rounded to the precision of this context) + */ + public BigFloat valueOf(long value) { + return new BigFloat(new BigDecimal(value, mathContext), this); + } + + /** + * parse unsigned value with this logic <pre><code>value & 18446744073709551615</code></pre> + * @param value an int value + * @param unsigned if true value will parse as unsigned integer + * @return the {@link BigFloat} value with this context (rounded to the precision of this context) + */ + public BigFloat valueOf(long value, boolean unsigned) { + if (!unsigned) { + return new BigFloat(new BigDecimal(value, mathContext), this); + } else { + if (value > -1) + return valueOf(value, false); + return new BigFloat(new BigDecimal(Long.MAX_VALUE) + .add(new BigDecimal(value & Long.MAX_VALUE)) + .add(BigDecimal.ONE), this); + } + } + + /** + * Creates a {@link BigFloat} value with this context. + * + * @param value the source double value + * + * @return the {@link BigFloat} value with this context (rounded to the precision of this context) + */ + public BigFloat valueOf(double value) { + if (Double.isInfinite(value)) + return value == Double.POSITIVE_INFINITY ? POSITIVE_INFINITY : NEGATIVE_INFINITY; + else if (Double.isNaN(value)) + return NaN; + return new BigFloat(new BigDecimal(String.valueOf(value), mathContext), this); + } + + /** + * Creates a {@link BigFloat} value with this context. + * + * @param value the source String value + * + * @return the {@link BigFloat} value with this context (rounded to the precision of this context) + * + * @throws NumberFormatException if the value is not a valid number. + */ + public BigFloat valueOf(String value) { + return new BigFloat(new BigDecimal(value, mathContext), this); + } + + /** + * Returns the constant pi with this context. + * + * @return pi with this context (rounded to the precision of this context) + * + * @see BigDecimalMath#pi(MathContext) + */ + public BigFloat pi() { + return valueOf(BigDecimalMath.pi(mathContext)); + } + + /** + * Returns the constant e with this context. + * + * @return e with this context (rounded to the precision of this context) + * + * @see BigDecimalMath#e(MathContext) + */ + public BigFloat e() { + return valueOf(BigDecimalMath.e(mathContext)); + } + + /** + * Returns the factorial of n with this context. + * + * @param n the value to calculate + * + * @return the factorial of n with this context (rounded to the precision of this context) + * + * @see BigDecimalMath#factorial(int) + */ + public BigFloat factorial(int n) { + return valueOf(BigDecimalMath.factorial(n)); + } + + @Override + public int hashCode() { + return mathContext.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Context other = (Context) obj; + return mathContext.equals(other.mathContext); + } + + @Override + public String toString() { + return mathContext.toString(); + } + } + /** + * Returns the {@link BigFloat} that is <code>- this</code>. + * + * @param x the value to negate + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#negate(MathContext) + */ + public static BigFloat negate(BigFloat x) { + if (x.isSpecial()) + if (x.isInfinity()) + return x == POSITIVE_INFINITY ? NEGATIVE_INFINITY : POSITIVE_INFINITY; + else + return NaN; + return x.context.valueOf(x.value.negate()); + } + + /** + * Returns the {@link BigFloat} that is the <code>abs(this)</code> (absolute value). + * + * @param x the value to make absolute + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimal#abs(MathContext) + */ + public static BigFloat abs(BigFloat x) { + if (x.isSpecial()) + return x.isInfinity() ? POSITIVE_INFINITY : NaN; + return x.context.valueOf(x.value.abs()); + } + + /** + * Returns the the maximum of two {@link BigFloat} values. + * + * @param value1 the first {@link BigFloat} value to compare + * @param value2 the second {@link BigFloat} value to compare + * + * @return the maximum {@link BigFloat} value + */ + public static BigFloat max(BigFloat value1, BigFloat value2) { + return value1.compareTo(value2) >= 0 ? value1 : value2; + } + + /** + * Returns the the maximum of n {@link BigFloat} values. + * + * @param value1 the first {@link BigFloat} value to compare + * @param values the other {@link BigFloat}s value to compare + * + * @return the maximum {@link BigFloat} value + */ + public static BigFloat max(BigFloat value1, BigFloat... values) { + BigFloat result = value1; + + for (BigFloat other : values) { + result = max(result, other); + } + + return result; + } + + /** + * Returns the the minimum of two {@link BigFloat} values. + * + * @param value1 the first {@link BigFloat} value to compare + * @param value2 the second {@link BigFloat} value to compare + * + * @return the minimum {@link BigFloat} value + */ + public static BigFloat min(BigFloat value1, BigFloat value2) { + return value1.compareTo(value2) < 0 ? value1 : value2; + } + + /** + * Returns the the minimum of n {@link BigFloat} values. + * + * @param value1 the first {@link BigFloat} value to compare + * @param values the other {@link BigFloat}s value to compare + * + * @return the minimum {@link BigFloat} value + */ + public static BigFloat min(BigFloat value1, BigFloat... values) { + BigFloat result = value1; + + for (BigFloat other : values) { + result = min(result, other); + } + + return result; + } + + private static BigFloat logSpecial(BigFloat val){ + if (val.isNaN() || val.isNegative()) + return NaN; + if (val == POSITIVE_INFINITY) + return POSITIVE_INFINITY; + if (val.isZero()) + return NEGATIVE_INFINITY; + return null; + } + + /** + * Returns the {@link BigFloat} that is <code>log(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#log(BigDecimal, MathContext) + */ + public static BigFloat log(BigFloat x) { + BigFloat temp = logSpecial(x); + return temp != null ? temp : x.context.valueOf(BigDecimalMath.log(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>log2(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#log2(BigDecimal, MathContext) + */ + public static BigFloat log2(BigFloat x) { + BigFloat temp = logSpecial(x); + return temp != null ? temp : x.context.valueOf(BigDecimalMath.log2(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>log10(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#log10(BigDecimal, MathContext) + */ + public static BigFloat log10(BigFloat x) { + BigFloat temp = logSpecial(x); + return temp != null ? temp : x.context.valueOf(BigDecimalMath.log10(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>exp(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#exp(BigDecimal, MathContext) + */ + public static BigFloat exp(BigFloat x) { + if(x.isSpecial()) + return x != NEGATIVE_INFINITY ? x : x.context.ZERO; + return x.context.valueOf(BigDecimalMath.exp(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>sqrt(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#sqrt(BigDecimal, MathContext) + */ + public static BigFloat sqrt(BigFloat x) { + if (x.isNaN() || x.isNegative()) + return NaN; + if (x.isZero() || x.isInfinity()) + return x; + return x.context.valueOf(BigDecimalMath.sqrt(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>pow(x, y)</code>. + * + * <p>If the two values do not have the same {@link Context}, the result will contain the {@link Context} with the larger precision.</p> + * + * @param x the {@link BigFloat} value to take to the power + * @param y the {@link BigFloat} value to serve as exponent + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#pow(BigDecimal, BigDecimal, MathContext) + */ + public static BigFloat pow(BigFloat x, BigFloat y) { + Context c = max(x.context, y.context); + return c.valueOf(BigDecimalMath.pow(x.value, y.value, c.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>root(x, y)</code>. + * + * <p>If the two values do not have the same {@link Context}, the result will contain the {@link Context} with the larger precision.</p> + * + * @param x the {@link BigFloat} value to calculate the n'th root + * @param y the {@link BigFloat} defining the root + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#pow(BigDecimal, BigDecimal, MathContext) + */ + public static BigFloat root(BigFloat x, BigFloat y) { + Context c = max(x.context, y.context); + return c.valueOf(BigDecimalMath.root(x.value, y.value, c.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>sin(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#sin(BigDecimal, MathContext) + */ + public static BigFloat sin(BigFloat x) { + if(x.isSpecial()) + return NaN; + if(x.isZero()) + return x; + return x.context.valueOf(BigDecimalMath.sin(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>cos(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#cos(BigDecimal, MathContext) + */ + public static BigFloat cos(BigFloat x) { + if(x.isSpecial()) + return NaN; + return x.context.valueOf(BigDecimalMath.cos(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>tan(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#tan(BigDecimal, MathContext) + */ + public static BigFloat tan(BigFloat x) { + if(x.isSpecial()) + return NaN; + if(x.isZero()) + return x; + return x.context.valueOf(BigDecimalMath.tan(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>cot(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#cot(BigDecimal, MathContext) + */ + public static BigFloat cot(BigFloat x) { + if(x.isSpecial()) + return x; + if(x.isZero()) + return POSITIVE_INFINITY; + return x.context.valueOf(BigDecimalMath.cot(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>asin(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#asin(BigDecimal, MathContext) + */ + public static BigFloat asin(BigFloat x) { + if (x.isZero()) + return x; + return x.isNaN() || (!isRangeAbs1(x)) ? NaN : + x.context.valueOf(BigDecimalMath.asin(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>acos(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#acos(BigDecimal, MathContext) + */ + public static BigFloat acos(BigFloat x) { + return x.isNaN() || (!isRangeAbs1(x)) ? NaN : + x.context.valueOf(BigDecimalMath.acos(x.value, x.context.mathContext)); + } + + /** + * @param x a bigfloat + * @return if abs(x) <= 1 + */ + private static boolean isRangeAbs1(BigFloat x) { + return isBetween(x.context.NEGATIVE_ONE, x.context.ONE, x); + } + + /** + * Returns the {@link BigFloat} that is <code>atan(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#atan(BigDecimal, MathContext) + */ + public static BigFloat atan(BigFloat x) { + return x.isSpecial() || x.isZero() ? x : x.context.valueOf(BigDecimalMath.atan(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>acot(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#acot(BigDecimal, MathContext) + */ + public static BigFloat acot(BigFloat x) { + return x.isSpecial() ? x : x.context.valueOf(BigDecimalMath.acot(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>sinh(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#sinh(BigDecimal, MathContext) + */ + public static BigFloat sinh(BigFloat x) { + if (x.isSpecial() || x.isZero()) + return x; + return x.context.valueOf(BigDecimalMath.sinh(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>cosh(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#cosh(BigDecimal, MathContext) + */ + public static BigFloat cosh(BigFloat x) { + if (x.isNaN()) + return NaN; + if (x.isInfinity()) + return POSITIVE_INFINITY; + if (x.isZero()) + return x.context.ONE; + return x.context.valueOf(BigDecimalMath.cosh(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>tanh(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#tanh(BigDecimal, MathContext) + */ + public static BigFloat tanh(BigFloat x) { + if (x.isNaN() || x.isZero()) + return x; + if (x.isInfinity()) + return x == POSITIVE_INFINITY ? x.context.ONE : x.context.NEGATIVE_ONE; + return x.context.valueOf(BigDecimalMath.tanh(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>coth(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#coth(BigDecimal, MathContext) + */ + public static BigFloat coth(BigFloat x) { + if(x.isSpecial()) + return x; + return x.context.valueOf(BigDecimalMath.coth(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>asinh(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#asinh(BigDecimal, MathContext) + */ + public static BigFloat asinh(BigFloat x) { + if(x.isSpecial()) + return x; + return x.context.valueOf(BigDecimalMath.asinh(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>acosh(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#acosh(BigDecimal, MathContext) + */ + public static BigFloat acosh(BigFloat x) { + return x.context.valueOf(BigDecimalMath.acosh(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>atanh(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#atanh(BigDecimal, MathContext) + */ + public static BigFloat atanh(BigFloat x) { + if(x.isSpecial()) + return x; + return x.context.valueOf(BigDecimalMath.atanh(x.value, x.context.mathContext)); + } + + /** + * Returns the {@link BigFloat} that is <code>acoth(x)</code>. + * + * @param x the value + * + * @return the resulting {@link BigFloat} + * + * @see BigDecimalMath#acoth(BigDecimal, MathContext) + */ + public static BigFloat acoth(BigFloat x) { + if(x.isSpecial()) + return x; + return x.context.valueOf(BigDecimalMath.acoth(x.value, x.context.mathContext)); + } + + public static boolean isBetween(BigFloat min, BigFloat max, BigFloat value) { + return value.compareTo(min) >= 0 && value.compareTo(max) <= 0; + } + + private static Context max(Context left, Context right) { + return left.mathContext.getPrecision() > right.mathContext.getPrecision() ? left : right; + } +} 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 > 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); + } + +} diff --git a/src/main/java/ch/obermuhlner/math/big/DefaultBigDecimalMath.java b/src/main/java/ch/obermuhlner/math/big/DefaultBigDecimalMath.java new file mode 100644 index 0000000000..d6dca31ecf --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/DefaultBigDecimalMath.java @@ -0,0 +1,736 @@ +package ch.obermuhlner.math.big; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.*; + +/** + * A wrapper around {@link BigDecimalMath} that passes a current {@link MathContext} to the + * functions that need a {@link MathContext} argument. + * + * <p>The initial default {@link MathContext} is equivalent to {@link MathContext#DECIMAL128} + * but this can be overridden by setting the following system properties:</p> + * <ul> + * <li><code>ch.obermuhlner.math.big.default.precision</code> to a positive integer precision (default=34)</li> + * <li><code>ch.obermuhlner.math.big.default.rounding</code> to a {@link RoundingMode} name (default=HALF_UP) </li> + * </ul> + * + * <p>It is also possible to programmatically set the default {@link MathContext} using {@link #setDefaultMathContext(MathContext)}. + * It is recommended to set the desired precision in the {@link MathContext} very early in the startup of the application and to not change it afterwards.</p> + * + * <p>Important: Avoid the pitfall of setting the precision temporarily using {@link #setDefaultMathContext(MathContext)} for a calculation. + * This can lead to race conditions and calculations with the wrong precision + * if other threads in your application do the same thing.</p> + * + * <p>To set a temporary {@link MathContext} you have to choice to use either: + * <ul> + * <li><code>DefaultBigDecimalMath.createLocalMathContext()</code> in a try-with-resources statement</li> + * <li><code>DefaultBigDecimalMath.withLocalMathContext()</code> with a lambda function</li> + * </ul> + * + * Example code using <code>DefaultBigDecimalMath.createLocalMathContext()</code>: + * <pre> +System.out.println("Pi[default]: " + DefaultBigDecimalMath.pi()); +try (DefaultBigDecimalMath.LocalMathContext context = DefaultBigDecimalMath.createLocalMathContext(5)) { + System.out.println("Pi[5]: " + DefaultBigDecimalMath.pi()); + try (DefaultBigDecimalMath.LocalMathContext context2 = DefaultBigDecimalMath.createLocalMathContext(10)) { + System.out.println("Pi[10]: " + DefaultBigDecimalMath.pi()); + } + System.out.println("Pi[5]: " + DefaultBigDecimalMath.pi()); +} +System.out.println("Pi[default]: " + DefaultBigDecimalMath.pi()); + </pre> + * + * Example code using <code>DefaultBigDecimalMath.withLocalMathContext()</code>: + * <pre> +System.out.println("Pi[default]: " + DefaultBigDecimalMath.pi()); +DefaultBigDecimalMath.withPrecision(5, () -> { + System.out.println("Pi[5]: " + DefaultBigDecimalMath.pi()); + DefaultBigDecimalMath.withPrecision(10, () -> { + System.out.println("Pi[10]: " + DefaultBigDecimalMath.pi()); + }); + System.out.println("Pi[5]: " + DefaultBigDecimalMath.pi()); +}); +System.out.println("Pi[default]: " + DefaultBigDecimalMath.pi()); +</pre> + * + * Both snippets with give the following ouput: + * <pre> +Pi[default]: 3.141592653589793238462643383279503 +Pi[5]: 3.1416 +Pi[10]: 3.141592654 +Pi[5]: 3.1416 +Pi[default]: 3.141592653589793238462643383279503 +</pre> + * <p>The temporary {@link MathContext} are stored in {@link ThreadLocal} variables + * and will therefore not conflict with each other when used in multi-threaded use case.</p> + * + * <p>Important: Due to the {@link ThreadLocal} variables the local {@link MathContext} will + * <strong>not</strong> be available in other threads. + * This includes streams using <code>parallel()</code>, thread pools and manually started threads. + * If you need temporary {@link MathContext} for calculations then you <strong>must</strong> + * set the local {@link MathContext} inside <strong>every</strong> separate thread.</p> + * + * <pre> +try (DefaultBigDecimalMath.LocalMathContext context = DefaultBigDecimalMath.createLocalMathContext(5)) { + BigDecimalStream.range(0.0, 1.0, 0.01, DefaultBigDecimalMath.currentMathContext()) + .map(b -> DefaultBigDecimalMath.cos(b)) + .map(b -> "sequential " + Thread.currentThread().getName() + " [5]: " + b) + .forEach(System.out::println); + + BigDecimalStream.range(0.0, 1.0, 0.01, DefaultBigDecimalMath.currentMathContext()) + .parallel() + .map(b -> { + try (DefaultBigDecimalMath.LocalMathContext context2 = DefaultBigDecimalMath.createLocalMathContext(5)) { + return DefaultBigDecimalMath.cos(b); + } + }) + .map(b -> "parallel " + Thread.currentThread().getName() + " [5]: " + b) + .forEach(System.out::println); +} +</pre> + */ +public class DefaultBigDecimalMath { + + private static MathContext defaultMathContext = createDefaultMathContext(); + private static ThreadLocal<Deque<MathContext>> mathContextStack = new ThreadLocal<>(); + + private static MathContext createDefaultMathContext () { + int precision = getIntSystemProperty("ch.obermuhlner.math.big.default.precision", MathContext.DECIMAL128.getPrecision()); + RoundingMode rounding = getRoundingModeSystemProperty("ch.obermuhlner.math.big.default.rounding", MathContext.DECIMAL128.getRoundingMode()); + + return new MathContext(precision, rounding); + } + + private static void pushMathContext(MathContext mathContext) { + Deque<MathContext> mathContexts = mathContextStack.get(); + if (mathContexts == null) { + mathContexts = new ArrayDeque<>(); + mathContextStack.set(mathContexts); + }; + mathContexts.addLast(mathContext); + } + + private static MathContext popMathContext() { + Deque<MathContext> mathContexts = mathContextStack.get(); + MathContext poppedMathContext = mathContexts.removeLast(); + if (mathContexts.isEmpty()) { + mathContextStack.remove(); + } + return poppedMathContext; + } + + private static int getIntSystemProperty(String propertyKey, int defaultValue) { + String propertyValue = System.getProperty(propertyKey, Integer.toString(defaultValue)); + try { + return Integer.parseInt(propertyValue); + } catch(NumberFormatException ex) { + return propertyException(propertyKey,propertyValue,defaultValue); + } + } + + private static RoundingMode getRoundingModeSystemProperty(String propertyKey, RoundingMode defaultValue) { + String propertyValue = System.getProperty(propertyKey, defaultValue.name()); + try { + return RoundingMode.valueOf(propertyValue); + } catch(IllegalArgumentException ex) { + return propertyException(propertyKey,propertyValue,defaultValue); + } + } + + private static <T> T propertyException(String propertyKey,String propertyValue,T defaultValue){ + System.err.println("Property '" + propertyKey + "' is not valid: " + propertyValue + " (using " + defaultValue + " instead)"); + return defaultValue; + } + + /** + * Sets the default {@link MathContext} used if no other {@link MathContext} is defined using {@link #withLocalMathContext(MathContext, Runnable)}. + * + * @param defaultMathContext the default {@link MathContext} + * @see #currentMathContext() + * @see #withLocalMathContext(int, Runnable) + * @see #withLocalMathContext(int, RoundingMode, Runnable) + * @see #withLocalMathContext(MathContext, Runnable) + */ + public static void setDefaultMathContext(MathContext defaultMathContext) { + Objects.requireNonNull(defaultMathContext); + DefaultBigDecimalMath.defaultMathContext = defaultMathContext; + } + + /** + * Returns the default {@link MathContext} used for all mathematical functions in this class. + * + * @return the default {@link MathContext} + */ + public static MathContext getDefaultMathContext() { + return defaultMathContext; + } + + /** + * Executes the given {@link Runnable} using the specified precision. + * + * @param precision the precision to use for calculations in the <code>runnable</code> + * @param runnable the {@link Runnable} to execute + */ + public static void withLocalMathContext(int precision, Runnable runnable) { + withLocalMathContext(new MathContext(precision), runnable); + } + + /** + * Executes the given {@link Runnable} using the specified precision and {@link RoundingMode}. + * + * @param precision the precision to use for calculations in the <code>runnable</code> + * @param roundingMode the {@link RoundingMode} to use for calculations in the <code>runnable</code> + * @param runnable the {@link Runnable} to execute + */ + public static void withLocalMathContext(int precision, RoundingMode roundingMode, Runnable runnable) { + withLocalMathContext(new MathContext(precision, roundingMode), runnable); + } + + /** + * Executes the given {@link Runnable} using the specified {@link MathContext}. + * + * @param mathContext the {@link MathContext} to use for calculations in the <code>runnable</code> + * @param runnable the {@link Runnable} to execute + */ + public static void withLocalMathContext(MathContext mathContext, Runnable runnable) { + try (LocalMathContext context = createLocalMathContext(mathContext)) { + runnable.run(); + } + } + + /** + * Executes the given {@link Runnable} using the specified precision. + * + * @param precision the precision to use for calculations + * @return the created {@link LocalMathContext} to be used in a try-with-resources statement + */ + public static LocalMathContext createLocalMathContext(int precision) { + return createLocalMathContext(new MathContext(precision)); + } + + /** + * Executes the given {@link Runnable} using the specified precision and {@link RoundingMode}. + * + * @param precision the precision to use for calculations + * @param roundingMode the {@link RoundingMode} to use for calculations in the <code>runnable</code> + * @return the created {@link LocalMathContext} to be used in a try-with-resources statement + */ + public static LocalMathContext createLocalMathContext(int precision, RoundingMode roundingMode) { + return createLocalMathContext(new MathContext(precision, roundingMode)); + } + + /** + * Executes the given {@link Runnable} using the specified {@link MathContext}. + * + * @param mathContext the {@link MathContext} to use for calculations + * @return the created {@link LocalMathContext} to be used in a try-with-resources statement + */ + public static LocalMathContext createLocalMathContext(MathContext mathContext) { + return new LocalMathContext(mathContext); + } + + /** + * Returns the current {@link MathContext} used for all mathematical functions in this class. + * + * <p>The current {@link MathContext} is the last {@link MathContext} specified + * using {@link #withLocalMathContext(MathContext, Runnable)} + * or the default {@link MathContext} if none was specified.</p> + * + * @return the current {@link MathContext} + * @see #currentMathContext() + * @see #withLocalMathContext(int, Runnable) + * @see #withLocalMathContext(int, RoundingMode, Runnable) + * @see #withLocalMathContext(MathContext, Runnable) + */ + public static MathContext currentMathContext() { + Deque<MathContext> mathContexts = mathContextStack.get(); + if (mathContexts == null || mathContexts.isEmpty()) { + return defaultMathContext; + } + + return mathContexts.getLast(); + } + + /** + * Rounds the specified {@link BigDecimal} to the precision of the current {@link MathContext}. + * + * @param value the {@link BigDecimal} to round + * @return the rounded {@link BigDecimal} value + * @see #currentMathContext() + * @see BigDecimalMath#round(BigDecimal, MathContext) + */ + public static BigDecimal round(BigDecimal value) { + return BigDecimalMath.round(value, defaultMathContext); + } + + /** + * Rounds the specified {@link BigDecimal} to the precision of the current {@link MathContext} including trailing zeroes. + * + * @param value the {@link BigDecimal} to round + * @return the rounded {@link BigDecimal} value including trailing zeroes + * @see #currentMathContext() + * @see BigDecimalMath#roundWithTrailingZeroes(BigDecimal, MathContext) + */ + public static BigDecimal roundWithTrailingZeroes(BigDecimal value) { + return BigDecimalMath.roundWithTrailingZeroes(value, currentMathContext()); + } + + /** + * Returns the {@link BigDecimal} that is <code>x + y</code> using the current {@link MathContext}. + * + * @param x the x value + * @param y the y value to add + * @return the resulting {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimal#add(BigDecimal, MathContext) + */ + public static BigDecimal add(BigDecimal x, BigDecimal y) { + return x.add(y, currentMathContext()); + } + + /** + * Returns the {@link BigDecimal} that is <code>x - y</code> using the current {@link MathContext}. + * + * @param x the x value + * @param y the y value to subtract + * @return the resulting {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimal#subtract(BigDecimal, MathContext) + */ + public static BigDecimal subtract(BigDecimal x, BigDecimal y) { + return x.subtract(y, currentMathContext()); + } + + /** + * Returns the {@link BigDecimal} that is <code>x * y</code> using the current {@link MathContext}. + * + * @param x the x value + * @param y the y value to multiply + * @return the resulting {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimal#multiply(BigDecimal, MathContext) + */ + public static BigDecimal multiply(BigDecimal x, BigDecimal y) { + return x.multiply(y, currentMathContext()); + } + + /** + * Returns the {@link BigDecimal} that is <code>x / y</code> using the current {@link MathContext}. + * + * @param x the x value + * @param y the y value to divide + * @return the resulting {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimal#divide(BigDecimal, MathContext) + */ + public static BigDecimal divide(BigDecimal x, BigDecimal y) { + return x.divide(y, currentMathContext()); + } + + /** + * Returns the {@link BigDecimal} that is <code>x % y</code> using the current {@link MathContext}. + * + * @param x the x value + * @param y the y value to divide + * @return the resulting {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimal#remainder(BigDecimal, MathContext) + */ + public static BigDecimal remainder(BigDecimal x, BigDecimal y) { + return x.remainder(y, currentMathContext()); + } + + /** + * Calculates the reciprocal of the specified {@link BigDecimal} using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} + * @return the reciprocal {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#reciprocal(BigDecimal, MathContext) + */ + public static BigDecimal reciprocal(BigDecimal x) { + return BigDecimalMath.reciprocal(x, currentMathContext()); + } + + /** + * Calculates the factorial of the specified {@link BigDecimal} using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} + * @return the factorial {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#factorial(BigDecimal, MathContext) + */ + public static BigDecimal factorial(BigDecimal x) { + return BigDecimalMath.factorial(x, currentMathContext()); + } + + /** + * Calculates the gamma function of the specified {@link BigDecimal} using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} + * @return the gamma {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#gamma(BigDecimal, MathContext) + */ + public static BigDecimal gamma(BigDecimal x) { + return BigDecimalMath.gamma(x, currentMathContext()); + } + + /** + * Calculates the Bernoulli number for the specified index using the current {@link MathContext}. + * + * @param n the index of the Bernoulli number to be calculated (starting at 0) + * @return the Bernoulli number for the specified index with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#bernoulli(int, MathContext) + */ + public static BigDecimal bernoulli(int n) { + return BigDecimalMath.bernoulli(n, currentMathContext()); + } + + /** + * Calculates {@link BigDecimal} x to the power of {@link BigDecimal} y (x<sup>y</sup>) using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} value to take to the power + * @param y the {@link BigDecimal} value to serve as exponent + * @return the calculated x to the power of y with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#pow(BigDecimal, BigDecimal, MathContext) + */ + public static BigDecimal pow(BigDecimal x, BigDecimal y) { + return BigDecimalMath.pow(x, y, currentMathContext()); + } + + /** + * Calculates {@link BigDecimal} x to the power of <code>long</code> y (x<sup>y</sup>) using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} value to take to the power + * @param y the <code>long</code> value to serve as exponent + * @return the calculated x to the power of y with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#pow(BigDecimal, long, MathContext) + */ + public static BigDecimal pow(BigDecimal x, long y) { + return BigDecimalMath.pow(x, y, currentMathContext()); + } + + /** + * Calculates the square root of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} value to calculate the square root + * @return the calculated square root of x with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#sqrt(BigDecimal, MathContext) + */ + public static BigDecimal sqrt(BigDecimal x) { + return BigDecimalMath.sqrt(x, currentMathContext()); + } + + /** + * Calculates the n'th root of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} value to calculate the n'th root + * @param n the {@link BigDecimal} defining the root + * + * @return the calculated n'th root of x with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#root(BigDecimal, BigDecimal, MathContext) + */ + public static BigDecimal root(BigDecimal x, BigDecimal n) { + return BigDecimalMath.root(x, n, currentMathContext()); + } + + /** + * Calculates the natural logarithm of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the natural logarithm for + * @return the calculated natural logarithm {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#log(BigDecimal, MathContext) + */ + public static BigDecimal log(BigDecimal x) { + return BigDecimalMath.log(x, currentMathContext()); + } + + /** + * Calculates the logarithm of {@link BigDecimal} x to the base 2 using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the logarithm base 2 for + * @return the calculated natural logarithm {@link BigDecimal} to the base 2 with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#log2(BigDecimal, MathContext) + */ + public static BigDecimal log2(BigDecimal x) { + return BigDecimalMath.log2(x, currentMathContext()); + } + + /** + * Calculates the logarithm of {@link BigDecimal} x to the base 10 using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the logarithm base 10 for + * @return the calculated natural logarithm {@link BigDecimal} to the base 10 with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#log10(BigDecimal, MathContext) + */ + public static BigDecimal log10(BigDecimal x) { + return BigDecimalMath.log10(x, currentMathContext()); + } + + /** + * Returns the number pi using the current {@link MathContext}. + * + * @return the number pi with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#pi(MathContext) + */ + public static BigDecimal pi() { + return BigDecimalMath.pi(currentMathContext()); + } + + /** + * Returns the number e using the current {@link MathContext}. + * + * @return the number e with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#e(MathContext) + */ + public static BigDecimal e() { + return BigDecimalMath.e(currentMathContext()); + } + + /** + * Calculates the natural exponent of {@link BigDecimal} x (e<sup>x</sup>) using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the exponent for + * @return the calculated exponent {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#exp(BigDecimal, MathContext) + */ + public static BigDecimal exp(BigDecimal x) { + return BigDecimalMath.exp(x, currentMathContext()); + } + + /** + * Calculates the sine (sinus) of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the sine for + * @return the calculated sine {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#sin(BigDecimal, MathContext) + */ + public static BigDecimal sin(BigDecimal x) { + return BigDecimalMath.sin(x, currentMathContext()); + } + + /** + * Calculates the arc sine (inverted sine) of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the arc sine for + * @return the calculated arc sine {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#asin(BigDecimal, MathContext) + */ + public static BigDecimal asin(BigDecimal x) { + return BigDecimalMath.asin(x, currentMathContext()); + } + + /** + * Calculates the cosine (cosinus) of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the cosine for + * @return the calculated cosine {@link BigDecimal} with the precision specified in the current {@link MathContext} + */ + public static BigDecimal cos(BigDecimal x) { + return BigDecimalMath.cos(x, currentMathContext()); + } + + /** + * Calculates the arc cosine (inverted cosine) of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the arc cosine for + * @return the calculated arc sine {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#acos(BigDecimal, MathContext) + */ + public static BigDecimal acos(BigDecimal x) { + return BigDecimalMath.acos(x, currentMathContext()); + } + + /** + * Calculates the tangens of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the tangens for + * @return the calculated tangens {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#tan(BigDecimal, MathContext) + */ + public static BigDecimal tan(BigDecimal x) { + return BigDecimalMath.tan(x, currentMathContext()); + } + + /** + * Calculates the arc tangens (inverted tangens) of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the arc tangens for + * @return the calculated arc tangens {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#atan(BigDecimal, MathContext) + */ + public static BigDecimal atan(BigDecimal x) { + return BigDecimalMath.atan(x, currentMathContext()); + } + + /** + * Calculates the arc tangens (inverted tangens) of {@link BigDecimal} y / x in the range -<i>pi</i> to <i>pi</i> using the current {@link MathContext}. + * + * @param y the {@link BigDecimal} + * @param x the {@link BigDecimal} + * @return the calculated arc tangens {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see #atan2(BigDecimal, BigDecimal) + */ + public static BigDecimal atan2(BigDecimal y, BigDecimal x) { + return BigDecimalMath.atan2(y, x, currentMathContext()); + } + + /** + * Calculates the cotangens of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the cotangens for + * @return the calculated cotanges {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#cot(BigDecimal, MathContext) + */ + public static BigDecimal cot(BigDecimal x) { + return BigDecimalMath.cot(x, currentMathContext()); + } + + /** + * Calculates the inverse cotangens (arc cotangens) of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the arc cotangens for + * @return the calculated arc cotangens {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#acot(BigDecimal, MathContext) + */ + public static BigDecimal acot(BigDecimal x) { + return BigDecimalMath.acot(x, currentMathContext()); + } + + /** + * Calculates the hyperbolic sine of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the hyperbolic sine for + * @return the calculated hyperbolic sine {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#sinh(BigDecimal, MathContext) + */ + public static BigDecimal sinh(BigDecimal x) { + return BigDecimalMath.sinh(x, currentMathContext()); + } + + /** + * Calculates the hyperbolic cosine of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the hyperbolic cosine for + * @return the calculated hyperbolic cosine {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#cosh(BigDecimal, MathContext) + */ + public static BigDecimal cosh(BigDecimal x) { + return BigDecimalMath.cosh(x, currentMathContext()); + } + + /** + * Calculates the hyperbolic tangens of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the hyperbolic tangens for + * @return the calculated hyperbolic tangens {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#tanh(BigDecimal, MathContext) + */ + public static BigDecimal tanh(BigDecimal x) { + return BigDecimalMath.tanh(x, currentMathContext()); + } + + /** + * Calculates the hyperbolic cotangens of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the hyperbolic cotangens for + * @return the calculated hyperbolic cotangens {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#coth(BigDecimal, MathContext) + */ + public static BigDecimal coth(BigDecimal x) { + return BigDecimalMath.coth(x, currentMathContext()); + } + + /** + * Calculates the arc hyperbolic sine (inverse hyperbolic sine) of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the arc hyperbolic sine for + * @return the calculated arc hyperbolic sine {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#asinh(BigDecimal, MathContext) + */ + public static BigDecimal asinh(BigDecimal x) { + return BigDecimalMath.asinh(x, currentMathContext()); + } + + /** + * Calculates the arc hyperbolic cosine (inverse hyperbolic cosine) of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the arc hyperbolic cosine for + * @return the calculated arc hyperbolic cosine {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#acosh(BigDecimal, MathContext) + */ + public static BigDecimal acosh(BigDecimal x) { + return BigDecimalMath.acosh(x, currentMathContext()); + } + + /** + * Calculates the arc hyperbolic tangens (inverse hyperbolic tangens) of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the arc hyperbolic tangens for + * @return the calculated arc hyperbolic tangens {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#atanh(BigDecimal, MathContext) + */ + public static BigDecimal atanh(BigDecimal x) { + return BigDecimalMath.atanh(x, currentMathContext()); + } + + /** + * Calculates the arc hyperbolic cotangens (inverse hyperbolic cotangens) of {@link BigDecimal} x using the current {@link MathContext}. + * + * @param x the {@link BigDecimal} to calculate the arc hyperbolic cotangens for + * @return the calculated arc hyperbolic cotangens {@link BigDecimal} with the precision specified in the current {@link MathContext} + * @see #currentMathContext() + * @see BigDecimalMath#acoth(BigDecimal, MathContext) + */ + public static BigDecimal acoth(BigDecimal x) { + return BigDecimalMath.acoth(x, currentMathContext()); + } + + /** + * The local context used to push and pop a {@link MathContext} on the stack. + * + * <p>The recommended way to use this class is to use the try-with-resources.</p> + */ + public static class LocalMathContext implements AutoCloseable { + public final MathContext mathContext; + + LocalMathContext(MathContext mathContext) { + this.mathContext = mathContext; + pushMathContext(mathContext); + } + + @Override + public void close() { + popMathContext(); + } + } +} diff --git a/src/main/java/ch/obermuhlner/math/big/internal/AsinCalculator.java b/src/main/java/ch/obermuhlner/math/big/internal/AsinCalculator.java new file mode 100644 index 0000000000..f8bcd4837b --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/AsinCalculator.java @@ -0,0 +1,46 @@ +package ch.obermuhlner.math.big.internal; + +import java.math.BigDecimal; +import java.math.MathContext; + +import ch.obermuhlner.math.big.BigRational; + +/** + * Calculates arc sinus using the Maclaurin series. + * + * <p>See <a href="https://de.wikipedia.org/wiki/Taylorreihe">Wikipedia: Taylorreihe</a></p> + * + * <p>No argument checking or optimizations are done. + * This implementation is <strong>not</strong> intended to be called directly.</p> + */ +public class AsinCalculator extends SeriesCalculator { + + public static final AsinCalculator INSTANCE = new AsinCalculator(); + + private int n = 0; + private BigRational factorial2n = BigRational.ONE; + private BigRational factorialN = BigRational.ONE; + private BigRational fourPowerN = BigRational.ONE; + + private AsinCalculator() { + } + + @Override + protected BigRational getCurrentFactor() { + BigRational factor = factorial2n.divide(fourPowerN.multiply(factorialN).multiply(factorialN).multiply(2 * n + 1)); + return factor; + } + + @Override + protected void calculateNextFactor() { + n++; + factorial2n = factorial2n.multiply(2 * n - 1).multiply(2 * n); + factorialN = factorialN.multiply(n); + fourPowerN = fourPowerN.multiply(4); + } + + @Override + protected PowerIterator createPowerIterator(BigDecimal x, MathContext mathContext) { + return new PowerTwoNPlusOneIterator(x, mathContext); + } +} diff --git a/src/main/java/ch/obermuhlner/math/big/internal/AtanhCalculator.java b/src/main/java/ch/obermuhlner/math/big/internal/AtanhCalculator.java new file mode 100644 index 0000000000..a2f844a094 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/AtanhCalculator.java @@ -0,0 +1,40 @@ +package ch.obermuhlner.math.big.internal; + +import ch.obermuhlner.math.big.BigRational; + +import java.math.BigDecimal; +import java.math.MathContext; + +/** + * Calculates sinus hyperbolicus using the Taylor series. + * + * <p>See <a href="https://en.wikipedia.org/wiki/Taylor_series">Wikipedia: Taylor series</a></p> + * + * <p>No argument checking or optimizations are done. + * This implementation is <strong>not</strong> intended to be called directly.</p> + */ +public class AtanhCalculator extends SeriesCalculator { + + public static final AtanhCalculator INSTANCE = new AtanhCalculator(); + + private int n = 0; + + private AtanhCalculator() { + super(true); + } + + @Override + protected BigRational getCurrentFactor() { + return BigRational.valueOf(1, 2 * n + 1); + } + + @Override + protected void calculateNextFactor() { + n++; + } + + @Override + protected PowerIterator createPowerIterator(BigDecimal x, MathContext mathContext) { + return new PowerTwoNPlusOneIterator(x, mathContext); + } +} diff --git a/src/main/java/ch/obermuhlner/math/big/internal/CosCalculator.java b/src/main/java/ch/obermuhlner/math/big/internal/CosCalculator.java new file mode 100644 index 0000000000..bf9edbd691 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/CosCalculator.java @@ -0,0 +1,48 @@ +package ch.obermuhlner.math.big.internal; + +import java.math.BigDecimal; +import java.math.MathContext; + +import ch.obermuhlner.math.big.BigRational; + +/** + * Calculates cosinus using the Maclaurin series. + * + * <p>See <a href="https://de.wikipedia.org/wiki/Taylorreihe">Wikipedia: Taylorreihe</a></p> + * + * <p>No argument checking or optimizations are done. + * This implementation is <strong>not</strong> intended to be called directly.</p> + */ +public class CosCalculator extends SeriesCalculator { + + public static final CosCalculator INSTANCE = new CosCalculator(); + + private int n = 0; + private boolean negative = false; + private BigRational factorial2n = BigRational.ONE; + + private CosCalculator() { + super(true); + } + + @Override + protected BigRational getCurrentFactor() { + BigRational factor = factorial2n.reciprocal(); + if (negative) { + factor = factor.negate(); + } + return factor; + } + + @Override + protected void calculateNextFactor() { + n++; + factorial2n = factorial2n.multiply(2 * n - 1).multiply(2 * n); + negative = !negative; + } + + @Override + protected PowerIterator createPowerIterator(BigDecimal x, MathContext mathContext) { + return new PowerTwoNIterator(x, mathContext); + } +} diff --git a/src/main/java/ch/obermuhlner/math/big/internal/CoshCalculator.java b/src/main/java/ch/obermuhlner/math/big/internal/CoshCalculator.java new file mode 100644 index 0000000000..f22631e8ae --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/CoshCalculator.java @@ -0,0 +1,43 @@ +package ch.obermuhlner.math.big.internal; + +import java.math.BigDecimal; +import java.math.MathContext; + +import ch.obermuhlner.math.big.BigRational; + +/** + * Calculates cosinus hyperbolicus using the Taylor series. + * + * <p>See <a href="https://en.wikipedia.org/wiki/Taylor_series">Wikipedia: Taylor series</a></p> + * + * <p>No argument checking or optimizations are done. + * This implementation is <strong>not</strong> intended to be called directly.</p> + */ +public class CoshCalculator extends SeriesCalculator { + + public static final CoshCalculator INSTANCE = new CoshCalculator(); + + private int n = 0; + + private BigRational factorial2n = BigRational.ONE; + + private CoshCalculator() { + super(true); + } + + @Override + protected BigRational getCurrentFactor() { + return factorial2n.reciprocal(); + } + + @Override + protected void calculateNextFactor() { + n++; + factorial2n = factorial2n.multiply(2 * n - 1).multiply(2 * n); + } + + @Override + protected PowerIterator createPowerIterator(BigDecimal x, MathContext mathContext) { + return new PowerTwoNIterator(x, mathContext); + } +} diff --git a/src/main/java/ch/obermuhlner/math/big/internal/ExpCalculator.java b/src/main/java/ch/obermuhlner/math/big/internal/ExpCalculator.java new file mode 100644 index 0000000000..16c6e6ac21 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/ExpCalculator.java @@ -0,0 +1,42 @@ +package ch.obermuhlner.math.big.internal; + +import java.math.BigDecimal; +import java.math.MathContext; + +import ch.obermuhlner.math.big.BigRational; + +/** + * Calculates exp using the Maclaurin series. + * + * <p>See <a href="https://de.wikipedia.org/wiki/Taylorreihe">Wikipedia: Taylorreihe</a></p> + * + * <p>No argument checking or optimizations are done. + * This implementation is <strong>not</strong> intended to be called directly.</p> + */ +public class ExpCalculator extends SeriesCalculator { + + public static final ExpCalculator INSTANCE = new ExpCalculator(); + + private int n = 0; + private BigRational oneOverFactorialOfN = BigRational.ONE; + + private ExpCalculator() { + // prevent instances + } + + @Override + protected BigRational getCurrentFactor() { + return oneOverFactorialOfN; + } + + @Override + protected void calculateNextFactor() { + n++; + oneOverFactorialOfN = oneOverFactorialOfN.divide(n); + } + + @Override + protected PowerIterator createPowerIterator(BigDecimal x, MathContext mathContext) { + return new PowerNIterator(x, mathContext); + } +} diff --git a/src/main/java/ch/obermuhlner/math/big/internal/PowerIterator.java b/src/main/java/ch/obermuhlner/math/big/internal/PowerIterator.java new file mode 100644 index 0000000000..def0f5f7ef --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/PowerIterator.java @@ -0,0 +1,28 @@ +package ch.obermuhlner.math.big.internal; + +import java.math.BigDecimal; + +/** + * Iterator over the powers of a value x. + * + * <p>This API allows to efficiently calculate the various powers of x in a taylor series by storing intermediate results.</p> + * <p>For example x<sup>n</sup> can be calculated using one multiplication by storing the previously calculated x<sup>n-1</sup> and x.</p> + * + * <p>{@link #getCurrentPower()} will be called first to retrieve the initial value.</p> + * + * For later iterations {@link #calculateNextPower()} will be called before {@link #getCurrentPower()}. + */ +public interface PowerIterator { + + /** + * Returns the current power. + * + * @return the current power. + */ + BigDecimal getCurrentPower(); + + /** + * Calculates the next power. + */ + void calculateNextPower(); +}
\ No newline at end of file diff --git a/src/main/java/ch/obermuhlner/math/big/internal/PowerNIterator.java b/src/main/java/ch/obermuhlner/math/big/internal/PowerNIterator.java new file mode 100644 index 0000000000..d6ef34e01e --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/PowerNIterator.java @@ -0,0 +1,33 @@ +package ch.obermuhlner.math.big.internal; + +import java.math.BigDecimal; +import java.math.MathContext; + +/** + * {@link PowerIterator} to calculate x<sup>n</sup>. + */ +public class PowerNIterator implements PowerIterator { + + private final BigDecimal x; + + private final MathContext mathContext; + + private BigDecimal powerOfX; + + public PowerNIterator(BigDecimal x, MathContext mathContext) { + this.x = x; + this.mathContext = mathContext; + + powerOfX = BigDecimal.ONE; + } + + @Override + public BigDecimal getCurrentPower() { + return powerOfX; + } + + @Override + public void calculateNextPower() { + powerOfX = powerOfX.multiply(x, mathContext); + } +}
\ No newline at end of file diff --git a/src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNIterator.java b/src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNIterator.java new file mode 100644 index 0000000000..839d617e16 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNIterator.java @@ -0,0 +1,33 @@ +package ch.obermuhlner.math.big.internal; + +import java.math.BigDecimal; +import java.math.MathContext; + +/** + * {@link PowerIterator} to calculate x<sup>2*n</sup>. + */ +public class PowerTwoNIterator implements PowerIterator { + + private final MathContext mathContext; + + private final BigDecimal xPowerTwo; + + private BigDecimal powerOfX; + + public PowerTwoNIterator(BigDecimal x, MathContext mathContext) { + this.mathContext = mathContext; + + xPowerTwo = x.multiply(x, mathContext); + powerOfX = BigDecimal.ONE; + } + + @Override + public BigDecimal getCurrentPower() { + return powerOfX; + } + + @Override + public void calculateNextPower() { + powerOfX = powerOfX.multiply(xPowerTwo, mathContext); + } +}
\ No newline at end of file diff --git a/src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNMinusOneIterator.java b/src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNMinusOneIterator.java new file mode 100644 index 0000000000..15ad0168c0 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNMinusOneIterator.java @@ -0,0 +1,35 @@ +package ch.obermuhlner.math.big.internal; + +import ch.obermuhlner.math.big.BigDecimalMath; + +import java.math.BigDecimal; +import java.math.MathContext; + +/** + * {@link PowerIterator} to calculate x<sup>2*n-1</sup>. + */ +public class PowerTwoNMinusOneIterator implements PowerIterator { + + private final MathContext mathContext; + + private final BigDecimal xPowerTwo; + + private BigDecimal powerOfX; + + public PowerTwoNMinusOneIterator(BigDecimal x, MathContext mathContext) { + this.mathContext = mathContext; + + xPowerTwo = x.multiply(x, mathContext); + powerOfX = BigDecimalMath.reciprocal(x, mathContext); + } + + @Override + public BigDecimal getCurrentPower() { + return powerOfX; + } + + @Override + public void calculateNextPower() { + powerOfX = powerOfX.multiply(xPowerTwo, mathContext); + } +}
\ No newline at end of file diff --git a/src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNPlusOneIterator.java b/src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNPlusOneIterator.java new file mode 100644 index 0000000000..57afa97634 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNPlusOneIterator.java @@ -0,0 +1,33 @@ +package ch.obermuhlner.math.big.internal; + +import java.math.BigDecimal; +import java.math.MathContext; + +/** + * {@link PowerIterator} to calculate x<sup>2*n+1</sup>. + */ +public class PowerTwoNPlusOneIterator implements PowerIterator { + + private final MathContext mathContext; + + private final BigDecimal xPowerTwo; + + private BigDecimal powerOfX; + + public PowerTwoNPlusOneIterator(BigDecimal x, MathContext mathContext) { + this.mathContext = mathContext; + + xPowerTwo = x.multiply(x, mathContext); + powerOfX = x; + } + + @Override + public BigDecimal getCurrentPower() { + return powerOfX; + } + + @Override + public void calculateNextPower() { + powerOfX = powerOfX.multiply(xPowerTwo, mathContext); + } +}
\ No newline at end of file diff --git a/src/main/java/ch/obermuhlner/math/big/internal/SeriesCalculator.java b/src/main/java/ch/obermuhlner/math/big/internal/SeriesCalculator.java new file mode 100644 index 0000000000..117c2737d2 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/SeriesCalculator.java @@ -0,0 +1,127 @@ +package ch.obermuhlner.math.big.internal; + +import ch.obermuhlner.math.big.BigRational; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.ArrayList; +import java.util.List; + +import static java.util.Objects.requireNonNull; + +/** + * Utility class to calculate taylor series efficiently until the maximum error (as defined by the precision in the {@link MathContext} is reached. + * + * <p>Stores the factors of the taylor series terms so that future calculations will be faster.</p> + */ +public abstract class SeriesCalculator { + + private final boolean calculateInPairs; + + private final List<BigRational> factors = new ArrayList<>(); + + /** + * Constructs a {@link SeriesCalculator} that calculates single terms. + */ + protected SeriesCalculator() { + this(false); + } + + /** + * Constructs a {@link SeriesCalculator} with control over whether the sum terms are calculated in pairs. + * + * <p>Calculation of pairs is useful for taylor series where the terms alternate the sign. + * In these cases it is more efficient to calculate two terms at once check then whether the acceptable error has been reached.</p> + * + * @param calculateInPairs <code>true</code> to calculate the terms in pairs, <code>false</code> to calculate single terms + */ + protected SeriesCalculator(boolean calculateInPairs) { + this.calculateInPairs = calculateInPairs; + } + + /** + * Calculates the series for the specified value x and the precision defined in the {@link MathContext}. + * + * @param x the value x + * @param mathContext the {@link MathContext} + * @return the calculated result + */ + public BigDecimal calculate(BigDecimal x, MathContext mathContext) { + BigDecimal acceptableError = BigDecimal.ONE.movePointLeft(mathContext.getPrecision() + 1); + + PowerIterator powerIterator = createPowerIterator(x, mathContext); + + BigDecimal sum = BigDecimal.ZERO; + BigDecimal step; + int i = 0; + do { + BigRational factor; + BigDecimal xToThePower; + + factor = getFactor(i); + xToThePower = powerIterator.getCurrentPower(); + powerIterator.calculateNextPower(); + step = factor.getNumerator().multiply(xToThePower).divide(factor.getDenominator(), mathContext); + i++; + + if (calculateInPairs) { + factor = getFactor(i); + xToThePower = powerIterator.getCurrentPower(); + powerIterator.calculateNextPower(); + BigDecimal step2 = factor.getNumerator().multiply(xToThePower).divide(factor.getDenominator(), mathContext); + step = step.add(step2); + i++; + } + + sum = sum.add(step); + //System.out.println(sum + " " + step); + } while (step.abs().compareTo(acceptableError) > 0); + + return sum.round(mathContext); + } + + /** + * Creates the {@link PowerIterator} used for this series. + * + * @param x the value x + * @param mathContext the {@link MathContext} + * @return the {@link PowerIterator} + */ + protected abstract PowerIterator createPowerIterator(BigDecimal x, MathContext mathContext); + + /** + * Returns the factor of the term with specified index. + * + * All mutable state of this class (and all its subclasses) must be modified in this method. + * This method is synchronized to allow thread-safe usage of this class. + * + * @param index the index (starting with 0) + * @return the factor of the specified term + */ + protected synchronized BigRational getFactor(int index) { + while (factors.size() <= index) { + BigRational factor = getCurrentFactor(); + addFactor(factor); + calculateNextFactor(); + } + return factors.get(index); + } + + private void addFactor(BigRational factor){ + factors.add(requireNonNull(factor, "Factor cannot be null")); + } + + /** + * Returns the factor of the highest term already calculated. + * <p>When called for the first time will return the factor of the first term (index 0).</p> + * <p>After this call the method {@link #calculateNextFactor()} will be called to prepare for the next term.</p> + * + * @return the factor of the highest term + */ + protected abstract BigRational getCurrentFactor(); + + /** + * Calculates the factor of the next term. + */ + protected abstract void calculateNextFactor(); +} diff --git a/src/main/java/ch/obermuhlner/math/big/internal/SinCalculator.java b/src/main/java/ch/obermuhlner/math/big/internal/SinCalculator.java new file mode 100644 index 0000000000..471074fd6b --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/SinCalculator.java @@ -0,0 +1,49 @@ +package ch.obermuhlner.math.big.internal; + +import java.math.BigDecimal; +import java.math.MathContext; + +import ch.obermuhlner.math.big.BigRational; + +/** + * Calculates sinus using the Maclaurin series. + * + * <p>See <a href="https://de.wikipedia.org/wiki/Taylorreihe">Wikipedia: Taylorreihe</a></p> + * + * <p>No argument checking or optimizations are done. + * This implementation is <strong>not</strong> intended to be called directly.</p> + */ +public class SinCalculator extends SeriesCalculator { + + public static final SinCalculator INSTANCE = new SinCalculator(); + + private int n = 0; + private boolean negative = false; + private BigRational factorial2nPlus1 = BigRational.ONE; + + private SinCalculator() { + super(true); + } + + @Override + protected BigRational getCurrentFactor() { + BigRational factor = factorial2nPlus1.reciprocal(); + if (negative) { + factor = factor.negate(); + } + return factor; + } + + @Override + protected void calculateNextFactor() { + n++; + factorial2nPlus1 = factorial2nPlus1.multiply(2 * n); + factorial2nPlus1 = factorial2nPlus1.multiply(2 * n + 1); + negative = !negative; + } + + @Override + protected PowerIterator createPowerIterator(BigDecimal x, MathContext mathContext) { + return new PowerTwoNPlusOneIterator(x, mathContext); + } +} diff --git a/src/main/java/ch/obermuhlner/math/big/internal/SinhCalculator.java b/src/main/java/ch/obermuhlner/math/big/internal/SinhCalculator.java new file mode 100644 index 0000000000..9601735755 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/internal/SinhCalculator.java @@ -0,0 +1,44 @@ +package ch.obermuhlner.math.big.internal; + +import java.math.BigDecimal; +import java.math.MathContext; + +import ch.obermuhlner.math.big.BigRational; + +/** + * Calculates sinus hyperbolicus using the Taylor series. + * + * <p>See <a href="https://en.wikipedia.org/wiki/Taylor_series">Wikipedia: Taylor series</a></p> + * + * <p>No argument checking or optimizations are done. + * This implementation is <strong>not</strong> intended to be called directly.</p> + */ +public class SinhCalculator extends SeriesCalculator { + + public static final SinhCalculator INSTANCE = new SinhCalculator(); + + private int n = 0; + + private BigRational factorial2nPlus1 = BigRational.ONE; + + private SinhCalculator() { + super(true); + } + + @Override + protected BigRational getCurrentFactor() { + return factorial2nPlus1.reciprocal(); + } + + @Override + protected void calculateNextFactor() { + n++; + factorial2nPlus1 = factorial2nPlus1.multiply(2 * n); + factorial2nPlus1 = factorial2nPlus1.multiply(2 * n + 1); + } + + @Override + protected PowerIterator createPowerIterator(BigDecimal x, MathContext mathContext) { + return new PowerTwoNPlusOneIterator(x, mathContext); + } +} diff --git a/src/main/java/ch/obermuhlner/math/big/stream/BigDecimalStream.java b/src/main/java/ch/obermuhlner/math/big/stream/BigDecimalStream.java new file mode 100644 index 0000000000..05a45fd846 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/stream/BigDecimalStream.java @@ -0,0 +1,219 @@ +package ch.obermuhlner.math.big.stream; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.Comparator; +import java.util.Spliterator; +import java.util.Spliterators.AbstractSpliterator; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import ch.obermuhlner.math.big.BigDecimalMath; + +/** + * Provides constructor methods for streams of {@link BigDecimal} elements. + */ +public class BigDecimalStream { + + /** + * Returns a sequential ordered {@code Stream<BigDecimal>} from {@code startInclusive} + * (inclusive) to {@code endExclusive} (exclusive) by an incremental {@code step}. + * + * <p>An equivalent sequence of increasing values can be produced + * sequentially using a {@code for} loop as follows: + * <pre>for (BigDecimal i = startInclusive; i.compareTo(endExclusive) < 0; i = i.add(step, mathContext)) { + // ... +}</pre> + * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @param step the step between elements + * @param mathContext the {@link MathContext} used for all mathematical operations + * @return a sequential {@code Stream<BigDecimal>} + */ + public static Stream<BigDecimal> range(BigDecimal startInclusive, BigDecimal endExclusive, BigDecimal step, MathContext mathContext) { + if (step.signum() == 0) { + throw new IllegalArgumentException("invalid step: 0"); + } + if (endExclusive.subtract(startInclusive).signum() != step.signum()) { + return Stream.empty(); + } + return StreamSupport.stream(new BigDecimalSpliterator(startInclusive, endExclusive, false, step, mathContext), false); + } + + /** + * Returns a sequential ordered {@code Stream<BigDecimal>} from {@code startInclusive} + * (inclusive) to {@code endExclusive} (exclusive) by an incremental {@code step}. + * + * <p>The {@code long} arguments are converted using {@link BigDecimal#valueOf(long)}.</p> + * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @param step the step between elements + * @param mathContext the {@link MathContext} used for all mathematical operations + * @return a sequential {@code Stream<BigDecimal>} + * @see #range(BigDecimal, BigDecimal, BigDecimal, MathContext) + */ + public static Stream<BigDecimal> range(long startInclusive, long endExclusive, long step, MathContext mathContext) { + return range(BigDecimal.valueOf(startInclusive), BigDecimal.valueOf(endExclusive), BigDecimal.valueOf(step), mathContext); + } + + /** + * Returns a sequential ordered {@code Stream<BigDecimal>} from {@code startInclusive} + * (inclusive) to {@code endExclusive} (exclusive) by an incremental {@code step}. + * + * <p>The {@code double} arguments are converted using {@link BigDecimal#valueOf(double)}.</p> + * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @param step the step between elements + * @param mathContext the {@link MathContext} used for all mathematical operations + * @return a sequential {@code Stream<BigDecimal>} + * @see #range(BigDecimal, BigDecimal, BigDecimal, MathContext) + */ + public static Stream<BigDecimal> range(double startInclusive, double endExclusive, double step, MathContext mathContext) { + return range(BigDecimal.valueOf(startInclusive), BigDecimal.valueOf(endExclusive), BigDecimal.valueOf(step), mathContext); + } + + /** + * Returns a sequential ordered {@code Stream<BigDecimal>} from {@code startInclusive} + * (inclusive) to {@code endInclusive} (inclusive) by an incremental {@code step}. + * + * <p>An equivalent sequence of increasing values can be produced + * sequentially using a {@code for} loop as follows: + * <pre>for (BigDecimal i = startInclusive; i.compareTo(endInclusive) <= 0; i = i.add(step, mathContext)) { + // ... +}</pre> + * + * @param startInclusive the (inclusive) initial value + * @param endInclusive the inclusive upper bound + * @param step the step between elements + * @param mathContext the {@link MathContext} used for all mathematical operations + * @return a sequential {@code Stream<BigDecimal>} + * @see #range(BigDecimal, BigDecimal, BigDecimal, MathContext) + */ + public static Stream<BigDecimal> rangeClosed(BigDecimal startInclusive, BigDecimal endInclusive, BigDecimal step, MathContext mathContext) { + if (step.signum() == 0) { + throw new IllegalArgumentException("invalid step: 0"); + } + if (endInclusive.subtract(startInclusive).signum() == -step.signum()) { + return Stream.empty(); + } + return StreamSupport.stream(new BigDecimalSpliterator(startInclusive, endInclusive, true, step, mathContext), false); + } + + /** + * Returns a sequential ordered {@code Stream<BigDecimal>} from {@code startInclusive} + * (inclusive) to {@code endInclusive} (inclusive) by an incremental {@code step}. + * + * <p>The {@code long} arguments are converted using {@link BigDecimal#valueOf(long)}.</p> + * + * @param startInclusive the (inclusive) initial value + * @param endInclusive the inclusive upper bound + * @param step the step between elements + * @param mathContext the {@link MathContext} used for all mathematical operations + * @return a sequential {@code Stream<BigDecimal>} + * @see #rangeClosed(BigDecimal, BigDecimal, BigDecimal, MathContext) + */ + public static Stream<BigDecimal> rangeClosed(long startInclusive, long endInclusive, long step, MathContext mathContext) { + return rangeClosed(BigDecimal.valueOf(startInclusive), BigDecimal.valueOf(endInclusive), BigDecimal.valueOf(step), mathContext); + } + + /** + * Returns a sequential ordered {@code Stream<BigDecimal>} from {@code startInclusive} + * (inclusive) to {@code endInclusive} (inclusive) by an incremental {@code step}. + * + * <p>The {@code double} arguments are converted using {@link BigDecimal#valueOf(double)}.</p> + * + * @param startInclusive the (inclusive) initial value + * @param endInclusive the inclusive upper bound + * @param step the step between elements + * @param mathContext the {@link MathContext} used for all mathematical operations + * @return a sequential {@code Stream<BigDecimal>} + * @see #rangeClosed(BigDecimal, BigDecimal, BigDecimal, MathContext) + */ + public static Stream<BigDecimal> rangeClosed(double startInclusive, double endInclusive, double step, MathContext mathContext) { + return rangeClosed(BigDecimal.valueOf(startInclusive), BigDecimal.valueOf(endInclusive), BigDecimal.valueOf(step), mathContext); + } + + private static class BigDecimalSpliterator extends AbstractSpliterator<BigDecimal> { + + private BigDecimal value; + private BigDecimal step; + private long count; + private MathContext mathContext; + + public BigDecimalSpliterator(BigDecimal startInclusive, BigDecimal step, long count, MathContext mathContext) { + super(count, + Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SORTED); + + this.value = startInclusive; + this.step = step; + this.count = count; + this.mathContext = mathContext; + } + + public BigDecimalSpliterator(BigDecimal startInclusive, BigDecimal end, boolean inclusive, BigDecimal step, MathContext mathContext) { + this(startInclusive, step, estimatedCount(startInclusive, end, inclusive, step, mathContext), mathContext); + } + + private static long estimatedCount(BigDecimal startInclusive, BigDecimal end, boolean inclusive, BigDecimal step, MathContext mathContext) { + BigDecimal count = end.subtract(startInclusive).divide(step, mathContext); + long result = count.longValue(); + if (BigDecimalMath.fractionalPart(count).signum() != 0) { + result++; + } else { + if (inclusive) { + result++; + } + } + return result; + } + + @Override + public Comparator<? super BigDecimal> getComparator() { + if (step.signum() < 0) { + return Comparator.reverseOrder(); + } + return null; + } + + @Override + public boolean tryAdvance(Consumer<? super BigDecimal> action) { + if (count == 0) { + return false; + } + + action.accept(value); + value = value.add(step, mathContext); + count--; + return true; + } + + @Override + public void forEachRemaining(Consumer<? super BigDecimal> action) { + while (count > 0) { + action.accept(value); + value = value.add(step, mathContext); + count--; + } + } + + @Override + public Spliterator<BigDecimal> trySplit() { + long firstHalfCount = count / 2; + + if (firstHalfCount == 0) { + return null; + } + + long secondHalfCount = count - firstHalfCount; + + count = firstHalfCount; + BigDecimal startSecondHalf = value.add(step.multiply(new BigDecimal(firstHalfCount), mathContext), mathContext); + + return new BigDecimalSpliterator(startSecondHalf, step, secondHalfCount, mathContext); + } + } +} diff --git a/src/main/java/ch/obermuhlner/math/big/stream/BigFloatStream.java b/src/main/java/ch/obermuhlner/math/big/stream/BigFloatStream.java new file mode 100644 index 0000000000..1c982302a6 --- /dev/null +++ b/src/main/java/ch/obermuhlner/math/big/stream/BigFloatStream.java @@ -0,0 +1,214 @@ +package ch.obermuhlner.math.big.stream; + +import java.util.Comparator; +import java.util.Spliterator; +import java.util.Spliterators.AbstractSpliterator; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import ch.obermuhlner.math.big.BigFloat; +import ch.obermuhlner.math.big.BigFloat.Context; + +/** + * Provides constructor methods for streams of {@link BigFloat} elements. + */ +public class BigFloatStream { + + /** + * Returns a sequential ordered {@code Stream<BigFloat>} from {@code startInclusive} + * (inclusive) to {@code endExclusive} (exclusive) by an incremental {@code step}. + * + * <p>An equivalent sequence of increasing values can be produced + * sequentially using a {@code for} loop as follows: + * <pre>for (BigFloat i = startInclusive; i.isLessThan(endExclusive); i = i.add(step)) { + // ... +}</pre> + * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @param step the step between elements + * @return a sequential {@code Stream<BigFloat>} + */ + public static Stream<BigFloat> range(BigFloat startInclusive, BigFloat endExclusive, BigFloat step) { + if (step.isZero()) { + throw new IllegalArgumentException("invalid step: 0"); + } + if (endExclusive.subtract(startInclusive).signum() != step.signum()) { + return Stream.empty(); + } + return StreamSupport.stream(new BigFloatSpliterator(startInclusive, endExclusive, false, step), false); + } + + /** + * Returns a sequential ordered {@code Stream<BigFloat>} from {@code startInclusive} + * (inclusive) to {@code endExclusive} (exclusive) by an incremental {@code step}. + * + * <p>{@link Context#valueOf(long)} is used to convert the {@code long} values.</p> + * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @param step the step between elements + * @param context the {@link Context} used to convert the {@code long} values + * @return a sequential {@code Stream<BigFloat>} + * @see #range(BigFloat, BigFloat, BigFloat) + */ + public static Stream<BigFloat> range(long startInclusive, long endExclusive, long step, Context context) { + return range(context.valueOf(startInclusive), context.valueOf(endExclusive), context.valueOf(step)); + } + + /** + * Returns a sequential ordered {@code Stream<BigFloat>} from {@code startInclusive} + * (inclusive) to {@code endExclusive} (exclusive) by an incremental {@code step}. + * + * <p>{@link Context#valueOf(double)} is used to convert the {@code double} values.</p> + * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @param step the step between elements + * @param context the {@link Context} used to convert the {@code double} values + * @return a sequential {@code Stream<BigFloat>} + * @see #range(BigFloat, BigFloat, BigFloat) + */ + public static Stream<BigFloat> range(double startInclusive, double endExclusive, double step, Context context) { + return range(context.valueOf(startInclusive), context.valueOf(endExclusive), context.valueOf(step)); + } + + /** + * Returns a sequential ordered {@code Stream<BigFloat>} from {@code startInclusive} + * (inclusive) to {@code endInclusive} (inclusive) by an incremental {@code step}. + * + * <p>An equivalent sequence of increasing values can be produced + * sequentially using a {@code for} loop as follows: + * <pre>for (BigFloat i = startInclusive; i.isLessThanOrEqual(endInclusive); i = i.add(step)) { + //... +} +</pre> + * + * @param startInclusive the (inclusive) initial value + * @param endInclusive the inclusive upper bound + * @param step the step between elements + * @return a sequential {@code Stream<BigFloat>} + */ + public static Stream<BigFloat> rangeClosed(BigFloat startInclusive, BigFloat endInclusive, BigFloat step) { + if (step.isZero()) { + throw new IllegalArgumentException("invalid step: 0"); + } + if (endInclusive.subtract(startInclusive).signum() == -step.signum()) { + return Stream.empty(); + } + return StreamSupport.stream(new BigFloatSpliterator(startInclusive, endInclusive, true, step), false); + } + + /** + * Returns a sequential ordered {@code Stream<BigFloat>} from {@code startInclusive} + * (inclusive) to {@code endInclusive} (inclusive) by an incremental {@code step}. + * + * <p>{@link Context#valueOf(long)} is used to convert the {@code long} values.</p> + * + * @param startInclusive the (inclusive) initial value + * @param endInclusive the inclusive upper bound + * @param step the step between elements + * @param context the {@link Context} used to convert the {@code long} values + * @return a sequential {@code Stream<BigFloat>} + * @see #rangeClosed(BigFloat, BigFloat, BigFloat) + */ + public static Stream<BigFloat> rangeClosed(long startInclusive, long endInclusive, long step, Context context) { + return rangeClosed(context.valueOf(startInclusive), context.valueOf(endInclusive), context.valueOf(step)); + } + + /** + * Returns a sequential ordered {@code Stream<BigFloat>} from {@code startInclusive} + * (inclusive) to {@code endInclusive} (inclusive) by an incremental {@code step}. + * + * <p>{@link Context#valueOf(double)} is used to convert the {@code double} values.</p> + * + * @param startInclusive the (inclusive) initial value + * @param endInclusive the inclusive upper bound + * @param step the step between elements + * @param context the {@link Context} used to convert the {@code double} values + * @return a sequential {@code Stream<BigFloat>} + * @see #rangeClosed(BigFloat, BigFloat, BigFloat) + */ + public static Stream<BigFloat> rangeClosed(double startInclusive, double endInclusive, double step, Context context) { + return rangeClosed(context.valueOf(startInclusive), context.valueOf(endInclusive), context.valueOf(step)); + } + + private static class BigFloatSpliterator extends AbstractSpliterator<BigFloat> { + + private BigFloat value; + private BigFloat step; + private long count; + + public BigFloatSpliterator(BigFloat startInclusive, BigFloat step, long count) { + super(count, + Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SORTED); + + this.value = startInclusive; + this.step = step; + this.count = count; + } + + public BigFloatSpliterator(BigFloat startInclusive, BigFloat end, boolean inclusive, BigFloat step) { + this(startInclusive, step, estimatedCount(startInclusive, end, inclusive, step)); + } + + private static long estimatedCount(BigFloat startInclusive, BigFloat end, boolean inclusive, BigFloat step) { + BigFloat count = end.subtract(startInclusive).divide(step); + long result = count.toLong(); + if (count.getFractionalPart().signum() != 0) { + result++; + } else { + if (inclusive) { + result++; + } + } + return result; + } + + @Override + public Comparator<? super BigFloat> getComparator() { + if (step.signum() < 0) { + return Comparator.reverseOrder(); + } + return null; + } + + @Override + public boolean tryAdvance(Consumer<? super BigFloat> action) { + if (count == 0) { + return false; + } + + action.accept(value); + value = value.add(step); + count--; + return true; + } + + @Override + public void forEachRemaining(Consumer<? super BigFloat> action) { + while (count > 0) { + action.accept(value); + value = value.add(step); + count--; + } + } + + @Override + public Spliterator<BigFloat> trySplit() { + long firstHalfCount = count / 2; + + if (firstHalfCount == 0) { + return null; + } + + long secondHalfCount = count - firstHalfCount; + + count = firstHalfCount; + BigFloat startSecondHalf = value.add(step.multiply(firstHalfCount)); + + return new BigFloatSpliterator(startSecondHalf, step, secondHalfCount); + } + } +} diff --git a/src/main/java/com/github/technus/tectech/compatibility/gtpp/GtppAtomLoader.java b/src/main/java/com/github/technus/tectech/compatibility/gtpp/GtppAtomLoader.java index 333747d632..32db2caed9 100644 --- a/src/main/java/com/github/technus/tectech/compatibility/gtpp/GtppAtomLoader.java +++ b/src/main/java/com/github/technus/tectech/compatibility/gtpp/GtppAtomLoader.java @@ -74,18 +74,18 @@ public class GtppAtomLoader implements Runnable{ TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getFirstStableIsotope(75), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("RHENIUM"),1); TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getFirstStableIsotope(81), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("THALLIUM"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(84),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("POLONIUM"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(85),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("ASTATINE"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(87),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("FRANCIUM"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(88),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("RADIUM"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(89),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("ACTINIUM"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(91),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("PROTACTINIUM"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(93),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("NEPTUNIUM"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(84), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("POLONIUM"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(85), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("ASTATINE"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(87), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("FRANCIUM"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(88), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("RADIUM"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(89), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("ACTINIUM"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(91), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("PROTACTINIUM"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(93), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("NEPTUNIUM"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(96),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("CURIUM"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(97),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("BERKELIUM"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(98),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("CALIFORNIUM"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(99),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("EINSTEINIUM"),1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(100),AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("FERMIUM"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(96), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("CURIUM"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(97), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("BERKELIUM"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(98), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("CALIFORNIUM"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(99), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("EINSTEINIUM"),1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(100), AVOGADRO_CONSTANT_144),OrePrefixes.dust, getUnlocalizedName("FERMIUM"),1); } } diff --git a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/cElementalInstanceStackMap.java b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/cElementalInstanceStackMap.java index a712dc2c39..ad22f5123b 100644 --- a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/cElementalInstanceStackMap.java +++ b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/cElementalInstanceStackMap.java @@ -9,6 +9,8 @@ import net.minecraft.util.EnumChatFormatting; import java.util.*; +import static com.github.technus.tectech.mechanics.elementalMatter.core.transformations.bTransformationInfo.AVOGADRO_CONSTANT; +import static com.github.technus.tectech.mechanics.elementalMatter.core.transformations.bTransformationInfo.AVOGADRO_CONSTANT_UNCERTAINTY; import static com.github.technus.tectech.mechanics.elementalMatter.definitions.primitive.cPrimitiveDefinition.nbtE__; import static com.github.technus.tectech.util.DoubleCount.add; import static com.github.technus.tectech.util.DoubleCount.sub; @@ -331,12 +333,12 @@ public final class cElementalInstanceStackMap implements Comparable<cElementalIn String[] info = new String[map.size() * 4]; int i = 0; for (cElementalInstanceStack instance : map.values()) { - info[i] = EnumChatFormatting.BLUE + instance.definition.getName(); - info[i + 1] = EnumChatFormatting.AQUA + instance.definition.getSymbol(); - info[i + 2] = "Amount " + EnumChatFormatting.GREEN + instance.amount; - info[i + 3] = "LifeTime " + EnumChatFormatting.GREEN + instance.getLifeTime(); - i += 4; + info[i++] = EnumChatFormatting.BLUE + instance.definition.getName(); + info[i++] = EnumChatFormatting.AQUA + instance.definition.getSymbol(); + info[i++] = "Amount " + EnumChatFormatting.GREEN + instance.amount/ AVOGADRO_CONSTANT +" mol"; + info[i++] = "LifeTime " + EnumChatFormatting.GREEN + (instance.getLifeTime()<0?"STABLE":instance.getLifeTime()); } + return info; } @@ -423,9 +425,7 @@ public final class cElementalInstanceStackMap implements Comparable<cElementalIn instance.nextColor(); } else { removeAmount(false,instance); - for (cElementalInstanceStack newInstance : newInstances.values()) { - putUnify(newInstance); - } + putUnifyAll(newInstances); } } } @@ -551,6 +551,6 @@ public final class cElementalInstanceStackMap implements Comparable<cElementalIn } public void cleanUp(){ - map.entrySet().removeIf(entry -> entry.getValue().amount < 1); + map.entrySet().removeIf(entry -> entry.getValue().amount < AVOGADRO_CONSTANT_UNCERTAINTY); } } diff --git a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/stacks/cElementalInstanceStack.java b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/stacks/cElementalInstanceStack.java index 2a441150f7..2aa6bb9711 100644 --- a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/stacks/cElementalInstanceStack.java +++ b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/stacks/cElementalInstanceStack.java @@ -1,5 +1,6 @@ package com.github.technus.tectech.mechanics.elementalMatter.core.stacks; +import ch.obermuhlner.math.big.BigFloat; import com.github.technus.tectech.TecTech; import com.github.technus.tectech.mechanics.elementalMatter.core.cElementalDecay; import com.github.technus.tectech.mechanics.elementalMatter.core.cElementalDefinitionStackMap; @@ -11,10 +12,12 @@ import net.minecraft.nbt.NBTTagCompound; import java.util.ArrayList; +import static com.github.technus.tectech.mechanics.elementalMatter.core.transformations.bTransformationInfo.AVOGADRO_CONSTANT; import static com.github.technus.tectech.mechanics.elementalMatter.definitions.primitive.cPrimitiveDefinition.null__; import static com.github.technus.tectech.mechanics.elementalMatter.definitions.primitive.eBosonDefinition.deadEnd; import static com.github.technus.tectech.thing.metaTileEntity.multi.GT_MetaTileEntity_EM_scanner.*; import static com.github.technus.tectech.util.DoubleCount.*; +import static java.lang.Math.min; import static java.lang.Math.ulp; /** @@ -189,9 +192,10 @@ public final class cElementalInstanceStack implements iHasElementalDefinition { } if(definition.usesMultipleDecayCalls(energy)){ double amountTemp=amount; - long decayCnt=(long) Math.min(Math.max(amount/DECAY_CALL_PER,MIN_MULTIPLE_DECAY_CALLS),MAX_MULTIPLE_DECAY_CALLS); + long decayCnt=(long) min(Math.max(amount/DECAY_CALL_PER,MIN_MULTIPLE_DECAY_CALLS),MAX_MULTIPLE_DECAY_CALLS); double amountPer= div(amount,decayCnt); amount= sub(amount,amountPer*(--decayCnt)); + //todo decay mechanics should handle splitting! cElementalInstanceStackMap output=decayMechanics(lifeTimeMult,apparentAge,newEnergyLevel); if(output==null){ amount=amountTemp; @@ -233,8 +237,27 @@ public final class cElementalInstanceStack implements iHasElementalDefinition { //Use to get direct decay output providing correct decay array private cElementalInstanceStackMap exponentialDecayCompute(cElementalDecay[] decays, double lifeTimeMult, double newProductsAge, long newEnergyLevel) { + if(lifeTime<1){ + throw new ArithmeticException("Value would be too big..."); + } double decayInverseRatio=Math.pow(2D,1D/* 1 second *//lifeTime); - double newAmount= div(amount,decayInverseRatio+ulp(decayInverseRatio)); + double newAmount,decayedAmount; + if(decayInverseRatio>0.99999999D){ + //todo cache this... + BigFloat dir=BigFloat.context(50).valueOf(2).pow(1D/* 1 second *//lifeTime); + BigFloat na=BigFloat.context(50).valueOf(amount).divide(dir); + newAmount=na.toDouble(); + if(newAmount>=amount) { + decayedAmount=BigFloat.context(50).valueOf(amount).subtract(na).toDouble(); + newAmount=amount-ulp(amount); + }else { + decayedAmount=amount-newAmount; + } + }else{ + newAmount= div(amount,decayInverseRatio); + decayedAmount=amount-newAmount; + } + //if(definition.getSymbol().startsWith("U ")) { // System.out.println("newAmount = " + newAmount); // System.out.println("amountRemaining = " + amountRemaining); @@ -245,18 +268,20 @@ public final class cElementalInstanceStack implements iHasElementalDefinition { // } // } //} - //if(newAmount==amount) {//no longer needed - // return null;//nothing decayed - //} else if(newAmount<=0) {//no longer needed - // return decayCompute(decays, lifeTimeMult, newProductsAge, newEnergyLevel); - //}//no longer needed + if(newAmount==amount) { + return null;//nothing decayed + } else if(newAmount<1) { + return decayCompute(decays, lifeTimeMult, newProductsAge, newEnergyLevel); + } //split to non decaying and decaying part double amount=this.amount; - this.amount= sub(this.amount,newAmount); + this.amount= decayedAmount; cElementalInstanceStackMap products=decayCompute(decays,lifeTimeMult,newProductsAge,newEnergyLevel); this.amount=newAmount; - products.putUnify(clone()); + if(products!=null){ + products.putUnify(clone()); + } this.amount=amount; return products; } @@ -411,7 +436,7 @@ public final class cElementalInstanceStack implements iHasElementalDefinition { if(instance.energy>maxEnergy){ maxEnergy=instance.energy; } - lifeTimeMul = Math.min(lifeTimeMul, instance.lifeTimeMult); + lifeTimeMul = min(lifeTimeMul, instance.lifeTimeMult); age = Math.max(age, instance.age); } } @@ -421,12 +446,12 @@ public final class cElementalInstanceStack implements iHasElementalDefinition { } double wholeParts=Math.floor(energyTotal); - energyTotal=Math.min(energyTotal-wholeParts,1D)+(wholeParts>=0?-0.11709966304863834D:0.11709966304863834D); + energyTotal= min(energyTotal-wholeParts,1D)+(wholeParts>=0?-0.11709966304863834D:0.11709966304863834D); long energy=(long) wholeParts + ((energyTotal > TecTech.RANDOM.nextDouble()) ? 1 : 0); if(energy*energyTotal<0){ energy=0; } - setEnergy(Math.min(maxEnergy,energy)); + setEnergy(min(maxEnergy,energy)); return this; } @@ -468,7 +493,7 @@ public final class cElementalInstanceStack implements iHasElementalDefinition { lines.add("ENERGY = " + energy); } if(Util.areBitsSet(SCAN_GET_AMOUNT,capabilities)) { - lines.add("AMOUNT = " + amount); + lines.add("AMOUNT = " + amount/ AVOGADRO_CONSTANT +" mol"); } scanContents(lines,definition.getSubParticles(),1,detailsOnDepthLevels); } @@ -537,6 +562,6 @@ public final class cElementalInstanceStack implements iHasElementalDefinition { @Override public String toString() { - return definition.getName()+ '\n' + definition.getSymbol() + '\n' + amount + '\n' + getMass(); + return definition.toString() + '\n' + amount/ AVOGADRO_CONSTANT + " mol\n" + getMass(); } } diff --git a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/templates/cElementalDefinition.java b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/templates/cElementalDefinition.java index 0061550ace..51025148a7 100644 --- a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/templates/cElementalDefinition.java +++ b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/templates/cElementalDefinition.java @@ -127,4 +127,9 @@ public abstract class cElementalDefinition extends iElementalDefinition { } return hash; } + + @Override + public String toString() { + return getName()+ '\n' + getSymbol(); + } } diff --git a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/transformations/bTransformationInfo.java b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/transformations/bTransformationInfo.java index 24ae8d0481..964baff155 100644 --- a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/transformations/bTransformationInfo.java +++ b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/transformations/bTransformationInfo.java @@ -17,8 +17,9 @@ import static com.github.technus.tectech.thing.item.DebugElementalInstanceContai * Created by Tec on 26.05.2017. */ public class bTransformationInfo { - public static final double AVOGADRO_CONSTANT =6.02214076e23D; - public static final double AVOGADRO_CONSTANT_144 =AVOGADRO_CONSTANT*144D; + public static final double AVOGADRO_CONSTANT =6.02214076e23D;//CUBE LOL XD + public static final double AVOGADRO_CONSTANT_UNCERTAINTY =1/6.02214076e23D;//CUBE LOL XD + public static final double AVOGADRO_CONSTANT_144 = AVOGADRO_CONSTANT *144D; public static final HashMap<Integer,aFluidQuantizationInfo> fluidQuantization=new HashMap<>(32); public static final HashMap<aItemQuantizationInfo,aItemQuantizationInfo> itemQuantization=new HashMap<>(32); diff --git a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/definitions/complex/dAtomDefinition.java b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/definitions/complex/dAtomDefinition.java index b1801b9b25..b990adef15 100644 --- a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/definitions/complex/dAtomDefinition.java +++ b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/definitions/complex/dAtomDefinition.java @@ -1564,16 +1564,16 @@ public final class dAtomDefinition extends cElementalDefinition { TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(83), AVOGADRO_CONSTANT_144), dust, Materials.Bismuth,1); //transformation.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(84),AVOGADRO_CONSTANT_144),OrePrefixes.dust, Materials.Polonium,1); //transformation.addFluid(new cElementalDefinitionStack(getBestUnstableIsotope(85),AVOGADRO_CONSTANT_144),Materials.Astatine.mPlasma.getID(), 144); - TRANSFORMATION_INFO.addFluid(new cElementalDefinitionStack(getBestUnstableIsotope(86),AVOGADRO_CONSTANT_144),Materials.Radon.mGas, 144); + TRANSFORMATION_INFO.addFluid(new cElementalDefinitionStack(getBestUnstableIsotope(86), AVOGADRO_CONSTANT_144),Materials.Radon.mGas, 144); //transformation.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(87),AVOGADRO_CONSTANT_144),OrePrefixes.dust, Materials.Francium,1); //transformation.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(88),AVOGADRO_CONSTANT_144),OrePrefixes.dust, Materials.Radium,1); //transformation.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(89),AVOGADRO_CONSTANT_144),OrePrefixes.dust, Materials.Actinium,1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(90),AVOGADRO_CONSTANT_144), dust, Materials.Thorium,1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(90), AVOGADRO_CONSTANT_144), dust, Materials.Thorium,1); //transformation.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(91),AVOGADRO_CONSTANT_144),OrePrefixes.dust, Materials.Protactinium,1); ////transformation.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(92),AVOGADRO_CONSTANT_144), dust, Materials.Uranium,1); //transformation.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(93),AVOGADRO_CONSTANT_144),OrePrefixes.dust, Materials.Neptunium,1); ////transformation.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(94),AVOGADRO_CONSTANT_144), dust, Materials.Plutonium,1); - TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(95),AVOGADRO_CONSTANT_144), dust, Materials.Americium,1); + TRANSFORMATION_INFO.addOredict(new cElementalDefinitionStack(getBestUnstableIsotope(95), AVOGADRO_CONSTANT_144), dust, Materials.Americium,1); try { dAtomDefinition temp; diff --git a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/definitions/complex/dHadronDefinition.java b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/definitions/complex/dHadronDefinition.java index 6b5f41f1c1..6b9e56bbf1 100644 --- a/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/definitions/complex/dHadronDefinition.java +++ b/src/main/java/com/github/technus/tectech/mechanics/elementalMatter/definitions/complex/dHadronDefinition.java @@ -465,7 +465,7 @@ public final class dHadronDefinition extends cElementalDefinition {//TODO Optimi public static void setTransformations(){ //Added to atom map, but should be in its own - cElementalDefinitionStack neutrons=new cElementalDefinitionStack(hadron_n, 1000*AVOGADRO_CONSTANT_144); + cElementalDefinitionStack neutrons=new cElementalDefinitionStack(hadron_n, 1000* AVOGADRO_CONSTANT_144); TRANSFORMATION_INFO.oredictDequantization.put(neutrons.definition,new aOredictDequantizationInfo(neutrons, dust, Materials.Neutronium,1)); bTransformationInfo.oredictQuantization.put( OreDictionary.getOreID(OrePrefixes.ingotHot.name()+Materials.Neutronium.mName), diff --git a/src/main/java/com/github/technus/tectech/thing/item/DebugElementalInstanceContainer_EM.java b/src/main/java/com/github/technus/tectech/thing/item/DebugElementalInstanceContainer_EM.java index 8e9266f060..6850b24e0f 100644 --- a/src/main/java/com/github/technus/tectech/thing/item/DebugElementalInstanceContainer_EM.java +++ b/src/main/java/com/github/technus/tectech/thing/item/DebugElementalInstanceContainer_EM.java @@ -142,10 +142,10 @@ public final class DebugElementalInstanceContainer_EM extends Item implements IE ItemStack that = new ItemStack(this, 1); that.setTagCompound(new NBTTagCompound()); list.add(that); - for(iElementalDefinition defintion: STACKS_REGISTERED){ - list.add(setContent(new ItemStack(this).setStackDisplayName(defintion.getName()+" x"+AVOGADRO_CONSTANT),new cElementalInstanceStackMap(new cElementalInstanceStack(defintion,AVOGADRO_CONSTANT)))); - list.add(setContent(new ItemStack(this).setStackDisplayName(defintion.getName()+" x"+AVOGADRO_CONSTANT_144),new cElementalInstanceStackMap(new cElementalInstanceStack(defintion,AVOGADRO_CONSTANT_144)))); - list.add(setContent(new ItemStack(this).setStackDisplayName(defintion.getName()+" x"+AVOGADRO_CONSTANT*1000),new cElementalInstanceStackMap(new cElementalInstanceStack(defintion,AVOGADRO_CONSTANT*1000)))); + for(iElementalDefinition definition: STACKS_REGISTERED){ + list.add(setContent(new ItemStack(this).setStackDisplayName(definition.getName()+" 1 mol"),new cElementalInstanceStackMap(new cElementalInstanceStack(definition, AVOGADRO_CONSTANT)))); + list.add(setContent(new ItemStack(this).setStackDisplayName(definition.getName()+" 144 mol"),new cElementalInstanceStackMap(new cElementalInstanceStack(definition, AVOGADRO_CONSTANT_144)))); + list.add(setContent(new ItemStack(this).setStackDisplayName(definition.getName()+" 1000 mol"),new cElementalInstanceStackMap(new cElementalInstanceStack(definition, AVOGADRO_CONSTANT *1000)))); } } diff --git a/src/main/java/com/github/technus/tectech/thing/metaTileEntity/hatch/GT_MetaTileEntity_Hatch_ElementalContainer.java b/src/main/java/com/github/technus/tectech/thing/metaTileEntity/hatch/GT_MetaTileEntity_Hatch_ElementalContainer.java index 139b203551..10bd099366 100644 --- a/src/main/java/com/github/technus/tectech/thing/metaTileEntity/hatch/GT_MetaTileEntity_Hatch_ElementalContainer.java +++ b/src/main/java/com/github/technus/tectech/thing/metaTileEntity/hatch/GT_MetaTileEntity_Hatch_ElementalContainer.java @@ -208,7 +208,7 @@ public abstract class GT_MetaTileEntity_Hatch_ElementalContainer extends GT_Meta } public int getMaxStacksCount() { - return mTier * 16; + return mTier * 128; } public double getMaxStackSize() { diff --git a/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/GT_MetaTileEntity_EM_collider.java b/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/GT_MetaTileEntity_EM_collider.java index 825b07f289..e0398a8fcb 100644 --- a/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/GT_MetaTileEntity_EM_collider.java +++ b/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/GT_MetaTileEntity_EM_collider.java @@ -368,7 +368,7 @@ public class GT_MetaTileEntity_EM_collider extends GT_MetaTileEntity_MultiblockB } public static void setValues(int heliumPlasmaValue) { - double MASS_TO_EU_PARTIAL = heliumPlasmaValue / (1.75893000478707E07*AVOGADRO_CONSTANT);//mass diff + double MASS_TO_EU_PARTIAL = heliumPlasmaValue / (1.75893000478707E07* AVOGADRO_CONSTANT);//mass diff MASS_TO_EU_INSTANT = MASS_TO_EU_PARTIAL * 20; STARTUP_COST = -heliumPlasmaValue * 10000; KEEPUP_COST = -heliumPlasmaValue; diff --git a/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/GT_MetaTileEntity_EM_decay.java b/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/GT_MetaTileEntity_EM_decay.java index a46c407325..37e121dd26 100644 --- a/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/GT_MetaTileEntity_EM_decay.java +++ b/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/GT_MetaTileEntity_EM_decay.java @@ -50,7 +50,7 @@ public class GT_MetaTileEntity_EM_decay extends GT_MetaTileEntity_MultiblockBase private static Textures.BlockIcons.CustomIcon ScreenOFF; private static Textures.BlockIcons.CustomIcon ScreenON; - public static final double URANIUM_INGOT_MASS_DIFF = 1.6114516E10*AVOGADRO_CONSTANT; + public static final double URANIUM_INGOT_MASS_DIFF = 1.6114516E10* AVOGADRO_CONSTANT; private static final double URANIUM_MASS_TO_EU_PARTIAL = ConfigUtil.getFloat(MainConfig.get(), "balance/energy/generator/nuclear") * 3_000_000.0 / URANIUM_INGOT_MASS_DIFF; public static final double URANIUM_MASS_TO_EU_INSTANT = URANIUM_MASS_TO_EU_PARTIAL * 20; diff --git a/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/em_machine/Behaviour_ElectromagneticSeparator.java b/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/em_machine/Behaviour_ElectromagneticSeparator.java index 558ae03e67..40e2942bab 100644 --- a/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/em_machine/Behaviour_ElectromagneticSeparator.java +++ b/src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/em_machine/Behaviour_ElectromagneticSeparator.java @@ -88,7 +88,7 @@ public class Behaviour_ElectromagneticSeparator implements GT_MetaTileEntity_EM_ public Behaviour_ElectromagneticSeparator(int desiredTier){ tier=(byte) desiredTier; ticks =Math.max(20,(1<<(12-desiredTier))*20); - maxCapacity= dAtomDefinition.getSomethingHeavy().getMass()*(2<<tier)*AVOGADRO_CONSTANT_144; + maxCapacity= dAtomDefinition.getSomethingHeavy().getMass()*(2<<tier)* AVOGADRO_CONSTANT_144; maxCharge=144D*(1<<(tier-5)); switch (tier){ case 12: |