summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math3/fraction/BigFraction.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/apache/commons/math3/fraction/BigFraction.java')
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/BigFraction.java1065
1 files changed, 1065 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math3/fraction/BigFraction.java b/src/main/java/org/apache/commons/math3/fraction/BigFraction.java
new file mode 100644
index 0000000..56616b3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/BigFraction.java
@@ -0,0 +1,1065 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fraction;
+
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.ArithmeticUtils;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Representation of a rational number without any overflow. This class is immutable.
+ *
+ * @since 2.0
+ */
+public class BigFraction extends Number
+ implements FieldElement<BigFraction>, Comparable<BigFraction>, Serializable {
+
+ /** A fraction representing "2 / 1". */
+ public static final BigFraction TWO = new BigFraction(2);
+
+ /** A fraction representing "1". */
+ public static final BigFraction ONE = new BigFraction(1);
+
+ /** A fraction representing "0". */
+ public static final BigFraction ZERO = new BigFraction(0);
+
+ /** A fraction representing "-1 / 1". */
+ public static final BigFraction MINUS_ONE = new BigFraction(-1);
+
+ /** A fraction representing "4/5". */
+ public static final BigFraction FOUR_FIFTHS = new BigFraction(4, 5);
+
+ /** A fraction representing "1/5". */
+ public static final BigFraction ONE_FIFTH = new BigFraction(1, 5);
+
+ /** A fraction representing "1/2". */
+ public static final BigFraction ONE_HALF = new BigFraction(1, 2);
+
+ /** A fraction representing "1/4". */
+ public static final BigFraction ONE_QUARTER = new BigFraction(1, 4);
+
+ /** A fraction representing "1/3". */
+ public static final BigFraction ONE_THIRD = new BigFraction(1, 3);
+
+ /** A fraction representing "3/5". */
+ public static final BigFraction THREE_FIFTHS = new BigFraction(3, 5);
+
+ /** A fraction representing "3/4". */
+ public static final BigFraction THREE_QUARTERS = new BigFraction(3, 4);
+
+ /** A fraction representing "2/5". */
+ public static final BigFraction TWO_FIFTHS = new BigFraction(2, 5);
+
+ /** A fraction representing "2/4". */
+ public static final BigFraction TWO_QUARTERS = new BigFraction(2, 4);
+
+ /** A fraction representing "2/3". */
+ public static final BigFraction TWO_THIRDS = new BigFraction(2, 3);
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -5630213147331578515L;
+
+ /** <code>BigInteger</code> representation of 100. */
+ private static final BigInteger ONE_HUNDRED = BigInteger.valueOf(100);
+
+ /** The numerator. */
+ private final BigInteger numerator;
+
+ /** The denominator. */
+ private final BigInteger denominator;
+
+ /**
+ * Create a {@link BigFraction} equivalent to the passed {@code BigInteger}, ie "num / 1".
+ *
+ * @param num the numerator.
+ */
+ public BigFraction(final BigInteger num) {
+ this(num, BigInteger.ONE);
+ }
+
+ /**
+ * Create a {@link BigFraction} given the numerator and denominator as {@code BigInteger}. The
+ * {@link BigFraction} is reduced to lowest terms.
+ *
+ * @param num the numerator, must not be {@code null}.
+ * @param den the denominator, must not be {@code null}.
+ * @throws ZeroException if the denominator is zero.
+ * @throws NullArgumentException if either of the arguments is null
+ */
+ public BigFraction(BigInteger num, BigInteger den) {
+ MathUtils.checkNotNull(num, LocalizedFormats.NUMERATOR);
+ MathUtils.checkNotNull(den, LocalizedFormats.DENOMINATOR);
+ if (den.signum() == 0) {
+ throw new ZeroException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+ if (num.signum() == 0) {
+ numerator = BigInteger.ZERO;
+ denominator = BigInteger.ONE;
+ } else {
+
+ // reduce numerator and denominator by greatest common denominator
+ final BigInteger gcd = num.gcd(den);
+ if (BigInteger.ONE.compareTo(gcd) < 0) {
+ num = num.divide(gcd);
+ den = den.divide(gcd);
+ }
+
+ // move sign to numerator
+ if (den.signum() == -1) {
+ num = num.negate();
+ den = den.negate();
+ }
+
+ // store the values in the final fields
+ numerator = num;
+ denominator = den;
+ }
+ }
+
+ /**
+ * Create a fraction given the double value.
+ *
+ * <p>This constructor behaves <em>differently</em> from {@link #BigFraction(double, double,
+ * int)}. It converts the double value exactly, considering its internal bits representation.
+ * This works for all values except NaN and infinities and does not requires any loop or
+ * convergence threshold.
+ *
+ * <p>Since this conversion is exact and since double numbers are sometimes approximated, the
+ * fraction created may seem strange in some cases. For example, calling <code>
+ * new BigFraction(1.0 / 3.0)</code> does <em>not</em> create the fraction 1/3, but the fraction
+ * 6004799503160661 / 18014398509481984 because the double number passed to the constructor is
+ * not exactly 1/3 (this number cannot be stored exactly in IEEE754).
+ *
+ * @see #BigFraction(double, double, int)
+ * @param value the double value to convert to a fraction.
+ * @exception MathIllegalArgumentException if value is NaN or infinite
+ */
+ public BigFraction(final double value) throws MathIllegalArgumentException {
+ if (Double.isNaN(value)) {
+ throw new MathIllegalArgumentException(LocalizedFormats.NAN_VALUE_CONVERSION);
+ }
+ if (Double.isInfinite(value)) {
+ throw new MathIllegalArgumentException(LocalizedFormats.INFINITE_VALUE_CONVERSION);
+ }
+
+ // compute m and k such that value = m * 2^k
+ final long bits = Double.doubleToLongBits(value);
+ final long sign = bits & 0x8000000000000000L;
+ final long exponent = bits & 0x7ff0000000000000L;
+ long m = bits & 0x000fffffffffffffL;
+ if (exponent != 0) {
+ // this was a normalized number, add the implicit most significant bit
+ m |= 0x0010000000000000L;
+ }
+ if (sign != 0) {
+ m = -m;
+ }
+ int k = ((int) (exponent >> 52)) - 1075;
+ while (((m & 0x001ffffffffffffeL) != 0) && ((m & 0x1) == 0)) {
+ m >>= 1;
+ ++k;
+ }
+
+ if (k < 0) {
+ numerator = BigInteger.valueOf(m);
+ denominator = BigInteger.ZERO.flipBit(-k);
+ } else {
+ numerator = BigInteger.valueOf(m).multiply(BigInteger.ZERO.flipBit(k));
+ denominator = BigInteger.ONE;
+ }
+ }
+
+ /**
+ * Create a fraction given the double value and maximum error allowed.
+ *
+ * <p>References:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">Continued Fraction</a>
+ * equations (11) and (22)-(26)
+ * </ul>
+ *
+ * @param value the double value to convert to a fraction.
+ * @param epsilon maximum error allowed. The resulting fraction is within <code>epsilon</code>
+ * of <code>value</code>, in absolute terms.
+ * @param maxIterations maximum number of convergents.
+ * @throws FractionConversionException if the continued fraction failed to converge.
+ * @see #BigFraction(double)
+ */
+ public BigFraction(final double value, final double epsilon, final int maxIterations)
+ throws FractionConversionException {
+ this(value, epsilon, Integer.MAX_VALUE, maxIterations);
+ }
+
+ /**
+ * Create a fraction given the double value and either the maximum error allowed or the maximum
+ * number of denominator digits.
+ *
+ * <p>NOTE: This constructor is called with EITHER - a valid epsilon value and the
+ * maxDenominator set to Integer.MAX_VALUE (that way the maxDenominator has no effect). OR - a
+ * valid maxDenominator value and the epsilon value set to zero (that way epsilon only has
+ * effect if there is an exact match before the maxDenominator value is reached).
+ *
+ * <p>It has been done this way so that the same code can be (re)used for both scenarios.
+ * However this could be confusing to users if it were part of the public API and this
+ * constructor should therefore remain PRIVATE. See JIRA issue ticket MATH-181 for more details:
+ *
+ * <p>https://issues.apache.org/jira/browse/MATH-181
+ *
+ * @param value the double value to convert to a fraction.
+ * @param epsilon maximum error allowed. The resulting fraction is within <code>epsilon</code>
+ * of <code>value</code>, in absolute terms.
+ * @param maxDenominator maximum denominator value allowed.
+ * @param maxIterations maximum number of convergents.
+ * @throws FractionConversionException if the continued fraction failed to converge.
+ */
+ private BigFraction(
+ final double value, final double epsilon, final int maxDenominator, int maxIterations)
+ throws FractionConversionException {
+ long overflow = Integer.MAX_VALUE;
+ double r0 = value;
+ long a0 = (long) FastMath.floor(r0);
+
+ if (FastMath.abs(a0) > overflow) {
+ throw new FractionConversionException(value, a0, 1l);
+ }
+
+ // check for (almost) integer arguments, which should not go
+ // to iterations.
+ if (FastMath.abs(a0 - value) < epsilon) {
+ numerator = BigInteger.valueOf(a0);
+ denominator = BigInteger.ONE;
+ return;
+ }
+
+ long p0 = 1;
+ long q0 = 0;
+ long p1 = a0;
+ long q1 = 1;
+
+ long p2 = 0;
+ long q2 = 1;
+
+ int n = 0;
+ boolean stop = false;
+ do {
+ ++n;
+ final double r1 = 1.0 / (r0 - a0);
+ final long a1 = (long) FastMath.floor(r1);
+ p2 = (a1 * p1) + p0;
+ q2 = (a1 * q1) + q0;
+ if ((p2 > overflow) || (q2 > overflow)) {
+ // in maxDenominator mode, if the last fraction was very close to the actual value
+ // q2 may overflow in the next iteration; in this case return the last one.
+ if (epsilon == 0.0 && FastMath.abs(q1) < maxDenominator) {
+ break;
+ }
+ throw new FractionConversionException(value, p2, q2);
+ }
+
+ final double convergent = (double) p2 / (double) q2;
+ if ((n < maxIterations)
+ && (FastMath.abs(convergent - value) > epsilon)
+ && (q2 < maxDenominator)) {
+ p0 = p1;
+ p1 = p2;
+ q0 = q1;
+ q1 = q2;
+ a0 = a1;
+ r0 = r1;
+ } else {
+ stop = true;
+ }
+ } while (!stop);
+
+ if (n >= maxIterations) {
+ throw new FractionConversionException(value, maxIterations);
+ }
+
+ if (q2 < maxDenominator) {
+ numerator = BigInteger.valueOf(p2);
+ denominator = BigInteger.valueOf(q2);
+ } else {
+ numerator = BigInteger.valueOf(p1);
+ denominator = BigInteger.valueOf(q1);
+ }
+ }
+
+ /**
+ * Create a fraction given the double value and maximum denominator.
+ *
+ * <p>References:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">Continued Fraction</a>
+ * equations (11) and (22)-(26)
+ * </ul>
+ *
+ * @param value the double value to convert to a fraction.
+ * @param maxDenominator The maximum allowed value for denominator.
+ * @throws FractionConversionException if the continued fraction failed to converge.
+ */
+ public BigFraction(final double value, final int maxDenominator)
+ throws FractionConversionException {
+ this(value, 0, maxDenominator, 100);
+ }
+
+ /**
+ * Create a {@link BigFraction} equivalent to the passed {@code int}, ie "num / 1".
+ *
+ * @param num the numerator.
+ */
+ public BigFraction(final int num) {
+ this(BigInteger.valueOf(num), BigInteger.ONE);
+ }
+
+ /**
+ * Create a {@link BigFraction} given the numerator and denominator as simple {@code int}. The
+ * {@link BigFraction} is reduced to lowest terms.
+ *
+ * @param num the numerator.
+ * @param den the denominator.
+ */
+ public BigFraction(final int num, final int den) {
+ this(BigInteger.valueOf(num), BigInteger.valueOf(den));
+ }
+
+ /**
+ * Create a {@link BigFraction} equivalent to the passed long, ie "num / 1".
+ *
+ * @param num the numerator.
+ */
+ public BigFraction(final long num) {
+ this(BigInteger.valueOf(num), BigInteger.ONE);
+ }
+
+ /**
+ * Create a {@link BigFraction} given the numerator and denominator as simple {@code long}. The
+ * {@link BigFraction} is reduced to lowest terms.
+ *
+ * @param num the numerator.
+ * @param den the denominator.
+ */
+ public BigFraction(final long num, final long den) {
+ this(BigInteger.valueOf(num), BigInteger.valueOf(den));
+ }
+
+ /**
+ * Creates a <code>BigFraction</code> instance with the 2 parts of a fraction Y/Z.
+ *
+ * <p>Any negative signs are resolved to be on the numerator.
+ *
+ * @param numerator the numerator, for example the three in 'three sevenths'.
+ * @param denominator the denominator, for example the seven in 'three sevenths'.
+ * @return a new fraction instance, with the numerator and denominator reduced.
+ * @throws ArithmeticException if the denominator is <code>zero</code>.
+ */
+ public static BigFraction getReducedFraction(final int numerator, final int denominator) {
+ if (numerator == 0) {
+ return ZERO; // normalize zero.
+ }
+
+ return new BigFraction(numerator, denominator);
+ }
+
+ /**
+ * Returns the absolute value of this {@link BigFraction}.
+ *
+ * @return the absolute value as a {@link BigFraction}.
+ */
+ public BigFraction abs() {
+ return (numerator.signum() == 1) ? this : negate();
+ }
+
+ /**
+ * Adds the value of this fraction to the passed {@link BigInteger}, returning the result in
+ * reduced form.
+ *
+ * @param bg the {@link BigInteger} to add, must'nt be <code>null</code>.
+ * @return a <code>BigFraction</code> instance with the resulting values.
+ * @throws NullArgumentException if the {@link BigInteger} is <code>null</code>.
+ */
+ public BigFraction add(final BigInteger bg) throws NullArgumentException {
+ MathUtils.checkNotNull(bg);
+
+ if (numerator.signum() == 0) {
+ return new BigFraction(bg);
+ }
+ if (bg.signum() == 0) {
+ return this;
+ }
+
+ return new BigFraction(numerator.add(denominator.multiply(bg)), denominator);
+ }
+
+ /**
+ * Adds the value of this fraction to the passed {@code integer}, returning the result in
+ * reduced form.
+ *
+ * @param i the {@code integer} to add.
+ * @return a <code>BigFraction</code> instance with the resulting values.
+ */
+ public BigFraction add(final int i) {
+ return add(BigInteger.valueOf(i));
+ }
+
+ /**
+ * Adds the value of this fraction to the passed {@code long}, returning the result in reduced
+ * form.
+ *
+ * @param l the {@code long} to add.
+ * @return a <code>BigFraction</code> instance with the resulting values.
+ */
+ public BigFraction add(final long l) {
+ return add(BigInteger.valueOf(l));
+ }
+
+ /**
+ * Adds the value of this fraction to another, returning the result in reduced form.
+ *
+ * @param fraction the {@link BigFraction} to add, must not be <code>null</code>.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws NullArgumentException if the {@link BigFraction} is {@code null}.
+ */
+ public BigFraction add(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (fraction.numerator.signum() == 0) {
+ return this;
+ }
+ if (numerator.signum() == 0) {
+ return fraction;
+ }
+
+ BigInteger num = null;
+ BigInteger den = null;
+
+ if (denominator.equals(fraction.denominator)) {
+ num = numerator.add(fraction.numerator);
+ den = denominator;
+ } else {
+ num =
+ (numerator.multiply(fraction.denominator))
+ .add((fraction.numerator).multiply(denominator));
+ den = denominator.multiply(fraction.denominator);
+ }
+
+ if (num.signum() == 0) {
+ return ZERO;
+ }
+
+ return new BigFraction(num, den);
+ }
+
+ /**
+ * Gets the fraction as a <code>BigDecimal</code>. This calculates the fraction as the numerator
+ * divided by denominator.
+ *
+ * @return the fraction as a <code>BigDecimal</code>.
+ * @throws ArithmeticException if the exact quotient does not have a terminating decimal
+ * expansion.
+ * @see BigDecimal
+ */
+ public BigDecimal bigDecimalValue() {
+ return new BigDecimal(numerator).divide(new BigDecimal(denominator));
+ }
+
+ /**
+ * Gets the fraction as a <code>BigDecimal</code> following the passed rounding mode. This
+ * calculates the fraction as the numerator divided by denominator.
+ *
+ * @param roundingMode rounding mode to apply. see {@link BigDecimal} constants.
+ * @return the fraction as a <code>BigDecimal</code>.
+ * @throws IllegalArgumentException if {@code roundingMode} does not represent a valid rounding
+ * mode.
+ * @see BigDecimal
+ */
+ public BigDecimal bigDecimalValue(final int roundingMode) {
+ return new BigDecimal(numerator).divide(new BigDecimal(denominator), roundingMode);
+ }
+
+ /**
+ * Gets the fraction as a <code>BigDecimal</code> following the passed scale and rounding mode.
+ * This calculates the fraction as the numerator divided by denominator.
+ *
+ * @param scale scale of the <code>BigDecimal</code> quotient to be returned. see {@link
+ * BigDecimal} for more information.
+ * @param roundingMode rounding mode to apply. see {@link BigDecimal} constants.
+ * @return the fraction as a <code>BigDecimal</code>.
+ * @see BigDecimal
+ */
+ public BigDecimal bigDecimalValue(final int scale, final int roundingMode) {
+ return new BigDecimal(numerator).divide(new BigDecimal(denominator), scale, roundingMode);
+ }
+
+ /**
+ * Compares this object to another based on size.
+ *
+ * @param object the object to compare to, must not be <code>null</code>.
+ * @return -1 if this is less than {@code object}, +1 if this is greater than {@code object}, 0
+ * if they are equal.
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(final BigFraction object) {
+ int lhsSigNum = numerator.signum();
+ int rhsSigNum = object.numerator.signum();
+
+ if (lhsSigNum != rhsSigNum) {
+ return (lhsSigNum > rhsSigNum) ? 1 : -1;
+ }
+ if (lhsSigNum == 0) {
+ return 0;
+ }
+
+ BigInteger nOd = numerator.multiply(object.denominator);
+ BigInteger dOn = denominator.multiply(object.numerator);
+ return nOd.compareTo(dOn);
+ }
+
+ /**
+ * Divide the value of this fraction by the passed {@code BigInteger}, ie {@code this * 1 / bg},
+ * returning the result in reduced form.
+ *
+ * @param bg the {@code BigInteger} to divide by, must not be {@code null}
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws NullArgumentException if the {@code BigInteger} is {@code null}
+ * @throws MathArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final BigInteger bg) {
+ if (bg == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (bg.signum() == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+ if (numerator.signum() == 0) {
+ return ZERO;
+ }
+ return new BigFraction(numerator, denominator.multiply(bg));
+ }
+
+ /**
+ * Divide the value of this fraction by the passed {@code int}, ie {@code this * 1 / i},
+ * returning the result in reduced form.
+ *
+ * @param i the {@code int} to divide by
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws MathArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final int i) {
+ return divide(BigInteger.valueOf(i));
+ }
+
+ /**
+ * Divide the value of this fraction by the passed {@code long}, ie {@code this * 1 / l},
+ * returning the result in reduced form.
+ *
+ * @param l the {@code long} to divide by
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws MathArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final long l) {
+ return divide(BigInteger.valueOf(l));
+ }
+
+ /**
+ * Divide the value of this fraction by another, returning the result in reduced form.
+ *
+ * @param fraction Fraction to divide by, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws NullArgumentException if the {@code fraction} is {@code null}.
+ * @throws MathArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (fraction.numerator.signum() == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+ if (numerator.signum() == 0) {
+ return ZERO;
+ }
+
+ return multiply(fraction.reciprocal());
+ }
+
+ /**
+ * Gets the fraction as a {@code double}. This calculates the fraction as the numerator divided
+ * by denominator.
+ *
+ * @return the fraction as a {@code double}
+ * @see java.lang.Number#doubleValue()
+ */
+ @Override
+ public double doubleValue() {
+ double result = numerator.doubleValue() / denominator.doubleValue();
+ if (Double.isNaN(result)) {
+ // Numerator and/or denominator must be out of range:
+ // Calculate how far to shift them to put them in range.
+ int shift =
+ FastMath.max(numerator.bitLength(), denominator.bitLength())
+ - FastMath.getExponent(Double.MAX_VALUE);
+ result =
+ numerator.shiftRight(shift).doubleValue()
+ / denominator.shiftRight(shift).doubleValue();
+ }
+ return result;
+ }
+
+ /**
+ * Test for the equality of two fractions. If the lowest term numerator and denominators are the
+ * same for both fractions, the two fractions are considered to be equal.
+ *
+ * @param other fraction to test for equality to this fraction, can be <code>null</code>.
+ * @return true if two fractions are equal, false if object is <code>null</code>, not an
+ * instance of {@link BigFraction}, or not equal to this fraction instance.
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object other) {
+ boolean ret = false;
+
+ if (this == other) {
+ ret = true;
+ } else if (other instanceof BigFraction) {
+ BigFraction rhs = ((BigFraction) other).reduce();
+ BigFraction thisOne = this.reduce();
+ ret =
+ thisOne.numerator.equals(rhs.numerator)
+ && thisOne.denominator.equals(rhs.denominator);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Gets the fraction as a {@code float}. This calculates the fraction as the numerator divided
+ * by denominator.
+ *
+ * @return the fraction as a {@code float}.
+ * @see java.lang.Number#floatValue()
+ */
+ @Override
+ public float floatValue() {
+ float result = numerator.floatValue() / denominator.floatValue();
+ if (Double.isNaN(result)) {
+ // Numerator and/or denominator must be out of range:
+ // Calculate how far to shift them to put them in range.
+ int shift =
+ FastMath.max(numerator.bitLength(), denominator.bitLength())
+ - FastMath.getExponent(Float.MAX_VALUE);
+ result =
+ numerator.shiftRight(shift).floatValue()
+ / denominator.shiftRight(shift).floatValue();
+ }
+ return result;
+ }
+
+ /**
+ * Access the denominator as a <code>BigInteger</code>.
+ *
+ * @return the denominator as a <code>BigInteger</code>.
+ */
+ public BigInteger getDenominator() {
+ return denominator;
+ }
+
+ /**
+ * Access the denominator as a {@code int}.
+ *
+ * @return the denominator as a {@code int}.
+ */
+ public int getDenominatorAsInt() {
+ return denominator.intValue();
+ }
+
+ /**
+ * Access the denominator as a {@code long}.
+ *
+ * @return the denominator as a {@code long}.
+ */
+ public long getDenominatorAsLong() {
+ return denominator.longValue();
+ }
+
+ /**
+ * Access the numerator as a <code>BigInteger</code>.
+ *
+ * @return the numerator as a <code>BigInteger</code>.
+ */
+ public BigInteger getNumerator() {
+ return numerator;
+ }
+
+ /**
+ * Access the numerator as a {@code int}.
+ *
+ * @return the numerator as a {@code int}.
+ */
+ public int getNumeratorAsInt() {
+ return numerator.intValue();
+ }
+
+ /**
+ * Access the numerator as a {@code long}.
+ *
+ * @return the numerator as a {@code long}.
+ */
+ public long getNumeratorAsLong() {
+ return numerator.longValue();
+ }
+
+ /**
+ * Gets a hashCode for the fraction.
+ *
+ * @return a hash code value for this object.
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return 37 * (37 * 17 + numerator.hashCode()) + denominator.hashCode();
+ }
+
+ /**
+ * Gets the fraction as an {@code int}. This returns the whole number part of the fraction.
+ *
+ * @return the whole number fraction part.
+ * @see java.lang.Number#intValue()
+ */
+ @Override
+ public int intValue() {
+ return numerator.divide(denominator).intValue();
+ }
+
+ /**
+ * Gets the fraction as a {@code long}. This returns the whole number part of the fraction.
+ *
+ * @return the whole number fraction part.
+ * @see java.lang.Number#longValue()
+ */
+ @Override
+ public long longValue() {
+ return numerator.divide(denominator).longValue();
+ }
+
+ /**
+ * Multiplies the value of this fraction by the passed <code>BigInteger</code>, returning the
+ * result in reduced form.
+ *
+ * @param bg the {@code BigInteger} to multiply by.
+ * @return a {@code BigFraction} instance with the resulting values.
+ * @throws NullArgumentException if {@code bg} is {@code null}.
+ */
+ public BigFraction multiply(final BigInteger bg) {
+ if (bg == null) {
+ throw new NullArgumentException();
+ }
+ if (numerator.signum() == 0 || bg.signum() == 0) {
+ return ZERO;
+ }
+ return new BigFraction(bg.multiply(numerator), denominator);
+ }
+
+ /**
+ * Multiply the value of this fraction by the passed {@code int}, returning the result in
+ * reduced form.
+ *
+ * @param i the {@code int} to multiply by.
+ * @return a {@link BigFraction} instance with the resulting values.
+ */
+ public BigFraction multiply(final int i) {
+ if (i == 0 || numerator.signum() == 0) {
+ return ZERO;
+ }
+
+ return multiply(BigInteger.valueOf(i));
+ }
+
+ /**
+ * Multiply the value of this fraction by the passed {@code long}, returning the result in
+ * reduced form.
+ *
+ * @param l the {@code long} to multiply by.
+ * @return a {@link BigFraction} instance with the resulting values.
+ */
+ public BigFraction multiply(final long l) {
+ if (l == 0 || numerator.signum() == 0) {
+ return ZERO;
+ }
+
+ return multiply(BigInteger.valueOf(l));
+ }
+
+ /**
+ * Multiplies the value of this fraction by another, returning the result in reduced form.
+ *
+ * @param fraction Fraction to multiply by, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws NullArgumentException if {@code fraction} is {@code null}.
+ */
+ public BigFraction multiply(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (numerator.signum() == 0 || fraction.numerator.signum() == 0) {
+ return ZERO;
+ }
+ return new BigFraction(
+ numerator.multiply(fraction.numerator), denominator.multiply(fraction.denominator));
+ }
+
+ /**
+ * Return the additive inverse of this fraction, returning the result in reduced form.
+ *
+ * @return the negation of this fraction.
+ */
+ public BigFraction negate() {
+ return new BigFraction(numerator.negate(), denominator);
+ }
+
+ /**
+ * Gets the fraction percentage as a {@code double}. This calculates the fraction as the
+ * numerator divided by denominator multiplied by 100.
+ *
+ * @return the fraction percentage as a {@code double}.
+ */
+ public double percentageValue() {
+ return multiply(ONE_HUNDRED).doubleValue();
+ }
+
+ /**
+ * Returns a {@code BigFraction} whose value is {@code (this<sup>exponent</sup>)}, returning the
+ * result in reduced form.
+ *
+ * @param exponent exponent to which this {@code BigFraction} is to be raised.
+ * @return <tt>this<sup>exponent</sup></tt>.
+ */
+ public BigFraction pow(final int exponent) {
+ if (exponent == 0) {
+ return ONE;
+ }
+ if (numerator.signum() == 0) {
+ return this;
+ }
+
+ if (exponent < 0) {
+ return new BigFraction(denominator.pow(-exponent), numerator.pow(-exponent));
+ }
+ return new BigFraction(numerator.pow(exponent), denominator.pow(exponent));
+ }
+
+ /**
+ * Returns a <code>BigFraction</code> whose value is <tt>(this<sup>exponent</sup>)</tt>,
+ * returning the result in reduced form.
+ *
+ * @param exponent exponent to which this <code>BigFraction</code> is to be raised.
+ * @return <tt>this<sup>exponent</sup></tt> as a <code>BigFraction</code>.
+ */
+ public BigFraction pow(final long exponent) {
+ if (exponent == 0) {
+ return ONE;
+ }
+ if (numerator.signum() == 0) {
+ return this;
+ }
+
+ if (exponent < 0) {
+ return new BigFraction(
+ ArithmeticUtils.pow(denominator, -exponent),
+ ArithmeticUtils.pow(numerator, -exponent));
+ }
+ return new BigFraction(
+ ArithmeticUtils.pow(numerator, exponent),
+ ArithmeticUtils.pow(denominator, exponent));
+ }
+
+ /**
+ * Returns a <code>BigFraction</code> whose value is <tt>(this<sup>exponent</sup>)</tt>,
+ * returning the result in reduced form.
+ *
+ * @param exponent exponent to which this <code>BigFraction</code> is to be raised.
+ * @return <tt>this<sup>exponent</sup></tt> as a <code>BigFraction</code>.
+ */
+ public BigFraction pow(final BigInteger exponent) {
+ if (exponent.signum() == 0) {
+ return ONE;
+ }
+ if (numerator.signum() == 0) {
+ return this;
+ }
+
+ if (exponent.signum() == -1) {
+ final BigInteger eNeg = exponent.negate();
+ return new BigFraction(
+ ArithmeticUtils.pow(denominator, eNeg), ArithmeticUtils.pow(numerator, eNeg));
+ }
+ return new BigFraction(
+ ArithmeticUtils.pow(numerator, exponent),
+ ArithmeticUtils.pow(denominator, exponent));
+ }
+
+ /**
+ * Returns a <code>double</code> whose value is <tt>(this<sup>exponent</sup>)</tt>, returning
+ * the result in reduced form.
+ *
+ * @param exponent exponent to which this <code>BigFraction</code> is to be raised.
+ * @return <tt>this<sup>exponent</sup></tt>.
+ */
+ public double pow(final double exponent) {
+ return FastMath.pow(numerator.doubleValue(), exponent)
+ / FastMath.pow(denominator.doubleValue(), exponent);
+ }
+
+ /**
+ * Return the multiplicative inverse of this fraction.
+ *
+ * @return the reciprocal fraction.
+ */
+ public BigFraction reciprocal() {
+ return new BigFraction(denominator, numerator);
+ }
+
+ /**
+ * Reduce this <code>BigFraction</code> to its lowest terms.
+ *
+ * @return the reduced <code>BigFraction</code>. It doesn't change anything if the fraction can
+ * be reduced.
+ */
+ public BigFraction reduce() {
+ final BigInteger gcd = numerator.gcd(denominator);
+
+ if (BigInteger.ONE.compareTo(gcd) < 0) {
+ return new BigFraction(numerator.divide(gcd), denominator.divide(gcd));
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Subtracts the value of an {@link BigInteger} from the value of this {@code BigFraction},
+ * returning the result in reduced form.
+ *
+ * @param bg the {@link BigInteger} to subtract, cannot be {@code null}.
+ * @return a {@code BigFraction} instance with the resulting values.
+ * @throws NullArgumentException if the {@link BigInteger} is {@code null}.
+ */
+ public BigFraction subtract(final BigInteger bg) {
+ if (bg == null) {
+ throw new NullArgumentException();
+ }
+ if (bg.signum() == 0) {
+ return this;
+ }
+ if (numerator.signum() == 0) {
+ return new BigFraction(bg.negate());
+ }
+
+ return new BigFraction(numerator.subtract(denominator.multiply(bg)), denominator);
+ }
+
+ /**
+ * Subtracts the value of an {@code integer} from the value of this {@code BigFraction},
+ * returning the result in reduced form.
+ *
+ * @param i the {@code integer} to subtract.
+ * @return a {@code BigFraction} instance with the resulting values.
+ */
+ public BigFraction subtract(final int i) {
+ return subtract(BigInteger.valueOf(i));
+ }
+
+ /**
+ * Subtracts the value of a {@code long} from the value of this {@code BigFraction}, returning
+ * the result in reduced form.
+ *
+ * @param l the {@code long} to subtract.
+ * @return a {@code BigFraction} instance with the resulting values.
+ */
+ public BigFraction subtract(final long l) {
+ return subtract(BigInteger.valueOf(l));
+ }
+
+ /**
+ * Subtracts the value of another fraction from the value of this one, returning the result in
+ * reduced form.
+ *
+ * @param fraction {@link BigFraction} to subtract, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws NullArgumentException if the {@code fraction} is {@code null}.
+ */
+ public BigFraction subtract(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (fraction.numerator.signum() == 0) {
+ return this;
+ }
+ if (numerator.signum() == 0) {
+ return fraction.negate();
+ }
+
+ BigInteger num = null;
+ BigInteger den = null;
+ if (denominator.equals(fraction.denominator)) {
+ num = numerator.subtract(fraction.numerator);
+ den = denominator;
+ } else {
+ num =
+ (numerator.multiply(fraction.denominator))
+ .subtract((fraction.numerator).multiply(denominator));
+ den = denominator.multiply(fraction.denominator);
+ }
+ return new BigFraction(num, den);
+ }
+
+ /**
+ * Returns the <code>String</code> representing this fraction, ie "num / dem" or just "num" if
+ * the denominator is one.
+ *
+ * @return a string representation of the fraction.
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ String str = null;
+ if (BigInteger.ONE.equals(denominator)) {
+ str = numerator.toString();
+ } else if (BigInteger.ZERO.equals(numerator)) {
+ str = "0";
+ } else {
+ str = numerator + " / " + denominator;
+ }
+ return str;
+ }
+
+ /** {@inheritDoc} */
+ public BigFractionField getField() {
+ return BigFractionField.getInstance();
+ }
+}