aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTec <daniel112092@gmail.com>2020-07-18 22:49:35 +0200
committerTec <daniel112092@gmail.com>2020-07-18 22:49:35 +0200
commit11e50eb56f6750d6bdfe15b986eef55e27452211 (patch)
treef6d4e90967e76c2fa2f988a823340fa85b9a604b
parent5c68b296e1b499ca83d78f24837768ac3a75df62 (diff)
downloadGT5-Unofficial-11e50eb56f6750d6bdfe15b986eef55e27452211.tar.gz
GT5-Unofficial-11e50eb56f6750d6bdfe15b986eef55e27452211.tar.bz2
GT5-Unofficial-11e50eb56f6750d6bdfe15b986eef55e27452211.zip
Implement big float, transition to molarity
-rw-r--r--src/main/java/ch/obermuhlner/math/big/BigComplex.java556
-rw-r--r--src/main/java/ch/obermuhlner/math/big/BigComplexMath.java413
-rw-r--r--src/main/java/ch/obermuhlner/math/big/BigDecimalMath.java1671
-rw-r--r--src/main/java/ch/obermuhlner/math/big/BigFloat.java1947
-rw-r--r--src/main/java/ch/obermuhlner/math/big/BigRational.java1103
-rw-r--r--src/main/java/ch/obermuhlner/math/big/DefaultBigDecimalMath.java736
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/AsinCalculator.java46
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/AtanhCalculator.java40
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/CosCalculator.java48
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/CoshCalculator.java43
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/ExpCalculator.java42
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/PowerIterator.java28
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/PowerNIterator.java33
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNIterator.java33
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNMinusOneIterator.java35
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/PowerTwoNPlusOneIterator.java33
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/SeriesCalculator.java127
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/SinCalculator.java49
-rw-r--r--src/main/java/ch/obermuhlner/math/big/internal/SinhCalculator.java44
-rw-r--r--src/main/java/ch/obermuhlner/math/big/stream/BigDecimalStream.java219
-rw-r--r--src/main/java/ch/obermuhlner/math/big/stream/BigFloatStream.java214
-rw-r--r--src/main/java/com/github/technus/tectech/compatibility/gtpp/GtppAtomLoader.java24
-rw-r--r--src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/cElementalInstanceStackMap.java18
-rw-r--r--src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/stacks/cElementalInstanceStack.java53
-rw-r--r--src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/templates/cElementalDefinition.java5
-rw-r--r--src/main/java/com/github/technus/tectech/mechanics/elementalMatter/core/transformations/bTransformationInfo.java5
-rw-r--r--src/main/java/com/github/technus/tectech/mechanics/elementalMatter/definitions/complex/dAtomDefinition.java6
-rw-r--r--src/main/java/com/github/technus/tectech/mechanics/elementalMatter/definitions/complex/dHadronDefinition.java2
-rw-r--r--src/main/java/com/github/technus/tectech/thing/item/DebugElementalInstanceContainer_EM.java8
-rw-r--r--src/main/java/com/github/technus/tectech/thing/metaTileEntity/hatch/GT_MetaTileEntity_Hatch_ElementalContainer.java2
-rw-r--r--src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/GT_MetaTileEntity_EM_collider.java2
-rw-r--r--src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/GT_MetaTileEntity_EM_decay.java2
-rw-r--r--src/main/java/com/github/technus/tectech/thing/metaTileEntity/multi/em_machine/Behaviour_ElectromagneticSeparator.java2
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 &lt; 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 &gt; 1 returns 0</p>
+ * <p>See: <a href="https://en.wikipedia.org/wiki/Bernoulli_number">Wikipedia: Bernoulli number</a></p>
+ *
+ * @param n the index of the Bernoulli number to be calculated (starting at 0)
+ * @param mathContext the {@link MathContext} used for the result
+ * @return the Bernoulli number for the specified index
+ * @throws ArithmeticException if x &lt; 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 &lt; 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 &lt; 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 &lt;= 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 &lt;= 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 &lt;= 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 &gt; 1 or x &lt; -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 &gt; 1 or x &lt; -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>,&nbsp;<code>y</code>) to polar coordinates (r,&nbsp;<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) &lt; 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) &gt; 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) &lt;= 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) &gt;= 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 &amp; 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 &amp; 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 &gt; 1 returns 0</p>
+ * <p>See: <a href="https://en.wikipedia.org/wiki/Bernoulli_number">Wikipedia: Bernoulli number</a></p>
+ *
+ * @param n the index of the Bernoulli number to be calculated (starting at 0)
+ * @return the Bernoulli number for the specified index
+ * @throws ArithmeticException if x is lesser than 0
+ */
+ public static BigRational bernoulli(int n) {
+ if (n < 0) {
+ throw new ArithmeticException("Illegal bernoulli(n) for n < 0: n = " + n);
+ }
+ if (n == 1) {
+ return valueOf(-1, 2);
+ } else if (n % 2 == 1) {
+ return ZERO;
+ }
+
+ synchronized (bernoulliCache) {
+ int index = n / 2;
+
+ if (bernoulliCache.size() <= index) {
+ for (int i = bernoulliCache.size(); i <= index; i++) {
+ BigRational b = calculateBernoulli(i * 2);
+ bernoulliCache.add(b);
+ }
+ }
+
+ return bernoulliCache.get(index);
+ }
+ }
+
+ private static BigRational calculateBernoulli(int n) {
+ return IntStream.rangeClosed(0, n).parallel().mapToObj(k -> {
+ BigRational jSum = ZERO ;
+ BigRational bin = ONE ;
+ for(int j=0 ; j <= k ; j++) {
+ BigRational jPowN = valueOf(j).pow(n);
+ if (j % 2 == 0) {
+ jSum = jSum.add(bin.multiply(jPowN)) ;
+ } else {
+ jSum = jSum.subtract(bin.multiply(jPowN)) ;
+ }
+
+ bin = bin.multiply(valueOf(k-j).divide(valueOf(j+1)));
+ }
+ return jSum.divide(valueOf(k+1));
+ }).reduce(ZERO, BigRational::add);
+ }
+
+}
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, () -&gt; {
+ System.out.println("Pi[5]: " + DefaultBigDecimalMath.pi());
+ DefaultBigDecimalMath.withPrecision(10, () -&gt; {
+ 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 -&gt; DefaultBigDecimalMath.cos(b))
+ .map(b -&gt; "sequential " + Thread.currentThread().getName() + " [5]: " + b)
+ .forEach(System.out::println);
+
+ BigDecimalStream.range(0.0, 1.0, 0.01, DefaultBigDecimalMath.currentMathContext())
+ .parallel()
+ .map(b -&gt; {
+ try (DefaultBigDecimalMath.LocalMathContext context2 = DefaultBigDecimalMath.createLocalMathContext(5)) {
+ return DefaultBigDecimalMath.cos(b);
+ }
+ })
+ .map(b -&gt; "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) &lt; 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) &lt;= 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: