Merge branch 'fraction__NUMBERS-6'
Copy fraction code from commons-math into commons-numbers
diff --git a/commons-numbers-complex/pom.xml b/commons-numbers-complex/pom.xml
index 735348f..3fec4ba 100644
--- a/commons-numbers-complex/pom.xml
+++ b/commons-numbers-complex/pom.xml
@@ -46,7 +46,6 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-numbers-core</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
diff --git a/commons-numbers-core/pom.xml b/commons-numbers-core/pom.xml
index 39dbdcb..76c2512 100644
--- a/commons-numbers-core/pom.xml
+++ b/commons-numbers-core/pom.xml
@@ -42,5 +42,20 @@
<numbers.parent.dir>${basedir}/..</numbers.parent.dir>
</properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
</project>
diff --git a/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/ArithmeticUtils.java b/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/ArithmeticUtils.java
new file mode 100644
index 0000000..13055f2
--- /dev/null
+++ b/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/ArithmeticUtils.java
@@ -0,0 +1,781 @@
+/*
+ * 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.numbers.core;
+
+import java.math.BigInteger;
+import java.text.MessageFormat;
+
+/**
+ * Some useful, arithmetics related, additions to the built-in functions in
+ * {@link Math}.
+ *
+ */
+public final class ArithmeticUtils {
+
+ /** Private constructor. */
+ private ArithmeticUtils() {
+ super();
+ }
+
+ /**
+ * Add two integers, checking for overflow.
+ *
+ * @param x an addend
+ * @param y an addend
+ * @return the sum {@code x+y}
+ * @throws ArithmeticException if the result can not be represented
+ * as an {@code int}.
+ */
+ public static int addAndCheck(int x, int y) {
+ long s = (long)x + (long)y;
+ if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) {
+ throw new NumbersArithmeticException("overflow in addition: {0} + {1}", x, y);
+ }
+ return (int)s;
+ }
+
+ /**
+ * Add two long integers, checking for overflow.
+ *
+ * @param a an addend
+ * @param b an addend
+ * @return the sum {@code a+b}
+ * @throws ArithmeticException if the result can not be represented as an long
+ */
+ public static long addAndCheck(long a, long b) {
+ return addAndCheck(a, b, "overflow in addition: {0} + {1}");
+ }
+
+ /**
+ * Computes the greatest common divisor of the absolute value of two
+ * numbers, using a modified version of the "binary gcd" method.
+ * See Knuth 4.5.2 algorithm B.
+ * The algorithm is due to Josef Stein (1961).
+ * <br>
+ * Special cases:
+ * <ul>
+ * <li>The invocations
+ * {@code gcd(Integer.MIN_VALUE, Integer.MIN_VALUE)},
+ * {@code gcd(Integer.MIN_VALUE, 0)} and
+ * {@code gcd(0, Integer.MIN_VALUE)} throw an
+ * {@code ArithmeticException}, because the result would be 2^31, which
+ * is too large for an int value.</li>
+ * <li>The result of {@code gcd(x, x)}, {@code gcd(0, x)} and
+ * {@code gcd(x, 0)} is the absolute value of {@code x}, except
+ * for the special cases above.</li>
+ * <li>The invocation {@code gcd(0, 0)} is the only one which returns
+ * {@code 0}.</li>
+ * </ul>
+ *
+ * @param p Number.
+ * @param q Number.
+ * @return the greatest common divisor (never negative).
+ * @throws ArithmeticException if the result cannot be represented as
+ * a non-negative {@code int} value.
+ */
+ public static int gcd(int p, int q) {
+ int a = p;
+ int b = q;
+ if (a == 0 ||
+ b == 0) {
+ if (a == Integer.MIN_VALUE ||
+ b == Integer.MIN_VALUE) {
+ throw new NumbersArithmeticException("overflow: gcd({0}, {1}) is 2^31",
+ p, q);
+ }
+ return Math.abs(a + b);
+ }
+
+ long al = a;
+ long bl = b;
+ boolean useLong = false;
+ if (a < 0) {
+ if(Integer.MIN_VALUE == a) {
+ useLong = true;
+ } else {
+ a = -a;
+ }
+ al = -al;
+ }
+ if (b < 0) {
+ if (Integer.MIN_VALUE == b) {
+ useLong = true;
+ } else {
+ b = -b;
+ }
+ bl = -bl;
+ }
+ if (useLong) {
+ if(al == bl) {
+ throw new NumbersArithmeticException("overflow: gcd({0}, {1}) is 2^31",
+ p, q);
+ }
+ long blbu = bl;
+ bl = al;
+ al = blbu % al;
+ if (al == 0) {
+ if (bl > Integer.MAX_VALUE) {
+ throw new NumbersArithmeticException("overflow: gcd({0}, {1}) is 2^31",
+ p, q);
+ }
+ return (int) bl;
+ }
+ blbu = bl;
+
+ // Now "al" and "bl" fit in an "int".
+ b = (int) al;
+ a = (int) (blbu % al);
+ }
+
+ return gcdPositive(a, b);
+ }
+
+ /**
+ * Computes the greatest common divisor of two <em>positive</em> numbers
+ * (this precondition is <em>not</em> checked and the result is undefined
+ * if not fulfilled) using the "binary gcd" method which avoids division
+ * and modulo operations.
+ * See Knuth 4.5.2 algorithm B.
+ * The algorithm is due to Josef Stein (1961).
+ * <br/>
+ * Special cases:
+ * <ul>
+ * <li>The result of {@code gcd(x, x)}, {@code gcd(0, x)} and
+ * {@code gcd(x, 0)} is the value of {@code x}.</li>
+ * <li>The invocation {@code gcd(0, 0)} is the only one which returns
+ * {@code 0}.</li>
+ * </ul>
+ *
+ * @param a Positive number.
+ * @param b Positive number.
+ * @return the greatest common divisor.
+ */
+ private static int gcdPositive(int a, int b) {
+ if (a == 0) {
+ return b;
+ }
+ else if (b == 0) {
+ return a;
+ }
+
+ // Make "a" and "b" odd, keeping track of common power of 2.
+ final int aTwos = Integer.numberOfTrailingZeros(a);
+ a >>= aTwos;
+ final int bTwos = Integer.numberOfTrailingZeros(b);
+ b >>= bTwos;
+ final int shift = Math.min(aTwos, bTwos);
+
+ // "a" and "b" are positive.
+ // If a > b then "gdc(a, b)" is equal to "gcd(a - b, b)".
+ // If a < b then "gcd(a, b)" is equal to "gcd(b - a, a)".
+ // Hence, in the successive iterations:
+ // "a" becomes the absolute difference of the current values,
+ // "b" becomes the minimum of the current values.
+ while (a != b) {
+ final int delta = a - b;
+ b = Math.min(a, b);
+ a = Math.abs(delta);
+
+ // Remove any power of 2 in "a" ("b" is guaranteed to be odd).
+ a >>= Integer.numberOfTrailingZeros(a);
+ }
+
+ // Recover the common power of 2.
+ return a << shift;
+ }
+
+ /**
+ * <p>
+ * Gets the greatest common divisor of the absolute value of two numbers,
+ * using the "binary gcd" method which avoids division and modulo
+ * operations. See Knuth 4.5.2 algorithm B. This algorithm is due to Josef
+ * Stein (1961).
+ * </p>
+ * Special cases:
+ * <ul>
+ * <li>The invocations
+ * {@code gcd(Long.MIN_VALUE, Long.MIN_VALUE)},
+ * {@code gcd(Long.MIN_VALUE, 0L)} and
+ * {@code gcd(0L, Long.MIN_VALUE)} throw an
+ * {@code ArithmeticException}, because the result would be 2^63, which
+ * is too large for a long value.</li>
+ * <li>The result of {@code gcd(x, x)}, {@code gcd(0L, x)} and
+ * {@code gcd(x, 0L)} is the absolute value of {@code x}, except
+ * for the special cases above.
+ * <li>The invocation {@code gcd(0L, 0L)} is the only one which returns
+ * {@code 0L}.</li>
+ * </ul>
+ *
+ * @param p Number.
+ * @param q Number.
+ * @return the greatest common divisor, never negative.
+ * @throws ArithmeticException if the result cannot be represented as
+ * a non-negative {@code long} value.
+ */
+ public static long gcd(final long p, final long q) {
+ long u = p;
+ long v = q;
+ if ((u == 0) || (v == 0)) {
+ if ((u == Long.MIN_VALUE) || (v == Long.MIN_VALUE)){
+ throw new NumbersArithmeticException("overflow: gcd({0}, {1}) is 2^63",
+ p, q);
+ }
+ return Math.abs(u) + Math.abs(v);
+ }
+ // keep u and v negative, as negative integers range down to
+ // -2^63, while positive numbers can only be as large as 2^63-1
+ // (i.e. we can't necessarily negate a negative number without
+ // overflow)
+ /* assert u!=0 && v!=0; */
+ if (u > 0) {
+ u = -u;
+ } // make u negative
+ if (v > 0) {
+ v = -v;
+ } // make v negative
+ // B1. [Find power of 2]
+ int k = 0;
+ while ((u & 1) == 0 && (v & 1) == 0 && k < 63) { // while u and v are
+ // both even...
+ u /= 2;
+ v /= 2;
+ k++; // cast out twos.
+ }
+ if (k == 63) {
+ throw new NumbersArithmeticException("overflow: gcd({0}, {1}) is 2^63",
+ p, q);
+ }
+ // B2. Initialize: u and v have been divided by 2^k and at least
+ // one is odd.
+ long t = ((u & 1) == 1) ? v : -(u / 2)/* B3 */;
+ // t negative: u was odd, v may be even (t replaces v)
+ // t positive: u was even, v is odd (t replaces u)
+ do {
+ /* assert u<0 && v<0; */
+ // B4/B3: cast out twos from t.
+ while ((t & 1) == 0) { // while t is even..
+ t /= 2; // cast out twos
+ }
+ // B5 [reset max(u,v)]
+ if (t > 0) {
+ u = -t;
+ } else {
+ v = t;
+ }
+ // B6/B3. at this point both u and v should be odd.
+ t = (v - u) / 2;
+ // |u| larger: t positive (replace u)
+ // |v| larger: t negative (replace v)
+ } while (t != 0);
+ return -u * (1L << k); // gcd is u*2^k
+ }
+
+ /**
+ * <p>
+ * Returns the least common multiple of the absolute value of two numbers,
+ * using the formula {@code lcm(a,b) = (a / gcd(a,b)) * b}.
+ * </p>
+ * Special cases:
+ * <ul>
+ * <li>The invocations {@code lcm(Integer.MIN_VALUE, n)} and
+ * {@code lcm(n, Integer.MIN_VALUE)}, where {@code abs(n)} is a
+ * power of 2, throw an {@code ArithmeticException}, because the result
+ * would be 2^31, which is too large for an int value.</li>
+ * <li>The result of {@code lcm(0, x)} and {@code lcm(x, 0)} is
+ * {@code 0} for any {@code x}.
+ * </ul>
+ *
+ * @param a Number.
+ * @param b Number.
+ * @return the least common multiple, never negative.
+ * @throws ArithmeticException if the result cannot be represented as
+ * a non-negative {@code int} value.
+ */
+ public static int lcm(int a, int b) {
+ if (a == 0 || b == 0){
+ return 0;
+ }
+ int lcm = Math.abs(ArithmeticUtils.mulAndCheck(a / gcd(a, b), b));
+ if (lcm == Integer.MIN_VALUE) {
+ throw new NumbersArithmeticException("overflow: lcm({0}, {1}) is 2^31",
+ a, b);
+ }
+ return lcm;
+ }
+
+ /**
+ * <p>
+ * Returns the least common multiple of the absolute value of two numbers,
+ * using the formula {@code lcm(a,b) = (a / gcd(a,b)) * b}.
+ * </p>
+ * Special cases:
+ * <ul>
+ * <li>The invocations {@code lcm(Long.MIN_VALUE, n)} and
+ * {@code lcm(n, Long.MIN_VALUE)}, where {@code abs(n)} is a
+ * power of 2, throw an {@code ArithmeticException}, because the result
+ * would be 2^63, which is too large for an int value.</li>
+ * <li>The result of {@code lcm(0L, x)} and {@code lcm(x, 0L)} is
+ * {@code 0L} for any {@code x}.
+ * </ul>
+ *
+ * @param a Number.
+ * @param b Number.
+ * @return the least common multiple, never negative.
+ * @throws ArithmeticException if the result cannot be represented
+ * as a non-negative {@code long} value.
+ */
+ public static long lcm(long a, long b) {
+ if (a == 0 || b == 0){
+ return 0;
+ }
+ long lcm = Math.abs(ArithmeticUtils.mulAndCheck(a / gcd(a, b), b));
+ if (lcm == Long.MIN_VALUE){
+ throw new NumbersArithmeticException("overflow: lcm({0}, {1}) is 2^63",
+ a, b);
+ }
+ return lcm;
+ }
+
+ /**
+ * Multiply two integers, checking for overflow.
+ *
+ * @param x Factor.
+ * @param y Factor.
+ * @return the product {@code x * y}.
+ * @throws ArithmeticException if the result can not be
+ * represented as an {@code int}.
+ */
+ public static int mulAndCheck(int x, int y) {
+ long m = ((long)x) * ((long)y);
+ if (m < Integer.MIN_VALUE || m > Integer.MAX_VALUE) {
+ throw new NumbersArithmeticException();
+ }
+ return (int)m;
+ }
+
+ /**
+ * Multiply two long integers, checking for overflow.
+ *
+ * @param a Factor.
+ * @param b Factor.
+ * @return the product {@code a * b}.
+ * @throws ArithmeticException if the result can not be represented
+ * as a {@code long}.
+ */
+ public static long mulAndCheck(long a, long b) {
+ long ret;
+ if (a > b) {
+ // use symmetry to reduce boundary cases
+ ret = mulAndCheck(b, a);
+ } else {
+ if (a < 0) {
+ if (b < 0) {
+ // check for positive overflow with negative a, negative b
+ if (a >= Long.MAX_VALUE / b) {
+ ret = a * b;
+ } else {
+ throw new NumbersArithmeticException();
+ }
+ } else if (b > 0) {
+ // check for negative overflow with negative a, positive b
+ if (Long.MIN_VALUE / b <= a) {
+ ret = a * b;
+ } else {
+ throw new NumbersArithmeticException();
+
+ }
+ } else {
+ // assert b == 0
+ ret = 0;
+ }
+ } else if (a > 0) {
+ // assert a > 0
+ // assert b > 0
+
+ // check for positive overflow with positive a, positive b
+ if (a <= Long.MAX_VALUE / b) {
+ ret = a * b;
+ } else {
+ throw new NumbersArithmeticException();
+ }
+ } else {
+ // assert a == 0
+ ret = 0;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Subtract two integers, checking for overflow.
+ *
+ * @param x Minuend.
+ * @param y Subtrahend.
+ * @return the difference {@code x - y}.
+ * @throws ArithmeticException if the result can not be represented
+ * as an {@code int}.
+ */
+ public static int subAndCheck(int x, int y) {
+ long s = (long)x - (long)y;
+ if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) {
+ throw new NumbersArithmeticException("overflow in subtraction: {0} - {1}", x, y);
+ }
+ return (int)s;
+ }
+
+ /**
+ * Subtract two long integers, checking for overflow.
+ *
+ * @param a Value.
+ * @param b Value.
+ * @return the difference {@code a - b}.
+ * @throws ArithmeticException if the result can not be represented as a
+ * {@code long}.
+ */
+ public static long subAndCheck(long a, long b) {
+ long ret;
+ if (b == Long.MIN_VALUE) {
+ if (a < 0) {
+ ret = a - b;
+ } else {
+ throw new NumbersArithmeticException("overflow in subtraction: {0} + {1}", a, -b);
+ }
+ } else {
+ // use additive inverse
+ ret = addAndCheck(a, -b, "overflow in subtraction: {0} + {1}");
+ }
+ return ret;
+ }
+
+ /**
+ * Raise an int to an int power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return \( k^e \)
+ * @throws IllegalArgumentException if {@code e < 0}.
+ * @throws ArithmeticException if the result would overflow.
+ */
+ public static int pow(final int k,
+ final int e) {
+ if (e < 0) {
+ throw new IllegalArgumentException("negative exponent ({" + e + "})");
+ }
+
+ int exp = e;
+ int result = 1;
+ int k2p = k;
+ while (true) {
+ if ((exp & 0x1) != 0) {
+ result = mulAndCheck(result, k2p);
+ }
+
+ exp >>= 1;
+ if (exp == 0) {
+ break;
+ }
+
+ k2p = mulAndCheck(k2p, k2p);
+ }
+
+ return result;
+ }
+
+ /**
+ * Raise a long to an int power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return \( k^e \)
+ * @throws IllegalArgumentException if {@code e < 0}.
+ * @throws ArithmeticException if the result would overflow.
+ */
+ public static long pow(final long k,
+ final int e) {
+ if (e < 0) {
+ throw new IllegalArgumentException("negative exponent ({" + e + "})");
+ }
+
+ int exp = e;
+ long result = 1;
+ long k2p = k;
+ while (true) {
+ if ((exp & 0x1) != 0) {
+ result = mulAndCheck(result, k2p);
+ }
+
+ exp >>= 1;
+ if (exp == 0) {
+ break;
+ }
+
+ k2p = mulAndCheck(k2p, k2p);
+ }
+
+ return result;
+ }
+
+ /**
+ * Raise a BigInteger to an int power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return k<sup>e</sup>
+ * @throws IllegalArgumentException if {@code e < 0}.
+ */
+ public static BigInteger pow(final BigInteger k, int e) {
+ if (e < 0) {
+ throw new IllegalArgumentException("negative exponent ({" + e + "})");
+ }
+
+ return k.pow(e);
+ }
+
+ /**
+ * Raise a BigInteger to a long power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return k<sup>e</sup>
+ * @throws IllegalArgumentException if {@code e < 0}.
+ */
+ public static BigInteger pow(final BigInteger k, long e) {
+ if (e < 0) {
+ throw new IllegalArgumentException("negative exponent ({" + e + "})");
+ }
+
+ BigInteger result = BigInteger.ONE;
+ BigInteger k2p = k;
+ while (e != 0) {
+ if ((e & 0x1) != 0) {
+ result = result.multiply(k2p);
+ }
+ k2p = k2p.multiply(k2p);
+ e >>= 1;
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Raise a BigInteger to a BigInteger power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return k<sup>e</sup>
+ * @throws IllegalArgumentException if {@code e < 0}.
+ */
+ public static BigInteger pow(final BigInteger k, BigInteger e) {
+ if (e.compareTo(BigInteger.ZERO) < 0) {
+ throw new IllegalArgumentException("negative exponent ({" + e + "})");
+ }
+
+ BigInteger result = BigInteger.ONE;
+ BigInteger k2p = k;
+ while (!BigInteger.ZERO.equals(e)) {
+ if (e.testBit(0)) {
+ result = result.multiply(k2p);
+ }
+ k2p = k2p.multiply(k2p);
+ e = e.shiftRight(1);
+ }
+
+ return result;
+ }
+
+ /**
+ * Add two long integers, checking for overflow.
+ *
+ * @param a Addend.
+ * @param b Addend.
+ * @param pattern Pattern to use for any thrown exception.
+ * @return the sum {@code a + b}.
+ * @throws ArithmeticException if the result cannot be represented
+ * as a {@code long}.
+ */
+ private static long addAndCheck(long a, long b, String message) {
+ final long result = a + b;
+ if (!((a ^ b) < 0 || (a ^ result) >= 0)) {
+ throw new NumbersArithmeticException(message, a, b);
+ }
+ return result;
+ }
+
+ /**
+ * Returns true if the argument is a power of two.
+ *
+ * @param n the number to test
+ * @return true if the argument is a power of two
+ */
+ public static boolean isPowerOfTwo(long n) {
+ return (n > 0) && ((n & (n - 1)) == 0);
+ }
+
+ /**
+ * Returns the unsigned remainder from dividing the first argument
+ * by the second where each argument and the result is interpreted
+ * as an unsigned value.
+ * <p>This method does not use the {@code long} datatype.</p>
+ *
+ * @param dividend the value to be divided
+ * @param divisor the value doing the dividing
+ * @return the unsigned remainder of the first argument divided by
+ * the second argument.
+ */
+ public static int remainderUnsigned(int dividend, int divisor) {
+ if (divisor >= 0) {
+ if (dividend >= 0) {
+ return dividend % divisor;
+ }
+ // The implementation is a Java port of algorithm described in the book
+ // "Hacker's Delight" (section "Unsigned short division from signed division").
+ int q = ((dividend >>> 1) / divisor) << 1;
+ dividend -= q * divisor;
+ if (dividend < 0 || dividend >= divisor) {
+ dividend -= divisor;
+ }
+ return dividend;
+ }
+ return dividend >= 0 || dividend < divisor ? dividend : dividend - divisor;
+ }
+
+ /**
+ * Returns the unsigned remainder from dividing the first argument
+ * by the second where each argument and the result is interpreted
+ * as an unsigned value.
+ * <p>This method does not use the {@code BigInteger} datatype.</p>
+ *
+ * @param dividend the value to be divided
+ * @param divisor the value doing the dividing
+ * @return the unsigned remainder of the first argument divided by
+ * the second argument.
+ */
+ public static long remainderUnsigned(long dividend, long divisor) {
+ if (divisor >= 0L) {
+ if (dividend >= 0L) {
+ return dividend % divisor;
+ }
+ // The implementation is a Java port of algorithm described in the book
+ // "Hacker's Delight" (section "Unsigned short division from signed division").
+ long q = ((dividend >>> 1) / divisor) << 1;
+ dividend -= q * divisor;
+ if (dividend < 0L || dividend >= divisor) {
+ dividend -= divisor;
+ }
+ return dividend;
+ }
+ return dividend >= 0L || dividend < divisor ? dividend : dividend - divisor;
+ }
+
+ /**
+ * Returns the unsigned quotient of dividing the first argument by
+ * the second where each argument and the result is interpreted as
+ * an unsigned value.
+ * <p>Note that in two's complement arithmetic, the three other
+ * basic arithmetic operations of add, subtract, and multiply are
+ * bit-wise identical if the two operands are regarded as both
+ * being signed or both being unsigned. Therefore separate {@code
+ * addUnsigned}, etc. methods are not provided.</p>
+ * <p>This method does not use the {@code long} datatype.</p>
+ *
+ * @param dividend the value to be divided
+ * @param divisor the value doing the dividing
+ * @return the unsigned quotient of the first argument divided by
+ * the second argument
+ */
+ public static int divideUnsigned(int dividend, int divisor) {
+ if (divisor >= 0) {
+ if (dividend >= 0) {
+ return dividend / divisor;
+ }
+ // The implementation is a Java port of algorithm described in the book
+ // "Hacker's Delight" (section "Unsigned short division from signed division").
+ int q = ((dividend >>> 1) / divisor) << 1;
+ dividend -= q * divisor;
+ if (dividend < 0L || dividend >= divisor) {
+ q++;
+ }
+ return q;
+ }
+ return dividend >= 0 || dividend < divisor ? 0 : 1;
+ }
+
+ /**
+ * Returns the unsigned quotient of dividing the first argument by
+ * the second where each argument and the result is interpreted as
+ * an unsigned value.
+ * <p>Note that in two's complement arithmetic, the three other
+ * basic arithmetic operations of add, subtract, and multiply are
+ * bit-wise identical if the two operands are regarded as both
+ * being signed or both being unsigned. Therefore separate {@code
+ * addUnsigned}, etc. methods are not provided.</p>
+ * <p>This method does not use the {@code BigInteger} datatype.</p>
+ *
+ * @param dividend the value to be divided
+ * @param divisor the value doing the dividing
+ * @return the unsigned quotient of the first argument divided by
+ * the second argument.
+ */
+ public static long divideUnsigned(long dividend, long divisor) {
+ if (divisor >= 0L) {
+ if (dividend >= 0L) {
+ return dividend / divisor;
+ }
+ // The implementation is a Java port of algorithm described in the book
+ // "Hacker's Delight" (section "Unsigned short division from signed division").
+ long q = ((dividend >>> 1) / divisor) << 1;
+ dividend -= q * divisor;
+ if (dividend < 0L || dividend >= divisor) {
+ q++;
+ }
+ return q;
+ }
+ return dividend >= 0L || dividend < divisor ? 0L : 1L;
+ }
+
+ private static class NumbersArithmeticException extends ArithmeticException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780474L;
+
+ private final Object[] formatArguments;
+
+ /**
+ * Default constructor.
+ */
+ public NumbersArithmeticException() {
+ this("arithmetic exception");
+ }
+
+ /**
+ * Constructor with a specific message.
+ *
+ * @param message Message pattern providing the specific context of
+ * the error.
+ * @param args Arguments.
+ */
+ public NumbersArithmeticException(String message, Object ... args) {
+ super(message);
+ this.formatArguments = args;
+ }
+
+ @Override
+ public String getMessage() {
+ return MessageFormat.format(super.getMessage(), formatArguments);
+ }
+ }
+}
diff --git a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/ArithmeticUtilsTest.java b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/ArithmeticUtilsTest.java
new file mode 100644
index 0000000..a7185da
--- /dev/null
+++ b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/ArithmeticUtilsTest.java
@@ -0,0 +1,782 @@
+/*
+ * 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.numbers.core;
+
+import java.util.Arrays;
+import java.math.BigInteger;
+import java.util.Collections;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test cases for the {@link ArithmeticUtils} class.
+ *
+ */
+public class ArithmeticUtilsTest {
+
+ @Test
+ public void testAddAndCheck() {
+ int big = Integer.MAX_VALUE;
+ int bigNeg = Integer.MIN_VALUE;
+ Assert.assertEquals(big, ArithmeticUtils.addAndCheck(big, 0));
+ try {
+ ArithmeticUtils.addAndCheck(big, 1);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ try {
+ ArithmeticUtils.addAndCheck(bigNeg, -1);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ }
+
+ @Test
+ public void testAddAndCheckLong() {
+ long max = Long.MAX_VALUE;
+ long min = Long.MIN_VALUE;
+ Assert.assertEquals(max, ArithmeticUtils.addAndCheck(max, 0L));
+ Assert.assertEquals(min, ArithmeticUtils.addAndCheck(min, 0L));
+ Assert.assertEquals(max, ArithmeticUtils.addAndCheck(0L, max));
+ Assert.assertEquals(min, ArithmeticUtils.addAndCheck(0L, min));
+ Assert.assertEquals(1, ArithmeticUtils.addAndCheck(-1L, 2L));
+ Assert.assertEquals(1, ArithmeticUtils.addAndCheck(2L, -1L));
+ Assert.assertEquals(-3, ArithmeticUtils.addAndCheck(-2L, -1L));
+ Assert.assertEquals(min, ArithmeticUtils.addAndCheck(min + 1, -1L));
+ Assert.assertEquals(-1, ArithmeticUtils.addAndCheck(min, max));
+ testAddAndCheckLongFailure(max, 1L);
+ testAddAndCheckLongFailure(min, -1L);
+ testAddAndCheckLongFailure(1L, max);
+ testAddAndCheckLongFailure(-1L, min);
+ testAddAndCheckLongFailure(max, max);
+ testAddAndCheckLongFailure(min, min);
+ }
+
+ @Test
+ public void testGcd() {
+ int a = 30;
+ int b = 50;
+ int c = 77;
+
+ Assert.assertEquals(0, ArithmeticUtils.gcd(0, 0));
+
+ Assert.assertEquals(b, ArithmeticUtils.gcd(0, b));
+ Assert.assertEquals(a, ArithmeticUtils.gcd(a, 0));
+ Assert.assertEquals(b, ArithmeticUtils.gcd(0, -b));
+ Assert.assertEquals(a, ArithmeticUtils.gcd(-a, 0));
+
+ Assert.assertEquals(10, ArithmeticUtils.gcd(a, b));
+ Assert.assertEquals(10, ArithmeticUtils.gcd(-a, b));
+ Assert.assertEquals(10, ArithmeticUtils.gcd(a, -b));
+ Assert.assertEquals(10, ArithmeticUtils.gcd(-a, -b));
+
+ Assert.assertEquals(1, ArithmeticUtils.gcd(a, c));
+ Assert.assertEquals(1, ArithmeticUtils.gcd(-a, c));
+ Assert.assertEquals(1, ArithmeticUtils.gcd(a, -c));
+ Assert.assertEquals(1, ArithmeticUtils.gcd(-a, -c));
+
+ Assert.assertEquals(3 * (1<<15), ArithmeticUtils.gcd(3 * (1<<20), 9 * (1<<15)));
+
+ Assert.assertEquals(Integer.MAX_VALUE, ArithmeticUtils.gcd(Integer.MAX_VALUE, 0));
+ Assert.assertEquals(Integer.MAX_VALUE, ArithmeticUtils.gcd(-Integer.MAX_VALUE, 0));
+ Assert.assertEquals(1<<30, ArithmeticUtils.gcd(1<<30, -Integer.MIN_VALUE));
+ try {
+ // gcd(Integer.MIN_VALUE, 0) > Integer.MAX_VALUE
+ ArithmeticUtils.gcd(Integer.MIN_VALUE, 0);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ try {
+ // gcd(0, Integer.MIN_VALUE) > Integer.MAX_VALUE
+ ArithmeticUtils.gcd(0, Integer.MIN_VALUE);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ try {
+ // gcd(Integer.MIN_VALUE, Integer.MIN_VALUE) > Integer.MAX_VALUE
+ ArithmeticUtils.gcd(Integer.MIN_VALUE, Integer.MIN_VALUE);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testGcdConsistency() {
+ // Use Integer to prevent varargs vs array issue with Arrays.asList
+ Integer[] primeList = {19, 23, 53, 67, 73, 79, 101, 103, 111, 131};
+
+ for (int i = 0; i < 20; i++) {
+ Collections.shuffle(Arrays.asList(primeList));
+ int p1 = primeList[0];
+ int p2 = primeList[1];
+ int p3 = primeList[2];
+ int p4 = primeList[3];
+ int i1 = p1 * p2 * p3;
+ int i2 = p1 * p2 * p4;
+ int gcd = p1 * p2;
+ Assert.assertEquals(gcd, ArithmeticUtils.gcd(i1, i2));
+ long l1 = i1;
+ long l2 = i2;
+ Assert.assertEquals(gcd, ArithmeticUtils.gcd(l1, l2));
+ }
+ }
+
+ @Test
+ public void testGcdLong(){
+ long a = 30;
+ long b = 50;
+ long c = 77;
+
+ Assert.assertEquals(0, ArithmeticUtils.gcd(0L, 0));
+
+ Assert.assertEquals(b, ArithmeticUtils.gcd(0, b));
+ Assert.assertEquals(a, ArithmeticUtils.gcd(a, 0));
+ Assert.assertEquals(b, ArithmeticUtils.gcd(0, -b));
+ Assert.assertEquals(a, ArithmeticUtils.gcd(-a, 0));
+
+ Assert.assertEquals(10, ArithmeticUtils.gcd(a, b));
+ Assert.assertEquals(10, ArithmeticUtils.gcd(-a, b));
+ Assert.assertEquals(10, ArithmeticUtils.gcd(a, -b));
+ Assert.assertEquals(10, ArithmeticUtils.gcd(-a, -b));
+
+ Assert.assertEquals(1, ArithmeticUtils.gcd(a, c));
+ Assert.assertEquals(1, ArithmeticUtils.gcd(-a, c));
+ Assert.assertEquals(1, ArithmeticUtils.gcd(a, -c));
+ Assert.assertEquals(1, ArithmeticUtils.gcd(-a, -c));
+
+ Assert.assertEquals(3L * (1L<<45), ArithmeticUtils.gcd(3L * (1L<<50), 9L * (1L<<45)));
+
+ Assert.assertEquals(1L<<45, ArithmeticUtils.gcd(1L<<45, Long.MIN_VALUE));
+
+ Assert.assertEquals(Long.MAX_VALUE, ArithmeticUtils.gcd(Long.MAX_VALUE, 0L));
+ Assert.assertEquals(Long.MAX_VALUE, ArithmeticUtils.gcd(-Long.MAX_VALUE, 0L));
+ Assert.assertEquals(1, ArithmeticUtils.gcd(60247241209L, 153092023L));
+ try {
+ // gcd(Long.MIN_VALUE, 0) > Long.MAX_VALUE
+ ArithmeticUtils.gcd(Long.MIN_VALUE, 0);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ try {
+ // gcd(0, Long.MIN_VALUE) > Long.MAX_VALUE
+ ArithmeticUtils.gcd(0, Long.MIN_VALUE);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ try {
+ // gcd(Long.MIN_VALUE, Long.MIN_VALUE) > Long.MAX_VALUE
+ ArithmeticUtils.gcd(Long.MIN_VALUE, Long.MIN_VALUE);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ }
+
+
+ @Test
+ public void testLcm() {
+ int a = 30;
+ int b = 50;
+ int c = 77;
+
+ Assert.assertEquals(0, ArithmeticUtils.lcm(0, b));
+ Assert.assertEquals(0, ArithmeticUtils.lcm(a, 0));
+ Assert.assertEquals(b, ArithmeticUtils.lcm(1, b));
+ Assert.assertEquals(a, ArithmeticUtils.lcm(a, 1));
+ Assert.assertEquals(150, ArithmeticUtils.lcm(a, b));
+ Assert.assertEquals(150, ArithmeticUtils.lcm(-a, b));
+ Assert.assertEquals(150, ArithmeticUtils.lcm(a, -b));
+ Assert.assertEquals(150, ArithmeticUtils.lcm(-a, -b));
+ Assert.assertEquals(2310, ArithmeticUtils.lcm(a, c));
+
+ // Assert that no intermediate value overflows:
+ // The naive implementation of lcm(a,b) would be (a*b)/gcd(a,b)
+ Assert.assertEquals((1<<20)*15, ArithmeticUtils.lcm((1<<20)*3, (1<<20)*5));
+
+ // Special case
+ Assert.assertEquals(0, ArithmeticUtils.lcm(0, 0));
+
+ try {
+ // lcm == abs(MIN_VALUE) cannot be represented as a nonnegative int
+ ArithmeticUtils.lcm(Integer.MIN_VALUE, 1);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+
+ try {
+ // lcm == abs(MIN_VALUE) cannot be represented as a nonnegative int
+ ArithmeticUtils.lcm(Integer.MIN_VALUE, 1<<20);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+
+ try {
+ ArithmeticUtils.lcm(Integer.MAX_VALUE, Integer.MAX_VALUE - 1);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testLcmLong() {
+ long a = 30;
+ long b = 50;
+ long c = 77;
+
+ Assert.assertEquals(0, ArithmeticUtils.lcm(0, b));
+ Assert.assertEquals(0, ArithmeticUtils.lcm(a, 0));
+ Assert.assertEquals(b, ArithmeticUtils.lcm(1, b));
+ Assert.assertEquals(a, ArithmeticUtils.lcm(a, 1));
+ Assert.assertEquals(150, ArithmeticUtils.lcm(a, b));
+ Assert.assertEquals(150, ArithmeticUtils.lcm(-a, b));
+ Assert.assertEquals(150, ArithmeticUtils.lcm(a, -b));
+ Assert.assertEquals(150, ArithmeticUtils.lcm(-a, -b));
+ Assert.assertEquals(2310, ArithmeticUtils.lcm(a, c));
+
+ Assert.assertEquals(Long.MAX_VALUE, ArithmeticUtils.lcm(60247241209L, 153092023L));
+
+ // Assert that no intermediate value overflows:
+ // The naive implementation of lcm(a,b) would be (a*b)/gcd(a,b)
+ Assert.assertEquals((1L<<50)*15, ArithmeticUtils.lcm((1L<<45)*3, (1L<<50)*5));
+
+ // Special case
+ Assert.assertEquals(0L, ArithmeticUtils.lcm(0L, 0L));
+
+ try {
+ // lcm == abs(MIN_VALUE) cannot be represented as a nonnegative int
+ ArithmeticUtils.lcm(Long.MIN_VALUE, 1);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+
+ try {
+ // lcm == abs(MIN_VALUE) cannot be represented as a nonnegative int
+ ArithmeticUtils.lcm(Long.MIN_VALUE, 1<<20);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+
+ Assert.assertEquals((long) Integer.MAX_VALUE * (Integer.MAX_VALUE - 1),
+ ArithmeticUtils.lcm((long)Integer.MAX_VALUE, Integer.MAX_VALUE - 1));
+ try {
+ ArithmeticUtils.lcm(Long.MAX_VALUE, Long.MAX_VALUE - 1);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException expected) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testMulAndCheck() {
+ int big = Integer.MAX_VALUE;
+ int bigNeg = Integer.MIN_VALUE;
+ Assert.assertEquals(big, ArithmeticUtils.mulAndCheck(big, 1));
+ try {
+ ArithmeticUtils.mulAndCheck(big, 2);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ try {
+ ArithmeticUtils.mulAndCheck(bigNeg, 2);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ }
+
+ @Test
+ public void testMulAndCheckLong() {
+ long max = Long.MAX_VALUE;
+ long min = Long.MIN_VALUE;
+ Assert.assertEquals(max, ArithmeticUtils.mulAndCheck(max, 1L));
+ Assert.assertEquals(min, ArithmeticUtils.mulAndCheck(min, 1L));
+ Assert.assertEquals(0L, ArithmeticUtils.mulAndCheck(max, 0L));
+ Assert.assertEquals(0L, ArithmeticUtils.mulAndCheck(min, 0L));
+ Assert.assertEquals(max, ArithmeticUtils.mulAndCheck(1L, max));
+ Assert.assertEquals(min, ArithmeticUtils.mulAndCheck(1L, min));
+ Assert.assertEquals(0L, ArithmeticUtils.mulAndCheck(0L, max));
+ Assert.assertEquals(0L, ArithmeticUtils.mulAndCheck(0L, min));
+ Assert.assertEquals(1L, ArithmeticUtils.mulAndCheck(-1L, -1L));
+ Assert.assertEquals(min, ArithmeticUtils.mulAndCheck(min / 2, 2));
+ testMulAndCheckLongFailure(max, 2L);
+ testMulAndCheckLongFailure(2L, max);
+ testMulAndCheckLongFailure(min, 2L);
+ testMulAndCheckLongFailure(2L, min);
+ testMulAndCheckLongFailure(min, -1L);
+ testMulAndCheckLongFailure(-1L, min);
+ }
+
+ @Test
+ public void testSubAndCheck() {
+ int big = Integer.MAX_VALUE;
+ int bigNeg = Integer.MIN_VALUE;
+ Assert.assertEquals(big, ArithmeticUtils.subAndCheck(big, 0));
+ Assert.assertEquals(bigNeg + 1, ArithmeticUtils.subAndCheck(bigNeg, -1));
+ Assert.assertEquals(-1, ArithmeticUtils.subAndCheck(bigNeg, -big));
+ try {
+ ArithmeticUtils.subAndCheck(big, -1);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ try {
+ ArithmeticUtils.subAndCheck(bigNeg, 1);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+ }
+
+ @Test
+ public void testSubAndCheckErrorMessage() {
+ int big = Integer.MAX_VALUE;
+ try {
+ ArithmeticUtils.subAndCheck(big, -1);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ Assert.assertTrue(ex.getMessage().length() > 1);
+ }
+ }
+
+ @Test
+ public void testSubAndCheckLong() {
+ long max = Long.MAX_VALUE;
+ long min = Long.MIN_VALUE;
+ Assert.assertEquals(max, ArithmeticUtils.subAndCheck(max, 0));
+ Assert.assertEquals(min, ArithmeticUtils.subAndCheck(min, 0));
+ Assert.assertEquals(-max, ArithmeticUtils.subAndCheck(0, max));
+ Assert.assertEquals(min + 1, ArithmeticUtils.subAndCheck(min, -1));
+ // min == -1-max
+ Assert.assertEquals(-1, ArithmeticUtils.subAndCheck(-max - 1, -max));
+ Assert.assertEquals(max, ArithmeticUtils.subAndCheck(-1, -1 - max));
+ testSubAndCheckLongFailure(0L, min);
+ testSubAndCheckLongFailure(max, -1L);
+ testSubAndCheckLongFailure(min, 1L);
+ }
+
+ @Test
+ public void testPow() {
+
+ Assert.assertEquals(1801088541, ArithmeticUtils.pow(21, 7));
+ Assert.assertEquals(1, ArithmeticUtils.pow(21, 0));
+ try {
+ ArithmeticUtils.pow(21, -7);
+ Assert.fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ Assert.assertEquals(1801088541, ArithmeticUtils.pow(21, 7));
+ Assert.assertEquals(1, ArithmeticUtils.pow(21, 0));
+ try {
+ ArithmeticUtils.pow(21, -7);
+ Assert.fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ Assert.assertEquals(1801088541l, ArithmeticUtils.pow(21l, 7));
+ Assert.assertEquals(1l, ArithmeticUtils.pow(21l, 0));
+ try {
+ ArithmeticUtils.pow(21l, -7);
+ Assert.fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ BigInteger twentyOne = BigInteger.valueOf(21l);
+ Assert.assertEquals(BigInteger.valueOf(1801088541l), ArithmeticUtils.pow(twentyOne, 7));
+ Assert.assertEquals(BigInteger.ONE, ArithmeticUtils.pow(twentyOne, 0));
+ try {
+ ArithmeticUtils.pow(twentyOne, -7);
+ Assert.fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ Assert.assertEquals(BigInteger.valueOf(1801088541l), ArithmeticUtils.pow(twentyOne, 7l));
+ Assert.assertEquals(BigInteger.ONE, ArithmeticUtils.pow(twentyOne, 0l));
+ try {
+ ArithmeticUtils.pow(twentyOne, -7l);
+ Assert.fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ Assert.assertEquals(BigInteger.valueOf(1801088541l), ArithmeticUtils.pow(twentyOne, BigInteger.valueOf(7l)));
+ Assert.assertEquals(BigInteger.ONE, ArithmeticUtils.pow(twentyOne, BigInteger.ZERO));
+ try {
+ ArithmeticUtils.pow(twentyOne, BigInteger.valueOf(-7l));
+ Assert.fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected behavior
+ }
+
+ BigInteger bigOne =
+ new BigInteger("1543786922199448028351389769265814882661837148" +
+ "4763915343722775611762713982220306372888519211" +
+ "560905579993523402015636025177602059044911261");
+ Assert.assertEquals(bigOne, ArithmeticUtils.pow(twentyOne, 103));
+ Assert.assertEquals(bigOne, ArithmeticUtils.pow(twentyOne, 103l));
+ Assert.assertEquals(bigOne, ArithmeticUtils.pow(twentyOne, BigInteger.valueOf(103l)));
+
+ }
+
+ @Test(expected=ArithmeticException.class)
+ public void testPowIntOverflow() {
+ ArithmeticUtils.pow(21, 8);
+ }
+
+ @Test
+ public void testPowInt() {
+ final int base = 21;
+
+ Assert.assertEquals(85766121L,
+ ArithmeticUtils.pow(base, 6));
+ Assert.assertEquals(1801088541L,
+ ArithmeticUtils.pow(base, 7));
+ }
+
+ @Test(expected=ArithmeticException.class)
+ public void testPowNegativeIntOverflow() {
+ ArithmeticUtils.pow(-21, 8);
+ }
+
+ @Test
+ public void testPowNegativeInt() {
+ final int base = -21;
+
+ Assert.assertEquals(85766121,
+ ArithmeticUtils.pow(base, 6));
+ Assert.assertEquals(-1801088541,
+ ArithmeticUtils.pow(base, 7));
+ }
+
+ @Test
+ public void testPowMinusOneInt() {
+ final int base = -1;
+ for (int i = 0; i < 100; i++) {
+ final int pow = ArithmeticUtils.pow(base, i);
+ Assert.assertEquals("i: " + i, i % 2 == 0 ? 1 : -1, pow);
+ }
+ }
+
+ @Test
+ public void testPowOneInt() {
+ final int base = 1;
+ for (int i = 0; i < 100; i++) {
+ final int pow = ArithmeticUtils.pow(base, i);
+ Assert.assertEquals("i: " + i, 1, pow);
+ }
+ }
+
+ @Test(expected=ArithmeticException.class)
+ public void testPowLongOverflow() {
+ ArithmeticUtils.pow(21, 15);
+ }
+
+ @Test
+ public void testPowLong() {
+ final long base = 21;
+
+ Assert.assertEquals(154472377739119461L,
+ ArithmeticUtils.pow(base, 13));
+ Assert.assertEquals(3243919932521508681L,
+ ArithmeticUtils.pow(base, 14));
+ }
+
+ @Test(expected=ArithmeticException.class)
+ public void testPowNegativeLongOverflow() {
+ ArithmeticUtils.pow(-21L, 15);
+ }
+
+ @Test
+ public void testPowNegativeLong() {
+ final long base = -21;
+
+ Assert.assertEquals(-154472377739119461L,
+ ArithmeticUtils.pow(base, 13));
+ Assert.assertEquals(3243919932521508681L,
+ ArithmeticUtils.pow(base, 14));
+ }
+
+ @Test
+ public void testPowMinusOneLong() {
+ final long base = -1;
+ for (int i = 0; i < 100; i++) {
+ final long pow = ArithmeticUtils.pow(base, i);
+ Assert.assertEquals("i: " + i, i % 2 == 0 ? 1 : -1, pow);
+ }
+ }
+
+ @Test
+ public void testPowOneLong() {
+ final long base = 1;
+ for (int i = 0; i < 100; i++) {
+ final long pow = ArithmeticUtils.pow(base, i);
+ Assert.assertEquals("i: " + i, 1, pow);
+ }
+ }
+
+ @Test
+ public void testIsPowerOfTwo() {
+ final int n = 1025;
+ final boolean[] expected = new boolean[n];
+ Arrays.fill(expected, false);
+ for (int i = 1; i < expected.length; i *= 2) {
+ expected[i] = true;
+ }
+ for (int i = 0; i < expected.length; i++) {
+ final boolean actual = ArithmeticUtils.isPowerOfTwo(i);
+ Assert.assertTrue(Integer.toString(i), actual == expected[i]);
+ }
+ }
+
+ private void testAddAndCheckLongFailure(long a, long b) {
+ try {
+ ArithmeticUtils.addAndCheck(a, b);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // success
+ }
+ }
+
+ private void testMulAndCheckLongFailure(long a, long b) {
+ try {
+ ArithmeticUtils.mulAndCheck(a, b);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // success
+ }
+ }
+
+ private void testSubAndCheckLongFailure(long a, long b) {
+ try {
+ ArithmeticUtils.subAndCheck(a, b);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // success
+ }
+ }
+
+ /**
+ * Testing helper method.
+ * @return an array of int numbers containing corner cases:<ul>
+ * <li>values near the beginning of int range,</li>
+ * <li>values near the end of int range,</li>
+ * <li>values near zero</li>
+ * <li>and some randomly distributed values.</li>
+ * </ul>
+ */
+ private static int[] getIntSpecialCases() {
+ int ints[] = new int[100];
+ int i = 0;
+ ints[i++] = Integer.MAX_VALUE;
+ ints[i++] = Integer.MAX_VALUE - 1;
+ ints[i++] = 100;
+ ints[i++] = 101;
+ ints[i++] = 102;
+ ints[i++] = 300;
+ ints[i++] = 567;
+ for (int j = 0; j < 20; j++) {
+ ints[i++] = j;
+ }
+ for (int j = i - 1; j >= 0; j--) {
+ ints[i++] = ints[j] > 0 ? -ints[j] : Integer.MIN_VALUE;
+ }
+ java.util.Random r = new java.util.Random(System.nanoTime());
+ for (; i < ints.length;) {
+ ints[i++] = r.nextInt();
+ }
+ return ints;
+ }
+
+ /**
+ * Testing helper method.
+ * @return an array of long numbers containing corner cases:<ul>
+ * <li>values near the beginning of long range,</li>
+ * <li>values near the end of long range,</li>
+ * <li>values near the beginning of int range,</li>
+ * <li>values near the end of int range,</li>
+ * <li>values near zero</li>
+ * <li>and some randomly distributed values.</li>
+ * </ul>
+ */
+ private static long[] getLongSpecialCases() {
+ long longs[] = new long[100];
+ int i = 0;
+ longs[i++] = Long.MAX_VALUE;
+ longs[i++] = Long.MAX_VALUE - 1L;
+ longs[i++] = (long) Integer.MAX_VALUE + 1L;
+ longs[i++] = Integer.MAX_VALUE;
+ longs[i++] = Integer.MAX_VALUE - 1;
+ longs[i++] = 100L;
+ longs[i++] = 101L;
+ longs[i++] = 102L;
+ longs[i++] = 300L;
+ longs[i++] = 567L;
+ for (int j = 0; j < 20; j++) {
+ longs[i++] = j;
+ }
+ for (int j = i - 1; j >= 0; j--) {
+ longs[i++] = longs[j] > 0L ? -longs[j] : Long.MIN_VALUE;
+ }
+ java.util.Random r = new java.util.Random(System.nanoTime());
+ for (; i < longs.length;) {
+ longs[i++] = r.nextLong();
+ }
+ return longs;
+ }
+
+ private static long toUnsignedLong(int number) {
+ return number < 0 ? 0x100000000L + (long)number : (long)number;
+ }
+
+ private static int remainderUnsignedExpected(int dividend, int divisor) {
+ return (int)remainderUnsignedExpected(toUnsignedLong(dividend), toUnsignedLong(divisor));
+ }
+
+ private static int divideUnsignedExpected(int dividend, int divisor) {
+ return (int)divideUnsignedExpected(toUnsignedLong(dividend), toUnsignedLong(divisor));
+ }
+
+ private static BigInteger toUnsignedBigInteger(long number) {
+ return number < 0L ? BigInteger.ONE.shiftLeft(64).add(BigInteger.valueOf(number)) : BigInteger.valueOf(number);
+ }
+
+ private static long remainderUnsignedExpected(long dividend, long divisor) {
+ return toUnsignedBigInteger(dividend).remainder(toUnsignedBigInteger(divisor)).longValue();
+ }
+
+ private static long divideUnsignedExpected(long dividend, long divisor) {
+ return toUnsignedBigInteger(dividend).divide(toUnsignedBigInteger(divisor)).longValue();
+ }
+
+ @Test(timeout=5000L)
+ public void testRemainderUnsignedInt() {
+ Assert.assertEquals(36, ArithmeticUtils.remainderUnsigned(-2147479015, 63));
+ Assert.assertEquals(6, ArithmeticUtils.remainderUnsigned(-2147479015, 25));
+ }
+
+ @Test(timeout=5000L)
+ public void testRemainderUnsignedIntSpecialCases() {
+ int ints[] = getIntSpecialCases();
+ for (int dividend : ints) {
+ for (int divisor : ints) {
+ if (divisor == 0) {
+ try {
+ ArithmeticUtils.remainderUnsigned(dividend, divisor);
+ Assert.fail("Should have failed with ArithmeticException: division by zero");
+ } catch (ArithmeticException e) {
+ // Success.
+ }
+ } else {
+ Assert.assertEquals(remainderUnsignedExpected(dividend, divisor), ArithmeticUtils.remainderUnsigned(dividend, divisor));
+ }
+ }
+ }
+ }
+
+ @Test(timeout=5000L)
+ public void testRemainderUnsignedLong() {
+ Assert.assertEquals(48L, ArithmeticUtils.remainderUnsigned(-2147479015L, 63L));
+ }
+
+ @Test//(timeout=5000L)
+ public void testRemainderUnsignedLongSpecialCases() {
+ long longs[] = getLongSpecialCases();
+ for (long dividend : longs) {
+ for (long divisor : longs) {
+ if (divisor == 0L) {
+ try {
+ ArithmeticUtils.remainderUnsigned(dividend, divisor);
+ Assert.fail("Should have failed with ArithmeticException: division by zero");
+ } catch (ArithmeticException e) {
+ // Success.
+ }
+ } else {
+ Assert.assertEquals(remainderUnsignedExpected(dividend, divisor), ArithmeticUtils.remainderUnsigned(dividend, divisor));
+ }
+ }
+ }
+ }
+
+ @Test(timeout=5000L)
+ public void testDivideUnsignedInt() {
+ Assert.assertEquals(34087115, ArithmeticUtils.divideUnsigned(-2147479015, 63));
+ Assert.assertEquals(85899531, ArithmeticUtils.divideUnsigned(-2147479015, 25));
+ Assert.assertEquals(2147483646, ArithmeticUtils.divideUnsigned(-3, 2));
+ Assert.assertEquals(330382098, ArithmeticUtils.divideUnsigned(-16, 13));
+ Assert.assertEquals(306783377, ArithmeticUtils.divideUnsigned(-16, 14));
+ Assert.assertEquals(2, ArithmeticUtils.divideUnsigned(-1, 2147483647));
+ Assert.assertEquals(2, ArithmeticUtils.divideUnsigned(-2, 2147483647));
+ Assert.assertEquals(1, ArithmeticUtils.divideUnsigned(-3, 2147483647));
+ Assert.assertEquals(1, ArithmeticUtils.divideUnsigned(-16, 2147483647));
+ Assert.assertEquals(1, ArithmeticUtils.divideUnsigned(-16, 2147483646));
+ }
+
+ @Test(timeout=5000L)
+ public void testDivideUnsignedIntSpecialCases() {
+ int ints[] = getIntSpecialCases();
+ for (int dividend : ints) {
+ for (int divisor : ints) {
+ if (divisor == 0) {
+ try {
+ ArithmeticUtils.divideUnsigned(dividend, divisor);
+ Assert.fail("Should have failed with ArithmeticException: division by zero");
+ } catch (ArithmeticException e) {
+ // Success.
+ }
+ } else {
+ Assert.assertEquals(divideUnsignedExpected(dividend, divisor), ArithmeticUtils.divideUnsigned(dividend, divisor));
+ }
+ }
+ }
+ }
+
+ @Test(timeout=5000L)
+ public void testDivideUnsignedLong() {
+ Assert.assertEquals(292805461453366231L, ArithmeticUtils.divideUnsigned(-2147479015L, 63L));
+ }
+
+ @Test(timeout=5000L)
+ public void testDivideUnsignedLongSpecialCases() {
+ long longs[] = getLongSpecialCases();
+ for (long dividend : longs) {
+ for (long divisor : longs) {
+ if (divisor == 0L) {
+ try {
+ ArithmeticUtils.divideUnsigned(dividend, divisor);
+ Assert.fail("Should have failed with ArithmeticException: division by zero");
+ } catch (ArithmeticException e) {
+ // Success.
+ }
+ } else {
+ Assert.assertEquals(divideUnsignedExpected(dividend, divisor), ArithmeticUtils.divideUnsigned(dividend, divisor));
+ }
+ }
+ }
+ }
+}
diff --git a/commons-numbers-fraction/LICENSE.txt b/commons-numbers-fraction/LICENSE.txt
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/commons-numbers-fraction/LICENSE.txt
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
diff --git a/commons-numbers-fraction/NOTICE.txt b/commons-numbers-fraction/NOTICE.txt
new file mode 100644
index 0000000..ea6ae07
--- /dev/null
+++ b/commons-numbers-fraction/NOTICE.txt
@@ -0,0 +1,6 @@
+Apache Commons Numbers
+Copyright 2001-2016 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/commons-numbers-fraction/README.md b/commons-numbers-fraction/README.md
new file mode 100644
index 0000000..0701bed
--- /dev/null
+++ b/commons-numbers-fraction/README.md
@@ -0,0 +1,98 @@
+<!---
+ 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.
+-->
+<!---
+ +======================================================================+
+ |**** ****|
+ |**** THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN ****|
+ |**** DO NOT EDIT DIRECTLY ****|
+ |**** ****|
+ +======================================================================+
+ | TEMPLATE FILE: readme-md-template.md |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ | |
+ | 1) Re-generate using: mvn commons:readme-md |
+ | |
+ | 2) Set the following properties in the component's pom: |
+ | - commons.componentid (required, alphabetic, lower case) |
+ | - commons.release.version (required) |
+ | |
+ | 3) Example Properties |
+ | |
+ | <properties> |
+ | <commons.componentid>math</commons.componentid> |
+ | <commons.release.version>1.2</commons.release.version> |
+ | </properties> |
+ | |
+ +======================================================================+
+--->
+Apache Commons Numbers Core
+===================
+
+Basic utilities.
+
+Documentation
+-------------
+
+More information can be found on the [homepage](https://commons.apache.org/proper/commons-numbers).
+The [JavaDoc](https://commons.apache.org/proper/commons-numbers/javadocs/api-release) can be browsed.
+Questions related to the usage of Apache Commons Numbers Core should be posted to the [user mailing list][ml].
+
+Where can I get the latest release?
+-----------------------------------
+You can download source and binaries from our [download page](https://commons.apache.org/proper/commons-numbers/download_numbers.cgi).
+
+Alternatively you can pull it from the central Maven repositories:
+
+```xml
+<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-numbers-core</artifactId>
+ <version>1.0</version>
+</dependency>
+```
+
+Contributing
+------------
+
+We accept PRs via github. The [developer mailing list][ml] is the main channel of communication for contributors.
+There are some guidelines which will make applying PRs easier for us:
++ No tabs! Please use spaces for indentation.
++ Respect the code style.
++ Create minimal diffs - disable on save actions like reformat source code or organize imports. If you feel the source code should be reformatted create a separate PR for this change.
++ Provide JUnit tests for your changes and make sure your changes don't break any existing tests by running ```mvn clean test```.
+
+If you plan to contribute on a regular basis, please consider filing a [contributor license agreement](https://www.apache.org/licenses/#clas).
+You can learn more about contributing via GitHub in our [contribution guidelines](CONTRIBUTING.md).
+
+License
+-------
+Code is under the [Apache Licence v2](https://www.apache.org/licenses/LICENSE-2.0.txt).
+
+Donations
+---------
+You like Apache Commons Numbers Core? Then [donate back to the ASF](https://www.apache.org/foundation/contributing.html) to support the development.
+
+Additional Resources
+--------------------
+
++ [Apache Commons Homepage](https://commons.apache.org/)
++ [Apache Bugtracker (JIRA)](https://issues.apache.org/jira/)
++ [Apache Commons Twitter Account](https://twitter.com/ApacheCommons)
++ #apachecommons IRC channel on freenode.org
+
+[ml]:https://commons.apache.org/mail-lists.html
diff --git a/commons-numbers-fraction/pom.xml b/commons-numbers-fraction/pom.xml
new file mode 100644
index 0000000..42c3aa5
--- /dev/null
+++ b/commons-numbers-fraction/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-numbers-parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-numbers-fraction</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <name>Apache Commons Numbers Fraction</name>
+
+ <description>Fraction utilities.</description>
+
+ <properties>
+ <!-- This value must reflect the current name of the base package. -->
+ <commons.osgi.symbolicName>org.apache.commons.numbers.</commons.osgi.symbolicName>
+ <!-- OSGi -->
+ <commons.osgi.export>org.apache.commons.numbers.fraction</commons.osgi.export>
+ <!-- Workaround to avoid duplicating config files. -->
+ <numbers.parent.dir>${basedir}/..</numbers.parent.dir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-numbers-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-numbers-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/AbstractFormat.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/AbstractFormat.java
new file mode 100644
index 0000000..9701f7f
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/AbstractFormat.java
@@ -0,0 +1,205 @@
+/*
+ * 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.numbers.fraction;
+
+import java.io.Serializable;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * Common part shared by both {@link FractionFormat} and {@link BigFractionFormat}.
+ */
+public abstract class AbstractFormat extends NumberFormat implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -6981118387974191891L;
+
+ /** The format used for the denominator. */
+ private NumberFormat denominatorFormat;
+
+ /** The format used for the numerator. */
+ private NumberFormat numeratorFormat;
+
+ /**
+ * Create an improper formatting instance with the default number format
+ * for the numerator and denominator.
+ */
+ protected AbstractFormat() {
+ this(getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an improper formatting instance with a custom number format for
+ * both the numerator and denominator.
+ * @param format the custom format for both the numerator and denominator.
+ */
+ protected AbstractFormat(final NumberFormat format) {
+ this(format, (NumberFormat) format.clone());
+ }
+
+ /**
+ * Create an improper formatting instance with a custom number format for
+ * the numerator and a custom number format for the denominator.
+ * @param numeratorFormat the custom format for the numerator.
+ * @param denominatorFormat the custom format for the denominator.
+ */
+ protected AbstractFormat(final NumberFormat numeratorFormat,
+ final NumberFormat denominatorFormat) {
+ this.numeratorFormat = numeratorFormat;
+ this.denominatorFormat = denominatorFormat;
+ }
+
+ /**
+ * Create a default number format. The default number format is based on
+ * {@link NumberFormat#getNumberInstance(java.util.Locale)}. The only
+ * customization is the maximum number of BigFraction digits, which is set to 0.
+ * @return the default number format.
+ */
+ protected static NumberFormat getDefaultNumberFormat() {
+ return getDefaultNumberFormat(Locale.getDefault());
+ }
+
+ /**
+ * Create a default number format. The default number format is based on
+ * {@link NumberFormat#getNumberInstance(java.util.Locale)}. The only
+ * customization is the maximum number of BigFraction digits, which is set to 0.
+ * @param locale the specific locale used by the format.
+ * @return the default number format specific to the given locale.
+ */
+ protected static NumberFormat getDefaultNumberFormat(final Locale locale) {
+ final NumberFormat nf = NumberFormat.getNumberInstance(locale);
+ nf.setMaximumFractionDigits(0);
+ nf.setParseIntegerOnly(true);
+ return nf;
+ }
+
+ /**
+ * Access the denominator format.
+ * @return the denominator format.
+ */
+ public NumberFormat getDenominatorFormat() {
+ return denominatorFormat;
+ }
+
+ /**
+ * Access the numerator format.
+ * @return the numerator format.
+ */
+ public NumberFormat getNumeratorFormat() {
+ return numeratorFormat;
+ }
+
+ /**
+ * Modify the denominator format.
+ * @param format the new denominator format value.
+ * @throws NullPointerException if {@code format} is {@code null}.
+ */
+ public void setDenominatorFormat(final NumberFormat format) {
+ if (format == null) {
+ throw new NullPointerException("denominator format");
+ }
+ this.denominatorFormat = format;
+ }
+
+ /**
+ * Modify the numerator format.
+ * @param format the new numerator format value.
+ * @throws NullPointerException if {@code format} is {@code null}.
+ */
+ public void setNumeratorFormat(final NumberFormat format) {
+ if (format == null) {
+ throw new NullPointerException("numerator format");
+ }
+ this.numeratorFormat = format;
+ }
+
+ /**
+ * Parses <code>source</code> until a non-whitespace character is found.
+ * @param source the string to parse
+ * @param pos input/output parsing parameter. On output, <code>pos</code>
+ * holds the index of the next non-whitespace character.
+ */
+ protected static void parseAndIgnoreWhitespace(final String source,
+ final ParsePosition pos) {
+ parseNextCharacter(source, pos);
+ pos.setIndex(pos.getIndex() - 1);
+ }
+
+ /**
+ * Parses <code>source</code> until a non-whitespace character is found.
+ * @param source the string to parse
+ * @param pos input/output parsing parameter.
+ * @return the first non-whitespace character.
+ */
+ protected static char parseNextCharacter(final String source,
+ final ParsePosition pos) {
+ int index = pos.getIndex();
+ final int n = source.length();
+ char ret = 0;
+
+ if (index < n) {
+ char c;
+ do {
+ c = source.charAt(index++);
+ } while (Character.isWhitespace(c) && index < n);
+ pos.setIndex(index);
+
+ if (index < n) {
+ ret = c;
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Formats a double value as a fraction and appends the result to a StringBuffer.
+ *
+ * @param value the double value to format
+ * @param buffer StringBuffer to append to
+ * @param position On input: an alignment field, if desired. On output: the
+ * offsets of the alignment field
+ * @return a reference to the appended buffer
+ * @see #format(Object, StringBuffer, FieldPosition)
+ */
+ @Override
+ public StringBuffer format(final double value,
+ final StringBuffer buffer, final FieldPosition position) {
+ return format(Double.valueOf(value), buffer, position);
+ }
+
+
+ /**
+ * Formats a long value as a fraction and appends the result to a StringBuffer.
+ *
+ * @param value the long value to format
+ * @param buffer StringBuffer to append to
+ * @param position On input: an alignment field, if desired. On output: the
+ * offsets of the alignment field
+ * @return a reference to the appended buffer
+ * @see #format(Object, StringBuffer, FieldPosition)
+ */
+ @Override
+ public StringBuffer format(final long value,
+ final StringBuffer buffer, final FieldPosition position) {
+ return format(Long.valueOf(value), buffer, position);
+ }
+
+}
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java
new file mode 100644
index 0000000..eab9b7f
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java
@@ -0,0 +1,1200 @@
+/*
+ * 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.numbers.fraction;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import org.apache.commons.numbers.core.ArithmeticUtils;
+
+/**
+ * Representation of a rational number without any overflow. This class is
+ * immutable.
+ */
+public class BigFraction
+ extends Number
+ implements 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);
+
+ /** Parameter name for fraction (to satisfy checkstyle). */
+ private static final String PARAM_NAME_FRACTION = "fraction";
+
+ /** Parameter name for BigIntegers (to satisfy checkstyle). */
+ private static final String PARAM_NAME_BG = "bg";
+
+ /** The numerator. */
+ private final BigInteger numerator;
+
+ /** The denominator. */
+ private final BigInteger denominator;
+
+ /**
+ * <p>
+ * Create a {@link BigFraction} equivalent to the passed {@code BigInteger}, ie
+ * "num / 1".
+ * </p>
+ *
+ * @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 ArithmeticException if the denominator is zero.
+ */
+ public BigFraction(BigInteger num, BigInteger den) {
+ checkNotNull(num, "numerator");
+ checkNotNull(den, "denominator");
+ if (den.signum() == 0) {
+ throw new FractionException(FractionException.ERROR_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>
+ * <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).
+ * </p>
+ * @see #BigFraction(double, double, int)
+ * @param value the double value to convert to a fraction.
+ * @exception IllegalArgumentException if value is NaN or infinite
+ */
+ public BigFraction(final double value) throws IllegalArgumentException {
+ if (Double.isNaN(value)) {
+ throw new IllegalArgumentException("cannot convert NaN value");
+ }
+ if (Double.isInfinite(value)) {
+ throw new IllegalArgumentException("cannot convert infinite value");
+ }
+
+ // 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)</li>
+ * </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 ArithmeticException
+ * if the continued fraction failed to converge.
+ * @see #BigFraction(double)
+ */
+ public BigFraction(final double value, final double epsilon,
+ final int maxIterations) {
+ 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>
+ * <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.
+ * </p>
+ *
+ * See JIRA issue ticket MATH-181 for more details:
+ *
+ * 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 ArithmeticException
+ * if the continued fraction failed to converge.
+ */
+ private BigFraction(final double value, final double epsilon,
+ final int maxDenominator, int maxIterations) {
+ long overflow = Integer.MAX_VALUE;
+ double r0 = value;
+ long a0 = (long) Math.floor(r0);
+
+ if (Math.abs(a0) > overflow) {
+ throw new FractionException(FractionException.ERROR_CONVERSION_OVERFLOW, value, a0, 1l);
+ }
+
+ // check for (almost) integer arguments, which should not go
+ // to iterations.
+ if (Math.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) Math.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 && Math.abs(q1) < maxDenominator) {
+ break;
+ }
+ throw new FractionException(FractionException.ERROR_CONVERSION_OVERFLOW, value, p2, q2);
+ }
+
+ final double convergent = (double) p2 / (double) q2;
+ if ((n < maxIterations) &&
+ (Math.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 FractionException(FractionException.ERROR_CONVERSION, 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)</li>
+ * </ul>
+ *
+ * @param value
+ * the double value to convert to a fraction.
+ * @param maxDenominator
+ * The maximum allowed value for denominator.
+ * @throws ArithmeticException
+ * if the continued fraction failed to converge.
+ */
+ public BigFraction(final double value, final int maxDenominator) {
+ this(value, 0, maxDenominator, 100);
+ }
+
+ /**
+ * <p>
+ * Create a {@link BigFraction} equivalent to the passed {@code int}, ie
+ * "num / 1".
+ * </p>
+ *
+ * @param num
+ * the numerator.
+ */
+ public BigFraction(final int num) {
+ this(BigInteger.valueOf(num), BigInteger.ONE);
+ }
+
+ /**
+ * <p>
+ * Create a {@link BigFraction} given the numerator and denominator as simple
+ * {@code int}. The {@link BigFraction} is reduced to lowest terms.
+ * </p>
+ *
+ * @param num
+ * the numerator.
+ * @param den
+ * the denominator.
+ */
+ public BigFraction(final int num, final int den) {
+ this(BigInteger.valueOf(num), BigInteger.valueOf(den));
+ }
+
+ /**
+ * <p>
+ * Create a {@link BigFraction} equivalent to the passed long, ie "num / 1".
+ * </p>
+ *
+ * @param num
+ * the numerator.
+ */
+ public BigFraction(final long num) {
+ this(BigInteger.valueOf(num), BigInteger.ONE);
+ }
+
+ /**
+ * <p>
+ * Create a {@link BigFraction} given the numerator and denominator as simple
+ * {@code long}. The {@link BigFraction} is reduced to lowest terms.
+ * </p>
+ *
+ * @param num
+ * the numerator.
+ * @param den
+ * the denominator.
+ */
+ public BigFraction(final long num, final long den) {
+ this(BigInteger.valueOf(num), BigInteger.valueOf(den));
+ }
+
+ /**
+ * <p>
+ * Creates a <code>BigFraction</code> instance with the 2 parts of a fraction
+ * Y/Z.
+ * </p>
+ *
+ * <p>
+ * Any negative signs are resolved to be on the numerator.
+ * </p>
+ *
+ * @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);
+ }
+
+ /**
+ * <p>
+ * Returns the absolute value of this {@link BigFraction}.
+ * </p>
+ *
+ * @return the absolute value as a {@link BigFraction}.
+ */
+ public BigFraction abs() {
+ return (numerator.signum() == 1) ? this : negate();
+ }
+
+ /**
+ * <p>
+ * Adds the value of this fraction to the passed {@link BigInteger},
+ * returning the result in reduced form.
+ * </p>
+ *
+ * @param bg
+ * the {@link BigInteger} to add, must'nt be <code>null</code>.
+ * @return a <code>BigFraction</code> instance with the resulting values.
+ */
+ public BigFraction add(final BigInteger bg) {
+ checkNotNull(bg, PARAM_NAME_BG);
+
+ if (numerator.signum() == 0) {
+ return new BigFraction(bg);
+ }
+ if (bg.signum() == 0) {
+ return this;
+ }
+
+ return new BigFraction(numerator.add(denominator.multiply(bg)), denominator);
+ }
+
+ /**
+ * <p>
+ * Adds the value of this fraction to the passed {@code integer}, returning
+ * the result in reduced form.
+ * </p>
+ *
+ * @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));
+ }
+
+ /**
+ * <p>
+ * Adds the value of this fraction to the passed {@code long}, returning
+ * the result in reduced form.
+ * </p>
+ *
+ * @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));
+ }
+
+ /**
+ * <p>
+ * Adds the value of this fraction to another, returning the result in
+ * reduced form.
+ * </p>
+ *
+ * @param fraction
+ * the {@link BigFraction} to add, must not be <code>null</code>.
+ * @return a {@link BigFraction} instance with the resulting values.
+ */
+ public BigFraction add(final BigFraction fraction) {
+ checkNotNull(fraction, PARAM_NAME_FRACTION);
+ if (fraction.numerator.signum() == 0) {
+ return this;
+ }
+ if (numerator.signum() == 0) {
+ return fraction;
+ }
+
+ final BigInteger num;
+ final BigInteger den;
+
+ 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);
+
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as a <code>BigDecimal</code>. This calculates the
+ * fraction as the numerator divided by denominator.
+ * </p>
+ *
+ * @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));
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as a <code>BigDecimal</code> following the passed
+ * rounding mode. This calculates the fraction as the numerator divided by
+ * denominator.
+ * </p>
+ *
+ * @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);
+ }
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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);
+ }
+
+ /**
+ * <p>
+ * Compares this object to another based on size.
+ * </p>
+ *
+ * @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)
+ */
+ @Override
+ 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);
+ }
+
+ /**
+ * <p>
+ * Divide the value of this fraction by the passed {@code BigInteger},
+ * ie {@code this * 1 / bg}, returning the result in reduced form.
+ * </p>
+ *
+ * @param bg the {@code BigInteger} to divide by, must not be {@code null}
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws ArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final BigInteger bg) {
+ checkNotNull(bg, PARAM_NAME_BG);
+ if (bg.signum() == 0) {
+ throw new FractionException(FractionException.ERROR_ZERO_DENOMINATOR);
+ }
+ if (numerator.signum() == 0) {
+ return ZERO;
+ }
+ return new BigFraction(numerator, denominator.multiply(bg));
+ }
+
+ /**
+ * <p>
+ * Divide the value of this fraction by the passed {@code int}, ie
+ * {@code this * 1 / i}, returning the result in reduced form.
+ * </p>
+ *
+ * @param i the {@code int} to divide by
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws ArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final int i) {
+ return divide(BigInteger.valueOf(i));
+ }
+
+ /**
+ * <p>
+ * Divide the value of this fraction by the passed {@code long}, ie
+ * {@code this * 1 / l}, returning the result in reduced form.
+ * </p>
+ *
+ * @param l the {@code long} to divide by
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws ArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final long l) {
+ return divide(BigInteger.valueOf(l));
+ }
+
+ /**
+ * <p>
+ * Divide the value of this fraction by another, returning the result in
+ * reduced form.
+ * </p>
+ *
+ * @param fraction Fraction to divide by, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws ArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final BigFraction fraction) {
+ checkNotNull(fraction, PARAM_NAME_FRACTION);
+ if (fraction.numerator.signum() == 0) {
+ throw new FractionException(FractionException.ERROR_ZERO_DENOMINATOR);
+ }
+ if (numerator.signum() == 0) {
+ return ZERO;
+ }
+
+ return multiply(fraction.reciprocal());
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as a {@code double}. This calculates the fraction as
+ * the numerator divided by denominator.
+ * </p>
+ *
+ * @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 = Math.max(numerator.bitLength(),
+ denominator.bitLength()) - Math.getExponent(Double.MAX_VALUE);
+ result = numerator.shiftRight(shift).doubleValue() /
+ denominator.shiftRight(shift).doubleValue();
+ }
+ return result;
+ }
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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;
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as a {@code float}. This calculates the fraction as
+ * the numerator divided by denominator.
+ * </p>
+ *
+ * @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 = Math.max(numerator.bitLength(),
+ denominator.bitLength()) - Math.getExponent(Float.MAX_VALUE);
+ result = numerator.shiftRight(shift).floatValue() /
+ denominator.shiftRight(shift).floatValue();
+ }
+ return result;
+ }
+
+ /**
+ * <p>
+ * Access the denominator as a <code>BigInteger</code>.
+ * </p>
+ *
+ * @return the denominator as a <code>BigInteger</code>.
+ */
+ public BigInteger getDenominator() {
+ return denominator;
+ }
+
+ /**
+ * <p>
+ * Access the denominator as a {@code int}.
+ * </p>
+ *
+ * @return the denominator as a {@code int}.
+ */
+ public int getDenominatorAsInt() {
+ return denominator.intValue();
+ }
+
+ /**
+ * <p>
+ * Access the denominator as a {@code long}.
+ * </p>
+ *
+ * @return the denominator as a {@code long}.
+ */
+ public long getDenominatorAsLong() {
+ return denominator.longValue();
+ }
+
+ /**
+ * <p>
+ * Access the numerator as a <code>BigInteger</code>.
+ * </p>
+ *
+ * @return the numerator as a <code>BigInteger</code>.
+ */
+ public BigInteger getNumerator() {
+ return numerator;
+ }
+
+ /**
+ * <p>
+ * Access the numerator as a {@code int}.
+ * </p>
+ *
+ * @return the numerator as a {@code int}.
+ */
+ public int getNumeratorAsInt() {
+ return numerator.intValue();
+ }
+
+ /**
+ * <p>
+ * Access the numerator as a {@code long}.
+ * </p>
+ *
+ * @return the numerator as a {@code long}.
+ */
+ public long getNumeratorAsLong() {
+ return numerator.longValue();
+ }
+
+ /**
+ * <p>
+ * Gets a hashCode for the fraction.
+ * </p>
+ *
+ * @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();
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as an {@code int}. This returns the whole number part
+ * of the fraction.
+ * </p>
+ *
+ * @return the whole number fraction part.
+ * @see java.lang.Number#intValue()
+ */
+ @Override
+ public int intValue() {
+ return numerator.divide(denominator).intValue();
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as a {@code long}. This returns the whole number part
+ * of the fraction.
+ * </p>
+ *
+ * @return the whole number fraction part.
+ * @see java.lang.Number#longValue()
+ */
+ @Override
+ public long longValue() {
+ return numerator.divide(denominator).longValue();
+ }
+
+ /**
+ * <p>
+ * Multiplies the value of this fraction by the passed
+ * <code>BigInteger</code>, returning the result in reduced form.
+ * </p>
+ *
+ * @param bg the {@code BigInteger} to multiply by.
+ * @return a {@code BigFraction} instance with the resulting values.
+ */
+ public BigFraction multiply(final BigInteger bg) {
+ checkNotNull(bg, PARAM_NAME_BG);
+ if (numerator.signum() == 0 || bg.signum() == 0) {
+ return ZERO;
+ }
+ return new BigFraction(bg.multiply(numerator), denominator);
+ }
+
+ /**
+ * <p>
+ * Multiply the value of this fraction by the passed {@code int}, returning
+ * the result in reduced form.
+ * </p>
+ *
+ * @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));
+ }
+
+ /**
+ * <p>
+ * Multiply the value of this fraction by the passed {@code long},
+ * returning the result in reduced form.
+ * </p>
+ *
+ * @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));
+ }
+
+ /**
+ * <p>
+ * Multiplies the value of this fraction by another, returning the result in
+ * reduced form.
+ * </p>
+ *
+ * @param fraction Fraction to multiply by, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values.
+ */
+ public BigFraction multiply(final BigFraction fraction) {
+ checkNotNull(fraction, PARAM_NAME_FRACTION);
+ if (numerator.signum() == 0 ||
+ fraction.numerator.signum() == 0) {
+ return ZERO;
+ }
+ return new BigFraction(numerator.multiply(fraction.numerator),
+ denominator.multiply(fraction.denominator));
+ }
+
+ /**
+ * <p>
+ * Return the additive inverse of this fraction, returning the result in
+ * reduced form.
+ * </p>
+ *
+ * @return the negation of this fraction.
+ */
+ public BigFraction negate() {
+ return new BigFraction(numerator.negate(), denominator);
+ }
+
+ /**
+ * <p>
+ * Gets the fraction percentage as a {@code double}. This calculates the
+ * fraction as the numerator divided by denominator multiplied by 100.
+ * </p>
+ *
+ * @return the fraction percentage as a {@code double}.
+ */
+ public double percentageValue() {
+ return multiply(ONE_HUNDRED).doubleValue();
+ }
+
+ /**
+ * <p>
+ * Returns a {@code BigFraction} whose value is
+ * {@code (this<sup>exponent</sup>)}, returning the result in reduced form.
+ * </p>
+ *
+ * @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));
+ }
+
+ /**
+ * <p>
+ * Returns a <code>BigFraction</code> whose value is
+ * <tt>(this<sup>exponent</sup>)</tt>, returning the result in reduced form.
+ * </p>
+ *
+ * @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));
+ }
+
+ /**
+ * <p>
+ * Returns a <code>BigFraction</code> whose value is
+ * <tt>(this<sup>exponent</sup>)</tt>, returning the result in reduced form.
+ * </p>
+ *
+ * @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));
+ }
+
+ /**
+ * <p>
+ * Returns a <code>double</code> whose value is
+ * <tt>(this<sup>exponent</sup>)</tt>, returning the result in reduced form.
+ * </p>
+ *
+ * @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 Math.pow(numerator.doubleValue(), exponent) /
+ Math.pow(denominator.doubleValue(), exponent);
+ }
+
+ /**
+ * <p>
+ * Return the multiplicative inverse of this fraction.
+ * </p>
+ *
+ * @return the reciprocal fraction.
+ */
+ public BigFraction reciprocal() {
+ return new BigFraction(denominator, numerator);
+ }
+
+ /**
+ * <p>
+ * Reduce this <code>BigFraction</code> to its lowest terms.
+ * </p>
+ *
+ * @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;
+ }
+ }
+
+ /**
+ * <p>
+ * Subtracts the value of an {@link BigInteger} from the value of this
+ * {@code BigFraction}, returning the result in reduced form.
+ * </p>
+ *
+ * @param bg the {@link BigInteger} to subtract, cannot be {@code null}.
+ * @return a {@code BigFraction} instance with the resulting values.
+ */
+ public BigFraction subtract(final BigInteger bg) {
+ checkNotNull(bg, PARAM_NAME_BG);
+ if (bg.signum() == 0) {
+ return this;
+ }
+ if (numerator.signum() == 0) {
+ return new BigFraction(bg.negate());
+ }
+
+ return new BigFraction(numerator.subtract(denominator.multiply(bg)), denominator);
+ }
+
+ /**
+ * <p>
+ * Subtracts the value of an {@code integer} from the value of this
+ * {@code BigFraction}, returning the result in reduced form.
+ * </p>
+ *
+ * @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));
+ }
+
+ /**
+ * <p>
+ * Subtracts the value of a {@code long} from the value of this
+ * {@code BigFraction}, returning the result in reduced form.
+ * </p>
+ *
+ * @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));
+ }
+
+ /**
+ * <p>
+ * Subtracts the value of another fraction from the value of this one,
+ * returning the result in reduced form.
+ * </p>
+ *
+ * @param fraction {@link BigFraction} to subtract, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values
+ */
+ public BigFraction subtract(final BigFraction fraction) {
+ checkNotNull(fraction, PARAM_NAME_FRACTION);
+ if (fraction.numerator.signum() == 0) {
+ return this;
+ }
+ if (numerator.signum() == 0) {
+ return fraction.negate();
+ }
+
+ final BigInteger num;
+ final BigInteger den;
+ 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);
+
+ }
+
+ /**
+ * <p>
+ * Returns the <code>String</code> representing this fraction, ie
+ * "num / dem" or just "num" if the denominator is one.
+ * </p>
+ *
+ * @return a string representation of the fraction.
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ final String str;
+ if (BigInteger.ONE.equals(denominator)) {
+ str = numerator.toString();
+ } else if (BigInteger.ZERO.equals(numerator)) {
+ str = "0";
+ } else {
+ str = numerator + " / " + denominator;
+ }
+ return str;
+ }
+
+ /**
+ * Check that the argument is not null and throw a NullPointerException
+ * if it is.
+ * @param arg the argument to check
+ * @param argName the name of the argument
+ */
+ private static void checkNotNull(Object arg, String argName) {
+ if (arg == null) {
+ throw new NullPointerException(argName);
+ }
+ }
+
+}
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFractionFormat.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFractionFormat.java
new file mode 100644
index 0000000..822e6a2
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFractionFormat.java
@@ -0,0 +1,282 @@
+/*
+ * 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.numbers.fraction;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * Formats a BigFraction number in proper format or improper format.
+ * <p>
+ * The number format for each of the whole number, numerator and,
+ * denominator can be configured.
+ * </p>
+ */
+public class BigFractionFormat extends AbstractFormat implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -2932167925527338976L;
+
+ /**
+ * Create an improper formatting instance with the default number format
+ * for the numerator and denominator.
+ */
+ public BigFractionFormat() {
+ }
+
+ /**
+ * Create an improper formatting instance with a custom number format for
+ * both the numerator and denominator.
+ * @param format the custom format for both the numerator and denominator.
+ */
+ public BigFractionFormat(final NumberFormat format) {
+ super(format);
+ }
+
+ /**
+ * Create an improper formatting instance with a custom number format for
+ * the numerator and a custom number format for the denominator.
+ * @param numeratorFormat the custom format for the numerator.
+ * @param denominatorFormat the custom format for the denominator.
+ */
+ public BigFractionFormat(final NumberFormat numeratorFormat,
+ final NumberFormat denominatorFormat) {
+ super(numeratorFormat, denominatorFormat);
+ }
+
+ /**
+ * Get the set of locales for which complex formats are available. This
+ * is the same set as the {@link NumberFormat} set.
+ * @return available complex format locales.
+ */
+ public static Locale[] getAvailableLocales() {
+ return NumberFormat.getAvailableLocales();
+ }
+
+ /**
+ * This static method calls formatBigFraction() on a default instance of
+ * BigFractionFormat.
+ *
+ * @param f BigFraction object to format
+ * @return A formatted BigFraction in proper form.
+ */
+ public static String formatBigFraction(final BigFraction f) {
+ return getImproperInstance().format(f);
+ }
+
+ /**
+ * Returns the default complex format for the current locale.
+ * @return the default complex format.
+ */
+ public static BigFractionFormat getImproperInstance() {
+ return getImproperInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default complex format for the given locale.
+ * @param locale the specific locale used by the format.
+ * @return the complex format specific to the given locale.
+ */
+ public static BigFractionFormat getImproperInstance(final Locale locale) {
+ return new BigFractionFormat(getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * Returns the default complex format for the current locale.
+ * @return the default complex format.
+ */
+ public static BigFractionFormat getProperInstance() {
+ return getProperInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default complex format for the given locale.
+ * @param locale the specific locale used by the format.
+ * @return the complex format specific to the given locale.
+ */
+ public static BigFractionFormat getProperInstance(final Locale locale) {
+ return new ProperBigFractionFormat(getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * Formats a {@link BigFraction} object to produce a string. The BigFraction is
+ * output in improper format.
+ *
+ * @param BigFraction the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the
+ * offsets of the alignment field
+ * @return the value passed in as toAppendTo.
+ */
+ public StringBuffer format(final BigFraction BigFraction,
+ final StringBuffer toAppendTo, final FieldPosition pos) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ getNumeratorFormat().format(BigFraction.getNumerator(), toAppendTo, pos);
+ toAppendTo.append(" / ");
+ getDenominatorFormat().format(BigFraction.getDenominator(), toAppendTo, pos);
+
+ return toAppendTo;
+ }
+
+ /**
+ * Formats an object and appends the result to a StringBuffer.
+ * <code>obj</code> must be either a {@link BigFraction} object or a
+ * {@link BigInteger} object or a {@link Number} object. Any other type of
+ * object will result in an {@link IllegalArgumentException} being thrown.
+ *
+ * @param obj the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the
+ * offsets of the alignment field
+ * @return the value passed in as toAppendTo.
+ * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
+ * @throws IllegalArgumentException if <code>obj</code> is not a valid type.
+ */
+ @Override
+ public StringBuffer format(final Object obj,
+ final StringBuffer toAppendTo, final FieldPosition pos) {
+
+ final StringBuffer ret;
+ if (obj instanceof BigFraction) {
+ ret = format((BigFraction) obj, toAppendTo, pos);
+ } else if (obj instanceof BigInteger) {
+ ret = format(new BigFraction((BigInteger) obj), toAppendTo, pos);
+ } else if (obj instanceof Number) {
+ ret = format(new BigFraction(((Number) obj).doubleValue()),
+ toAppendTo, pos);
+ } else {
+ throw new IllegalArgumentException("cannot format given object as a fraction number");
+ }
+
+ return ret;
+ }
+
+ /**
+ * Parses a string to produce a {@link BigFraction} object.
+ * @param source the string to parse
+ * @return the parsed {@link BigFraction} object.
+ * @exception ParseException if the beginning of the specified string
+ * cannot be parsed.
+ */
+ @Override
+ public BigFraction parse(final String source) throws ParseException {
+ final ParsePosition parsePosition = new ParsePosition(0);
+ final BigFraction result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw new FractionParseException(source, parsePosition.getErrorIndex(), BigFraction.class);
+ }
+ return result;
+ }
+
+ /**
+ * Parses a string to produce a {@link BigFraction} object.
+ * This method expects the string to be formatted as an improper BigFraction.
+ * @param source the string to parse
+ * @param pos input/output parsing parameter.
+ * @return the parsed {@link BigFraction} object.
+ */
+ @Override
+ public BigFraction parse(final String source, final ParsePosition pos) {
+ final int initialIndex = pos.getIndex();
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse numerator
+ final BigInteger num = parseNextBigInteger(source, pos);
+ if (num == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse '/'
+ final int startIndex = pos.getIndex();
+ final char c = parseNextCharacter(source, pos);
+ switch (c) {
+ case 0 :
+ // no '/'
+ // return num as a BigFraction
+ return new BigFraction(num);
+ case '/' :
+ // found '/', continue parsing denominator
+ break;
+ default :
+ // invalid '/'
+ // set index back to initial, error index should be the last
+ // character examined.
+ pos.setIndex(initialIndex);
+ pos.setErrorIndex(startIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse denominator
+ final BigInteger den = parseNextBigInteger(source, pos);
+ if (den == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ return new BigFraction(num, den);
+ }
+
+ /**
+ * Parses a string to produce a <code>BigInteger</code>.
+ * @param source the string to parse
+ * @param pos input/output parsing parameter.
+ * @return a parsed <code>BigInteger</code> or null if string does not
+ * contain a BigInteger at the specified position
+ */
+ protected BigInteger parseNextBigInteger(final String source,
+ final ParsePosition pos) {
+
+ final int start = pos.getIndex();
+ int end = (source.charAt(start) == '-') ? (start + 1) : start;
+ while((end < source.length()) &&
+ Character.isDigit(source.charAt(end))) {
+ ++end;
+ }
+
+ try {
+ BigInteger n = new BigInteger(source.substring(start, end));
+ pos.setIndex(end);
+ return n;
+ } catch (NumberFormatException nfe) {
+ pos.setErrorIndex(start);
+ return null;
+ }
+
+ }
+
+}
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/Fraction.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/Fraction.java
new file mode 100644
index 0000000..673d0d5
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/Fraction.java
@@ -0,0 +1,656 @@
+/*
+ * 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.numbers.fraction;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import org.apache.commons.numbers.core.ArithmeticUtils;
+
+/**
+ * Representation of a rational number.
+ *
+ * implements Serializable since 2.0
+ */
+public class Fraction
+ extends Number
+ implements Comparable<Fraction>, Serializable {
+
+ /** A fraction representing "2 / 1". */
+ public static final Fraction TWO = new Fraction(2, 1);
+
+ /** A fraction representing "1". */
+ public static final Fraction ONE = new Fraction(1, 1);
+
+ /** A fraction representing "0". */
+ public static final Fraction ZERO = new Fraction(0, 1);
+
+ /** A fraction representing "4/5". */
+ public static final Fraction FOUR_FIFTHS = new Fraction(4, 5);
+
+ /** A fraction representing "1/5". */
+ public static final Fraction ONE_FIFTH = new Fraction(1, 5);
+
+ /** A fraction representing "1/2". */
+ public static final Fraction ONE_HALF = new Fraction(1, 2);
+
+ /** A fraction representing "1/4". */
+ public static final Fraction ONE_QUARTER = new Fraction(1, 4);
+
+ /** A fraction representing "1/3". */
+ public static final Fraction ONE_THIRD = new Fraction(1, 3);
+
+ /** A fraction representing "3/5". */
+ public static final Fraction THREE_FIFTHS = new Fraction(3, 5);
+
+ /** A fraction representing "3/4". */
+ public static final Fraction THREE_QUARTERS = new Fraction(3, 4);
+
+ /** A fraction representing "2/5". */
+ public static final Fraction TWO_FIFTHS = new Fraction(2, 5);
+
+ /** A fraction representing "2/4". */
+ public static final Fraction TWO_QUARTERS = new Fraction(2, 4);
+
+ /** A fraction representing "2/3". */
+ public static final Fraction TWO_THIRDS = new Fraction(2, 3);
+
+ /** A fraction representing "-1 / 1". */
+ public static final Fraction MINUS_ONE = new Fraction(-1, 1);
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 3698073679419233275L;
+
+ /** Parameter name for fraction (to satisfy checkstyle). */
+ private static final String PARAM_NAME_FRACTION = "fraction";
+
+ /** The default epsilon used for convergence. */
+ private static final double DEFAULT_EPSILON = 1e-5;
+
+ /** The denominator. */
+ private final int denominator;
+
+ /** The numerator. */
+ private final int numerator;
+
+ /**
+ * Create a fraction given the double value.
+ * @param value the double value to convert to a fraction.
+ * @throws IllegalArgumentException if the continued fraction failed to
+ * converge.
+ */
+ public Fraction(double value) {
+ this(value, DEFAULT_EPSILON, 100);
+ }
+
+ /**
+ * 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)</li>
+ * </ul>
+ *
+ * @param value the double value to convert to a fraction.
+ * @param epsilon maximum error allowed. The resulting fraction is within
+ * {@code epsilon} of {@code value}, in absolute terms.
+ * @param maxIterations maximum number of convergents
+ * @throws IllegalArgumentException if the continued fraction failed to
+ * converge.
+ */
+ public Fraction(double value, double epsilon, int maxIterations)
+ {
+ this(value, epsilon, Integer.MAX_VALUE, maxIterations);
+ }
+
+ /**
+ * 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)</li>
+ * </ul>
+ *
+ * @param value the double value to convert to a fraction.
+ * @param maxDenominator The maximum allowed value for denominator
+ * @throws IllegalArgumentException if the continued fraction failed to
+ * converge
+ */
+ public Fraction(double value, int maxDenominator)
+ {
+ this(value, 0, maxDenominator, 100);
+ }
+
+ /**
+ * 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><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.
+ * </p>
+ *
+ * See JIRA issue ticket MATH-181 for more details:
+ *
+ * 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} of {@code value}, in absolute terms.
+ * @param maxDenominator maximum denominator value allowed.
+ * @param maxIterations maximum number of convergents
+ * @throws IllegalArgumentException if the continued fraction failed to
+ * converge.
+ */
+ private Fraction(double value, double epsilon, int maxDenominator, int maxIterations)
+ {
+ long overflow = Integer.MAX_VALUE;
+ double r0 = value;
+ long a0 = (long)Math.floor(r0);
+ if (Math.abs(a0) > overflow) {
+ throw new FractionException(FractionException.ERROR_CONVERSION, value, a0, 1l);
+ }
+
+ // check for (almost) integer arguments, which should not go to iterations.
+ if (Math.abs(a0 - value) < epsilon) {
+ this.numerator = (int) a0;
+ this.denominator = 1;
+ 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;
+ double r1 = 1.0 / (r0 - a0);
+ long a1 = (long)Math.floor(r1);
+ p2 = (a1 * p1) + p0;
+ q2 = (a1 * q1) + q0;
+
+ if ((Math.abs(p2) > overflow) || (Math.abs(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 && Math.abs(q1) < maxDenominator) {
+ break;
+ }
+ throw new FractionException(FractionException.ERROR_CONVERSION, value, p2, q2);
+ }
+
+ double convergent = (double)p2 / (double)q2;
+ if (n < maxIterations && Math.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 FractionException(FractionException.ERROR_CONVERSION, value, maxIterations);
+ }
+
+ if (q2 < maxDenominator) {
+ this.numerator = (int) p2;
+ this.denominator = (int) q2;
+ } else {
+ this.numerator = (int) p1;
+ this.denominator = (int) q1;
+ }
+
+ }
+
+ /**
+ * Create a fraction from an int.
+ * The fraction is num / 1.
+ * @param num the numerator.
+ */
+ public Fraction(int num) {
+ this(num, 1);
+ }
+
+ /**
+ * Create a fraction given the numerator and denominator. The fraction is
+ * reduced to lowest terms.
+ * @param num the numerator.
+ * @param den the denominator.
+ * @throws ArithmeticException if the denominator is {@code zero}
+ * or if integer overflow occurs
+ */
+ public Fraction(int num, int den) {
+ if (den == 0) {
+ throw new ArithmeticException("division by zero");
+ }
+ if (den < 0) {
+ if (num == Integer.MIN_VALUE ||
+ den == Integer.MIN_VALUE) {
+ throw new FractionException(FractionException.ERROR_NEGATION_OVERFLOW, num, den);
+ }
+ num = -num;
+ den = -den;
+ }
+ // reduce numerator and denominator by greatest common denominator.
+ final int d = ArithmeticUtils.gcd(num, den);
+ if (d > 1) {
+ num /= d;
+ den /= d;
+ }
+
+ // move sign to numerator.
+ if (den < 0) {
+ num = -num;
+ den = -den;
+ }
+ this.numerator = num;
+ this.denominator = den;
+ }
+
+ /**
+ * Returns the absolute value of this fraction.
+ * @return the absolute value.
+ */
+ public Fraction abs() {
+ Fraction ret;
+ if (numerator >= 0) {
+ ret = this;
+ } else {
+ ret = negate();
+ }
+ return ret;
+ }
+
+ /**
+ * Compares this object to another based on size.
+ * @param object the object to compare to
+ * @return -1 if this is less than {@code object}, +1 if this is greater
+ * than {@code object}, 0 if they are equal.
+ */
+ @Override
+ public int compareTo(Fraction object) {
+ long nOd = ((long) numerator) * object.denominator;
+ long dOn = ((long) denominator) * object.numerator;
+ return (nOd < dOn) ? -1 : ((nOd > dOn) ? +1 : 0);
+ }
+
+ /**
+ * Gets the fraction as a {@code double}. This calculates the fraction as
+ * the numerator divided by denominator.
+ * @return the fraction as a {@code double}
+ */
+ @Override
+ public double doubleValue() {
+ return (double)numerator / (double)denominator;
+ }
+
+ /**
+ * 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
+ * @return true if two fractions are equal, false if object is
+ * {@code null}, not an instance of {@link Fraction}, or not equal
+ * to this fraction instance.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof Fraction) {
+ // since fractions are always in lowest terms, numerators and
+ // denominators can be compared directly for equality.
+ Fraction rhs = (Fraction)other;
+ return (numerator == rhs.numerator) &&
+ (denominator == rhs.denominator);
+ }
+ return false;
+ }
+
+ /**
+ * Gets the fraction as a {@code float}. This calculates the fraction as
+ * the numerator divided by denominator.
+ * @return the fraction as a {@code float}
+ */
+ @Override
+ public float floatValue() {
+ return (float)doubleValue();
+ }
+
+ /**
+ * Access the denominator.
+ * @return the denominator.
+ */
+ public int getDenominator() {
+ return denominator;
+ }
+
+ /**
+ * Access the numerator.
+ * @return the numerator.
+ */
+ public int getNumerator() {
+ return numerator;
+ }
+
+ /**
+ * Gets a hashCode for the fraction.
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ return 37 * (37 * 17 + numerator) + denominator;
+ }
+
+ /**
+ * Gets the fraction as an {@code int}. This returns the whole number part
+ * of the fraction.
+ * @return the whole number fraction part
+ */
+ @Override
+ public int intValue() {
+ return (int)doubleValue();
+ }
+
+ /**
+ * Gets the fraction as a {@code long}. This returns the whole number part
+ * of the fraction.
+ * @return the whole number fraction part
+ */
+ @Override
+ public long longValue() {
+ return (long)doubleValue();
+ }
+
+ /**
+ * Return the additive inverse of this fraction.
+ * @return the negation of this fraction.
+ */
+ public Fraction negate() {
+ if (numerator==Integer.MIN_VALUE) {
+ throw new FractionException(FractionException.ERROR_NEGATION_OVERFLOW, numerator, denominator);
+ }
+ return new Fraction(-numerator, denominator);
+ }
+
+ /**
+ * Return the multiplicative inverse of this fraction.
+ * @return the reciprocal fraction
+ */
+ public Fraction reciprocal() {
+ return new Fraction(denominator, numerator);
+ }
+
+ /**
+ * <p>Adds the value of this fraction to another, returning the result in reduced form.
+ * The algorithm follows Knuth, 4.5.1.</p>
+ *
+ * @param fraction the fraction to add, must not be {@code null}
+ * @return a {@code Fraction} instance with the resulting values
+ * @throws NullPointerException if the fraction is {@code null}
+ * @throws ArithmeticException if the resulting numerator or denominator exceeds
+ * {@code Integer.MAX_VALUE}
+ */
+ public Fraction add(Fraction fraction) {
+ return addSub(fraction, true /* add */);
+ }
+
+ /**
+ * Add an integer to the fraction.
+ * @param i the {@code integer} to add.
+ * @return this + i
+ */
+ public Fraction add(final int i) {
+ return new Fraction(numerator + i * denominator, denominator);
+ }
+
+ /**
+ * <p>Subtracts the value of another fraction from the value of this one,
+ * returning the result in reduced form.</p>
+ *
+ * @param fraction the fraction to subtract, must not be {@code null}
+ * @return a {@code Fraction} instance with the resulting values
+ * @throws NullPointerException if the fraction is {@code null}
+ * @throws ArithmeticException if the resulting numerator or denominator
+ * cannot be represented in an {@code int}.
+ */
+ public Fraction subtract(Fraction fraction) {
+ return addSub(fraction, false /* subtract */);
+ }
+
+ /**
+ * Subtract an integer from the fraction.
+ * @param i the {@code integer} to subtract.
+ * @return this - i
+ */
+ public Fraction subtract(final int i) {
+ return new Fraction(numerator - i * denominator, denominator);
+ }
+
+ /**
+ * Implement add and subtract using algorithm described in Knuth 4.5.1.
+ *
+ * @param fraction the fraction to subtract, must not be {@code null}
+ * @param isAdd true to add, false to subtract
+ * @return a {@code Fraction} instance with the resulting values
+ * @throws NullPointerException if the fraction is {@code null}
+ * @throws ArithmeticException if the resulting numerator or denominator
+ * cannot be represented in an {@code int}.
+ */
+ private Fraction addSub(Fraction fraction, boolean isAdd) {
+ if (fraction == null) {
+ throw new NullPointerException(PARAM_NAME_FRACTION);
+ }
+ // zero is identity for addition.
+ if (numerator == 0) {
+ return isAdd ? fraction : fraction.negate();
+ }
+ if (fraction.numerator == 0) {
+ return this;
+ }
+ // if denominators are randomly distributed, d1 will be 1 about 61%
+ // of the time.
+ int d1 = ArithmeticUtils.gcd(denominator, fraction.denominator);
+ if (d1==1) {
+ // result is ( (u*v' +/- u'v) / u'v')
+ int uvp = ArithmeticUtils.mulAndCheck(numerator, fraction.denominator);
+ int upv = ArithmeticUtils.mulAndCheck(fraction.numerator, denominator);
+ return new Fraction
+ (isAdd ? ArithmeticUtils.addAndCheck(uvp, upv) :
+ ArithmeticUtils.subAndCheck(uvp, upv),
+ ArithmeticUtils.mulAndCheck(denominator, fraction.denominator));
+ }
+ // the quantity 't' requires 65 bits of precision; see knuth 4.5.1
+ // exercise 7. we're going to use a BigInteger.
+ // t = u(v'/d1) +/- v(u'/d1)
+ BigInteger uvp = BigInteger.valueOf(numerator)
+ .multiply(BigInteger.valueOf(fraction.denominator/d1));
+ BigInteger upv = BigInteger.valueOf(fraction.numerator)
+ .multiply(BigInteger.valueOf(denominator/d1));
+ BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv);
+ // but d2 doesn't need extra precision because
+ // d2 = gcd(t,d1) = gcd(t mod d1, d1)
+ int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue();
+ int d2 = (tmodd1==0)?d1:ArithmeticUtils.gcd(tmodd1, d1);
+
+ // result is (t/d2) / (u'/d1)(v'/d2)
+ BigInteger w = t.divide(BigInteger.valueOf(d2));
+ if (w.bitLength() > 31) {
+ throw new FractionException(
+ "overflow, numerator too large after multiply: {0}", w.toString(), "");
+ }
+ return new Fraction (w.intValue(),
+ ArithmeticUtils.mulAndCheck(denominator/d1,
+ fraction.denominator/d2));
+ }
+
+ /**
+ * <p>Multiplies the value of this fraction by another, returning the
+ * result in reduced form.</p>
+ *
+ * @param fraction the fraction to multiply by, must not be {@code null}
+ * @return a {@code Fraction} instance with the resulting values
+ * @throws NullPointerException if the fraction is {@code null}
+ * @throws ArithmeticException if the resulting numerator or denominator exceeds
+ * {@code Integer.MAX_VALUE}
+ */
+ public Fraction multiply(Fraction fraction) {
+ if (fraction == null) {
+ throw new NullPointerException(PARAM_NAME_FRACTION);
+ }
+ if (numerator == 0 || fraction.numerator == 0) {
+ return ZERO;
+ }
+ // knuth 4.5.1
+ // make sure we don't overflow unless the result *must* overflow.
+ int d1 = ArithmeticUtils.gcd(numerator, fraction.denominator);
+ int d2 = ArithmeticUtils.gcd(fraction.numerator, denominator);
+ return getReducedFraction
+ (ArithmeticUtils.mulAndCheck(numerator/d1, fraction.numerator/d2),
+ ArithmeticUtils.mulAndCheck(denominator/d2, fraction.denominator/d1));
+ }
+
+ /**
+ * Multiply the fraction by an integer.
+ * @param i the {@code integer} to multiply by.
+ * @return this * i
+ */
+ public Fraction multiply(final int i) {
+ return multiply(new Fraction(i));
+ }
+
+ /**
+ * <p>Divide the value of this fraction by another.</p>
+ *
+ * @param fraction the fraction to divide by, must not be {@code null}
+ * @return a {@code Fraction} instance with the resulting values
+ * @throws ArithmeticException if the fraction to divide by is zero
+ * or if the resulting numerator or denominator
+ * exceeds {@code Integer.MAX_VALUE}
+ */
+ public Fraction divide(Fraction fraction) {
+ if (fraction == null) {
+ throw new NullPointerException(PARAM_NAME_FRACTION);
+ }
+ if (fraction.numerator == 0) {
+ throw new FractionException("the fraction to divide by must not be zero: {0}/{1}",
+ fraction.numerator, fraction.denominator);
+ }
+ return multiply(fraction.reciprocal());
+ }
+
+ /**
+ * Divide the fraction by an integer.
+ * @param i the {@code integer} to divide by.
+ * @return this * i
+ */
+ public Fraction divide(final int i) {
+ return divide(new Fraction(i));
+ }
+
+ /**
+ * <p>
+ * Gets the fraction percentage as a {@code double}. This calculates the
+ * fraction as the numerator divided by denominator multiplied by 100.
+ * </p>
+ *
+ * @return the fraction percentage as a {@code double}.
+ */
+ public double percentageValue() {
+ return 100 * doubleValue();
+ }
+
+ /**
+ * <p>Creates a {@code Fraction} instance with the 2 parts
+ * of a fraction Y/Z.</p>
+ *
+ * <p>Any negative signs are resolved to be on the numerator.</p>
+ *
+ * @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}
+ */
+ public static Fraction getReducedFraction(int numerator, int denominator) {
+ if (denominator == 0) {
+ throw new FractionException(FractionException.ERROR_ZERO_DENOMINATOR);
+ }
+ if (numerator==0) {
+ return ZERO; // normalize zero.
+ }
+ // allow 2^k/-2^31 as a valid fraction (where k>0)
+ if (denominator==Integer.MIN_VALUE && (numerator&1)==0) {
+ numerator/=2; denominator/=2;
+ }
+ if (denominator < 0) {
+ if (numerator==Integer.MIN_VALUE ||
+ denominator==Integer.MIN_VALUE) {
+ throw new FractionException(FractionException.ERROR_NEGATION_OVERFLOW, numerator, denominator);
+ }
+ numerator = -numerator;
+ denominator = -denominator;
+ }
+ // simplify fraction.
+ int gcd = ArithmeticUtils.gcd(numerator, denominator);
+ numerator /= gcd;
+ denominator /= gcd;
+ return new Fraction(numerator, denominator);
+ }
+
+ /**
+ * <p>
+ * Returns the {@code String} representing this fraction, ie
+ * "num / dem" or just "num" if the denominator is one.
+ * </p>
+ *
+ * @return a string representation of the fraction.
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ final String str;
+ if (denominator == 1) {
+ str = Integer.toString(numerator);
+ } else if (numerator == 0) {
+ str = "0";
+ } else {
+ str = numerator + " / " + denominator;
+ }
+ return str;
+ }
+}
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionException.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionException.java
new file mode 100644
index 0000000..1eea0d6
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionException.java
@@ -0,0 +1,59 @@
+/*
+ * 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.numbers.fraction;
+
+import java.text.MessageFormat;
+
+/**
+ * Package private exception class with constants for frequently used messages.
+ */
+class FractionException extends ArithmeticException {
+
+ /** Error message for overflow during conversion. */
+ static final String ERROR_CONVERSION_OVERFLOW = "Overflow trying to convert {0} to fraction ({1}/{2})";
+ /** Error message when iterative conversion fails. */
+ static final String ERROR_CONVERSION = "Unable to convert {0} to fraction after {1} iterations";
+ /** Error message for overflow by negation. */
+ static final String ERROR_NEGATION_OVERFLOW = "overflow in fraction {0}/{1}, cannot negate";
+ /** Error message for zero-valued denominator. */
+ static final String ERROR_ZERO_DENOMINATOR = "denominator must be different from 0";
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 201701191744L;
+
+ /** Arguments for formatting the message. */
+ protected Object[] formatArguments;
+
+ /**
+ * Create an exception where the message is constructed by applying
+ * the {@code format()} method from {@code java.text.MessageFormat}.
+ *
+ * @param message the exception message with replaceable parameters
+ * @param formatArguments the arguments for formatting the message
+ */
+ FractionException(String message, Object... formatArguments) {
+ super(message);
+ this.formatArguments = formatArguments;
+ }
+
+ @Override
+ public String getMessage() {
+ return MessageFormat.format(super.getMessage(), formatArguments);
+ }
+
+
+}
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionFormat.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionFormat.java
new file mode 100644
index 0000000..2189724
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionFormat.java
@@ -0,0 +1,260 @@
+/*
+ * 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.numbers.fraction;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * Formats a Fraction number in proper format or improper format. The number
+ * format for each of the whole number, numerator and, denominator can be
+ * configured.
+ */
+public class FractionFormat extends AbstractFormat {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 3008655719530972612L;
+
+ /**
+ * Create an improper formatting instance with the default number format
+ * for the numerator and denominator.
+ */
+ public FractionFormat() {
+ }
+
+ /**
+ * Create an improper formatting instance with a custom number format for
+ * both the numerator and denominator.
+ * @param format the custom format for both the numerator and denominator.
+ */
+ public FractionFormat(final NumberFormat format) {
+ super(format);
+ }
+
+ /**
+ * Create an improper formatting instance with a custom number format for
+ * the numerator and a custom number format for the denominator.
+ * @param numeratorFormat the custom format for the numerator.
+ * @param denominatorFormat the custom format for the denominator.
+ */
+ public FractionFormat(final NumberFormat numeratorFormat,
+ final NumberFormat denominatorFormat) {
+ super(numeratorFormat, denominatorFormat);
+ }
+
+ /**
+ * Get the set of locales for which complex formats are available. This
+ * is the same set as the {@link NumberFormat} set.
+ * @return available complex format locales.
+ */
+ public static Locale[] getAvailableLocales() {
+ return NumberFormat.getAvailableLocales();
+ }
+
+ /**
+ * This static method calls formatFraction() on a default instance of
+ * FractionFormat.
+ *
+ * @param f Fraction object to format
+ * @return a formatted fraction in proper form.
+ */
+ public static String formatFraction(Fraction f) {
+ return getImproperInstance().format(f);
+ }
+
+ /**
+ * Returns the default complex format for the current locale.
+ * @return the default complex format.
+ */
+ public static FractionFormat getImproperInstance() {
+ return getImproperInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default complex format for the given locale.
+ * @param locale the specific locale used by the format.
+ * @return the complex format specific to the given locale.
+ */
+ public static FractionFormat getImproperInstance(final Locale locale) {
+ return new FractionFormat(getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * Returns the default complex format for the current locale.
+ * @return the default complex format.
+ */
+ public static FractionFormat getProperInstance() {
+ return getProperInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default complex format for the given locale.
+ * @param locale the specific locale used by the format.
+ * @return the complex format specific to the given locale.
+ */
+ public static FractionFormat getProperInstance(final Locale locale) {
+ return new ProperFractionFormat(getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * Create a default number format. The default number format is based on
+ * {@link NumberFormat#getNumberInstance(java.util.Locale)} with the only
+ * customizing is the maximum number of fraction digits, which is set to 0.
+ * @return the default number format.
+ */
+ protected static NumberFormat getDefaultNumberFormat() {
+ return getDefaultNumberFormat(Locale.getDefault());
+ }
+
+ /**
+ * Formats a {@link Fraction} object to produce a string. The fraction is
+ * output in improper format.
+ *
+ * @param fraction the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the
+ * offsets of the alignment field
+ * @return the value passed in as toAppendTo.
+ */
+ public StringBuffer format(final Fraction fraction,
+ final StringBuffer toAppendTo, final FieldPosition pos) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ getNumeratorFormat().format(fraction.getNumerator(), toAppendTo, pos);
+ toAppendTo.append(" / ");
+ getDenominatorFormat().format(fraction.getDenominator(), toAppendTo,
+ pos);
+
+ return toAppendTo;
+ }
+
+ /**
+ * Formats an object and appends the result to a StringBuffer. <code>obj</code> must be either a
+ * {@link Fraction} object or a {@link Number} object. Any other type of
+ * object will result in an {@link IllegalArgumentException} being thrown.
+ *
+ * @param obj the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the
+ * offsets of the alignment field
+ * @return the value passed in as toAppendTo.
+ * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
+ * @throws ArithmeticException if the number cannot be converted to a fraction
+ * @throws IllegalArgumentException if <code>obj</code> is not a valid type.
+ */
+ @Override
+ public StringBuffer format(final Object obj,
+ final StringBuffer toAppendTo,
+ final FieldPosition pos) {
+ StringBuffer ret = null;
+
+ if (obj instanceof Fraction) {
+ ret = format((Fraction) obj, toAppendTo, pos);
+ } else if (obj instanceof Number) {
+ ret = format(new Fraction(((Number) obj).doubleValue()), toAppendTo, pos);
+ } else {
+ throw new IllegalArgumentException(
+ "cannot format given object as a fraction number");
+ }
+
+ return ret;
+ }
+
+ /**
+ * Parses a string to produce a {@link Fraction} object.
+ * @param source the string to parse
+ * @return the parsed {@link Fraction} object.
+ * @exception ParseException if the beginning of the specified string
+ * cannot be parsed.
+ */
+ @Override
+ public Fraction parse(final String source) throws ParseException {
+ final ParsePosition parsePosition = new ParsePosition(0);
+ final Fraction result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw new FractionParseException(source, parsePosition.getErrorIndex(), Fraction.class);
+ }
+ return result;
+ }
+
+ /**
+ * Parses a string to produce a {@link Fraction} object. This method
+ * expects the string to be formatted as an improper fraction.
+ * @param source the string to parse
+ * @param pos input/output parsing parameter.
+ * @return the parsed {@link Fraction} object.
+ */
+ @Override
+ public Fraction parse(final String source, final ParsePosition pos) {
+ final int initialIndex = pos.getIndex();
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse numerator
+ final Number num = getNumeratorFormat().parse(source, pos);
+ if (num == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse '/'
+ final int startIndex = pos.getIndex();
+ final char c = parseNextCharacter(source, pos);
+ switch (c) {
+ case 0 :
+ // no '/'
+ // return num as a fraction
+ return new Fraction(num.intValue(), 1);
+ case '/' :
+ // found '/', continue parsing denominator
+ break;
+ default :
+ // invalid '/'
+ // set index back to initial, error index should be the last
+ // character examined.
+ pos.setIndex(initialIndex);
+ pos.setErrorIndex(startIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse denominator
+ final Number den = getDenominatorFormat().parse(source, pos);
+ if (den == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ return new Fraction(num.intValue(), den.intValue());
+ }
+
+}
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionParseException.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionParseException.java
new file mode 100644
index 0000000..08886e0
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionParseException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.numbers.fraction;
+
+import java.text.MessageFormat;
+import java.text.ParseException;
+
+/**
+ * Error thrown when a string cannot be parsed into a fraction.
+ */
+class FractionParseException extends ParseException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 201701181879L;
+
+ /**
+ * Constructs an exception with specified formatted detail message.
+ * Message formatting is delegated to {@link java.text.MessageFormat}.
+ * @param source string being parsed
+ * @param position position of error
+ * @param type type of target object
+ */
+ FractionParseException(String source, int position, Class<?> type) {
+ super(MessageFormat.format("string \"{0}\" unparseable (from position {1}) as an object of type {2}",
+ source, position, type),
+ position);
+ }
+}
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ProperBigFractionFormat.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ProperBigFractionFormat.java
new file mode 100644
index 0000000..7460d0e
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ProperBigFractionFormat.java
@@ -0,0 +1,233 @@
+/*
+ * 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.numbers.fraction;
+
+import java.math.BigInteger;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+
+/**
+ * Formats a BigFraction number in proper format. The number format for each of
+ * the whole number, numerator and, denominator can be configured.
+ * <p>
+ * Minus signs are only allowed in the whole number part - i.e.,
+ * "-3 1/2" is legitimate and denotes -7/2, but "-3 -1/2" is invalid and
+ * will result in a <code>ParseException</code>.</p>
+ */
+public class ProperBigFractionFormat extends BigFractionFormat {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -6337346779577272307L;
+
+ /** The format used for the whole number. */
+ private NumberFormat wholeFormat;
+
+ /**
+ * Create a proper formatting instance with the default number format for
+ * the whole, numerator, and denominator.
+ */
+ public ProperBigFractionFormat() {
+ this(getDefaultNumberFormat());
+ }
+
+ /**
+ * Create a proper formatting instance with a custom number format for the
+ * whole, numerator, and denominator.
+ * @param format the custom format for the whole, numerator, and
+ * denominator.
+ */
+ public ProperBigFractionFormat(final NumberFormat format) {
+ this(format, (NumberFormat)format.clone(), (NumberFormat)format.clone());
+ }
+
+ /**
+ * Create a proper formatting instance with a custom number format for each
+ * of the whole, numerator, and denominator.
+ * @param wholeFormat the custom format for the whole.
+ * @param numeratorFormat the custom format for the numerator.
+ * @param denominatorFormat the custom format for the denominator.
+ */
+ public ProperBigFractionFormat(final NumberFormat wholeFormat,
+ final NumberFormat numeratorFormat,
+ final NumberFormat denominatorFormat) {
+ super(numeratorFormat, denominatorFormat);
+ setWholeFormat(wholeFormat);
+ }
+
+ /**
+ * Formats a {@link BigFraction} object to produce a string. The BigFraction
+ * is output in proper format.
+ *
+ * @param fraction the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the
+ * offsets of the alignment field
+ * @return the value passed in as toAppendTo.
+ */
+ @Override
+ public StringBuffer format(final BigFraction fraction,
+ final StringBuffer toAppendTo, final FieldPosition pos) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ BigInteger num = fraction.getNumerator();
+ BigInteger den = fraction.getDenominator();
+ BigInteger whole = num.divide(den);
+ num = num.remainder(den);
+
+ if (!BigInteger.ZERO.equals(whole)) {
+ getWholeFormat().format(whole, toAppendTo, pos);
+ toAppendTo.append(' ');
+ if (num.compareTo(BigInteger.ZERO) < 0) {
+ num = num.negate();
+ }
+ }
+ getNumeratorFormat().format(num, toAppendTo, pos);
+ toAppendTo.append(" / ");
+ getDenominatorFormat().format(den, toAppendTo, pos);
+
+ return toAppendTo;
+ }
+
+ /**
+ * Access the whole format.
+ * @return the whole format.
+ */
+ public NumberFormat getWholeFormat() {
+ return wholeFormat;
+ }
+
+ /**
+ * Parses a string to produce a {@link BigFraction} object. This method
+ * expects the string to be formatted as a proper BigFraction.
+ * <p>
+ * Minus signs are only allowed in the whole number part - i.e.,
+ * "-3 1/2" is legitimate and denotes -7/2, but "-3 -1/2" is invalid and
+ * will result in a <code>ParseException</code>.</p>
+ *
+ * @param source the string to parse
+ * @param pos input/ouput parsing parameter.
+ * @return the parsed {@link BigFraction} object.
+ */
+ @Override
+ public BigFraction parse(final String source, final ParsePosition pos) {
+ // try to parse improper BigFraction
+ BigFraction ret = super.parse(source, pos);
+ if (ret != null) {
+ return ret;
+ }
+
+ final int initialIndex = pos.getIndex();
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse whole
+ BigInteger whole = parseNextBigInteger(source, pos);
+ if (whole == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse numerator
+ BigInteger num = parseNextBigInteger(source, pos);
+ if (num == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ if (num.compareTo(BigInteger.ZERO) < 0) {
+ // minus signs should be leading, invalid expression
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse '/'
+ final int startIndex = pos.getIndex();
+ final char c = parseNextCharacter(source, pos);
+ switch (c) {
+ case 0 :
+ // no '/'
+ // return num as a BigFraction
+ return new BigFraction(num);
+ case '/' :
+ // found '/', continue parsing denominator
+ break;
+ default :
+ // invalid '/'
+ // set index back to initial, error index should be the last
+ // character examined.
+ pos.setIndex(initialIndex);
+ pos.setErrorIndex(startIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse denominator
+ final BigInteger den = parseNextBigInteger(source, pos);
+ if (den == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ if (den.compareTo(BigInteger.ZERO) < 0) {
+ // minus signs must be leading, invalid
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ boolean wholeIsNeg = whole.compareTo(BigInteger.ZERO) < 0;
+ if (wholeIsNeg) {
+ whole = whole.negate();
+ }
+ num = whole.multiply(den).add(num);
+ if (wholeIsNeg) {
+ num = num.negate();
+ }
+
+ return new BigFraction(num, den);
+
+ }
+
+ /**
+ * Modify the whole format.
+ * @param format The new whole format value.
+ * @throws NullPointerException if {@code format} is {@code null}.
+ */
+ public void setWholeFormat(final NumberFormat format) {
+ if (format == null) {
+ throw new NullPointerException("whole format");
+ }
+ this.wholeFormat = format;
+ }
+}
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ProperFractionFormat.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ProperFractionFormat.java
new file mode 100644
index 0000000..bc857c9
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ProperFractionFormat.java
@@ -0,0 +1,225 @@
+/*
+ * 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.numbers.fraction;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+
+/**
+ * Formats a Fraction number in proper format. The number format for each of
+ * the whole number, numerator and, denominator can be configured.
+ * <p>
+ * Minus signs are only allowed in the whole number part - i.e.,
+ * "-3 1/2" is legitimate and denotes -7/2, but "-3 -1/2" is invalid and
+ * will result in a <code>ParseException</code>.</p>
+ */
+public class ProperFractionFormat extends FractionFormat {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 760934726031766749L;
+
+ /** The format used for the whole number. */
+ private NumberFormat wholeFormat;
+
+ /**
+ * Create a proper formatting instance with the default number format for
+ * the whole, numerator, and denominator.
+ */
+ public ProperFractionFormat() {
+ this(getDefaultNumberFormat());
+ }
+
+ /**
+ * Create a proper formatting instance with a custom number format for the
+ * whole, numerator, and denominator.
+ * @param format the custom format for the whole, numerator, and
+ * denominator.
+ */
+ public ProperFractionFormat(NumberFormat format) {
+ this(format, (NumberFormat)format.clone(), (NumberFormat)format.clone());
+ }
+
+ /**
+ * Create a proper formatting instance with a custom number format for each
+ * of the whole, numerator, and denominator.
+ * @param wholeFormat the custom format for the whole.
+ * @param numeratorFormat the custom format for the numerator.
+ * @param denominatorFormat the custom format for the denominator.
+ */
+ public ProperFractionFormat(NumberFormat wholeFormat,
+ NumberFormat numeratorFormat,
+ NumberFormat denominatorFormat)
+ {
+ super(numeratorFormat, denominatorFormat);
+ setWholeFormat(wholeFormat);
+ }
+
+ /**
+ * Formats a {@link Fraction} object to produce a string. The fraction
+ * is output in proper format.
+ *
+ * @param fraction the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the
+ * offsets of the alignment field
+ * @return the value passed in as toAppendTo.
+ */
+ @Override
+ public StringBuffer format(Fraction fraction, StringBuffer toAppendTo,
+ FieldPosition pos) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ int num = fraction.getNumerator();
+ int den = fraction.getDenominator();
+ int whole = num / den;
+ num %= den;
+
+ if (whole != 0) {
+ getWholeFormat().format(whole, toAppendTo, pos);
+ toAppendTo.append(' ');
+ num = Math.abs(num);
+ }
+ getNumeratorFormat().format(num, toAppendTo, pos);
+ toAppendTo.append(" / ");
+ getDenominatorFormat().format(den, toAppendTo, pos);
+
+ return toAppendTo;
+ }
+
+ /**
+ * Access the whole format.
+ * @return the whole format.
+ */
+ public NumberFormat getWholeFormat() {
+ return wholeFormat;
+ }
+
+ /**
+ * Parses a string to produce a {@link Fraction} object. This method
+ * expects the string to be formatted as a proper fraction.
+ * <p>
+ * Minus signs are only allowed in the whole number part - i.e.,
+ * "-3 1/2" is legitimate and denotes -7/2, but "-3 -1/2" is invalid and
+ * will result in a <code>ParseException</code>.</p>
+ *
+ * @param source the string to parse
+ * @param pos input/ouput parsing parameter.
+ * @return the parsed {@link Fraction} object.
+ */
+ @Override
+ public Fraction parse(String source, ParsePosition pos) {
+ // try to parse improper fraction
+ Fraction ret = super.parse(source, pos);
+ if (ret != null) {
+ return ret;
+ }
+
+ int initialIndex = pos.getIndex();
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse whole
+ Number whole = getWholeFormat().parse(source, pos);
+ if (whole == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse numerator
+ Number num = getNumeratorFormat().parse(source, pos);
+ if (num == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ if (num.intValue() < 0) {
+ // minus signs should be leading, invalid expression
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse '/'
+ int startIndex = pos.getIndex();
+ char c = parseNextCharacter(source, pos);
+ switch (c) {
+ case 0 :
+ // no '/'
+ // return num as a fraction
+ return new Fraction(num.intValue(), 1);
+ case '/' :
+ // found '/', continue parsing denominator
+ break;
+ default :
+ // invalid '/'
+ // set index back to initial, error index should be the last
+ // character examined.
+ pos.setIndex(initialIndex);
+ pos.setErrorIndex(startIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse denominator
+ Number den = getDenominatorFormat().parse(source, pos);
+ if (den == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ if (den.intValue() < 0) {
+ // minus signs must be leading, invalid
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ int w = whole.intValue();
+ int sign = w >= 0 ? 1 : -1;
+ int n = num.intValue();
+ int d = den.intValue();
+ return new Fraction(((Math.abs(w) * d) + n) * sign, d);
+ }
+
+ /**
+ * Modify the whole format.
+ * @param format The new whole format value.
+ * @throws NullPointerException if {@code format} is {@code null}.
+ */
+ public void setWholeFormat(NumberFormat format) {
+ if (format == null) {
+ throw new NullPointerException("whole format");
+ }
+ this.wholeFormat = format;
+ }
+}
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/package-info.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/package-info.java
new file mode 100644
index 0000000..94bf784
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+/**
+ *
+ * Fraction number type and fraction number formatting.
+ *
+ */
+package org.apache.commons.numbers.fraction;
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionFormatTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionFormatTest.java
new file mode 100644
index 0000000..7998986
--- /dev/null
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionFormatTest.java
@@ -0,0 +1,331 @@
+/*
+ * 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.numbers.fraction;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class BigFractionFormatTest {
+
+ BigFractionFormat properFormat = null;
+ BigFractionFormat improperFormat = null;
+
+ protected Locale getLocale() {
+ return Locale.getDefault();
+ }
+
+ @Before
+ public void setUp() {
+ properFormat = BigFractionFormat.getProperInstance(getLocale());
+ improperFormat = BigFractionFormat.getImproperInstance(getLocale());
+ }
+
+ @Test
+ public void testFormat() {
+ BigFraction c = new BigFraction(1, 2);
+ String expected = "1 / 2";
+
+ String actual = properFormat.format(c);
+ Assert.assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testFormatNegative() {
+ BigFraction c = new BigFraction(-1, 2);
+ String expected = "-1 / 2";
+
+ String actual = properFormat.format(c);
+ Assert.assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testFormatZero() {
+ BigFraction c = new BigFraction(0, 1);
+ String expected = "0 / 1";
+
+ String actual = properFormat.format(c);
+ Assert.assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testFormatImproper() {
+ BigFraction c = new BigFraction(5, 3);
+
+ String actual = properFormat.format(c);
+ Assert.assertEquals("1 2 / 3", actual);
+
+ actual = improperFormat.format(c);
+ Assert.assertEquals("5 / 3", actual);
+ }
+
+ @Test
+ public void testFormatImproperNegative() {
+ BigFraction c = new BigFraction(-5, 3);
+
+ String actual = properFormat.format(c);
+ Assert.assertEquals("-1 2 / 3", actual);
+
+ actual = improperFormat.format(c);
+ Assert.assertEquals("-5 / 3", actual);
+ }
+
+ @Test
+ public void testParse() throws Exception {
+ String source = "1 / 2";
+
+ {
+ BigFraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(BigInteger.ONE, c.getNumerator());
+ Assert.assertEquals(BigInteger.valueOf(2l), c.getDenominator());
+
+ c = improperFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(BigInteger.ONE, c.getNumerator());
+ Assert.assertEquals(BigInteger.valueOf(2l), c.getDenominator());
+ }
+ }
+
+ @Test
+ public void testParseInteger() throws Exception {
+ String source = "10";
+ {
+ BigFraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(BigInteger.TEN, c.getNumerator());
+ Assert.assertEquals(BigInteger.ONE, c.getDenominator());
+ }
+ {
+ BigFraction c = improperFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(BigInteger.TEN, c.getNumerator());
+ Assert.assertEquals(BigInteger.ONE, c.getDenominator());
+ }
+ }
+
+ @Test
+ public void testParseInvalid() {
+ String source = "a";
+ String msg = "should not be able to parse '10 / a'.";
+ try {
+ properFormat.parse(source);
+ Assert.fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ try {
+ improperFormat.parse(source);
+ Assert.fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ @Test
+ public void testParseInvalidDenominator() {
+ String source = "10 / a";
+ String msg = "should not be able to parse '10 / a'.";
+ try {
+ properFormat.parse(source);
+ Assert.fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ try {
+ improperFormat.parse(source);
+ Assert.fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ @Test
+ public void testParseNegative() throws Exception {
+
+ {
+ String source = "-1 / 2";
+ BigFraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(-1, c.getNumeratorAsInt());
+ Assert.assertEquals(2, c.getDenominatorAsInt());
+
+ c = improperFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(-1, c.getNumeratorAsInt());
+ Assert.assertEquals(2, c.getDenominatorAsInt());
+
+ source = "1 / -2";
+ c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(-1, c.getNumeratorAsInt());
+ Assert.assertEquals(2, c.getDenominatorAsInt());
+
+ c = improperFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(-1, c.getNumeratorAsInt());
+ Assert.assertEquals(2, c.getDenominatorAsInt());
+ }
+ }
+
+ @Test
+ public void testParseProper() throws Exception {
+ String source = "1 2 / 3";
+
+ {
+ BigFraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(5, c.getNumeratorAsInt());
+ Assert.assertEquals(3, c.getDenominatorAsInt());
+ }
+
+ try {
+ improperFormat.parse(source);
+ Assert.fail("invalid improper fraction.");
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ @Test
+ public void testParseProperNegative() throws Exception {
+ String source = "-1 2 / 3";
+ {
+ BigFraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(-5, c.getNumeratorAsInt());
+ Assert.assertEquals(3, c.getDenominatorAsInt());
+ }
+
+ try {
+ improperFormat.parse(source);
+ Assert.fail("invalid improper fraction.");
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ @Test
+ public void testParseProperInvalidMinus() {
+ String source = "2 -2 / 3";
+ try {
+ properFormat.parse(source);
+ Assert.fail("invalid minus in improper fraction.");
+ } catch (ParseException ex) {
+ // expected
+ }
+ source = "2 2 / -3";
+ try {
+ properFormat.parse(source);
+ Assert.fail("invalid minus in improper fraction.");
+ } catch (ParseException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testParseBig() throws Exception {
+ BigFraction f1 =
+ improperFormat.parse("167213075789791382630275400487886041651764456874403" +
+ " / " +
+ "53225575123090058458126718248444563466137046489291");
+ Assert.assertEquals(Math.PI, f1.doubleValue(), 0.0);
+ BigFraction f2 =
+ properFormat.parse("3 " +
+ "7536350420521207255895245742552351253353317406530" +
+ " / " +
+ "53225575123090058458126718248444563466137046489291");
+ Assert.assertEquals(Math.PI, f2.doubleValue(), 0.0);
+ Assert.assertEquals(f1, f2);
+ BigDecimal pi =
+ new BigDecimal("3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068");
+ Assert.assertEquals(pi, f1.bigDecimalValue(99, BigDecimal.ROUND_HALF_EVEN));
+ }
+
+ @Test
+ public void testNumeratorFormat() {
+ NumberFormat old = properFormat.getNumeratorFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ properFormat.setNumeratorFormat(nf);
+ Assert.assertEquals(nf, properFormat.getNumeratorFormat());
+ properFormat.setNumeratorFormat(old);
+
+ old = improperFormat.getNumeratorFormat();
+ nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ improperFormat.setNumeratorFormat(nf);
+ Assert.assertEquals(nf, improperFormat.getNumeratorFormat());
+ improperFormat.setNumeratorFormat(old);
+ }
+
+ @Test
+ public void testDenominatorFormat() {
+ NumberFormat old = properFormat.getDenominatorFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ properFormat.setDenominatorFormat(nf);
+ Assert.assertEquals(nf, properFormat.getDenominatorFormat());
+ properFormat.setDenominatorFormat(old);
+
+ old = improperFormat.getDenominatorFormat();
+ nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ improperFormat.setDenominatorFormat(nf);
+ Assert.assertEquals(nf, improperFormat.getDenominatorFormat());
+ improperFormat.setDenominatorFormat(old);
+ }
+
+ @Test
+ public void testWholeFormat() {
+ ProperBigFractionFormat format = (ProperBigFractionFormat)properFormat;
+
+ NumberFormat old = format.getWholeFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ format.setWholeFormat(nf);
+ Assert.assertEquals(nf, format.getWholeFormat());
+ format.setWholeFormat(old);
+ }
+
+ @Test
+ public void testLongFormat() {
+ Assert.assertEquals("10 / 1", improperFormat.format(10l));
+ }
+
+ @Test
+ public void testDoubleFormat() {
+ Assert.assertEquals("1 / 16", improperFormat.format(0.0625));
+ }
+}
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java
new file mode 100644
index 0000000..55982ea
--- /dev/null
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java
@@ -0,0 +1,634 @@
+/*
+ * 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.numbers.fraction;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import org.apache.commons.numbers.core.TestUtils;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class BigFractionTest {
+
+ private void assertFraction(int expectedNumerator, int expectedDenominator, BigFraction actual) {
+ Assert.assertEquals(expectedNumerator, actual.getNumeratorAsInt());
+ Assert.assertEquals(expectedDenominator, actual.getDenominatorAsInt());
+ }
+
+ private void assertFraction(long expectedNumerator, long expectedDenominator, BigFraction actual) {
+ Assert.assertEquals(expectedNumerator, actual.getNumeratorAsLong());
+ Assert.assertEquals(expectedDenominator, actual.getDenominatorAsLong());
+ }
+
+ @Test
+ public void testConstructor() {
+ assertFraction(0, 1, new BigFraction(0, 1));
+ assertFraction(0, 1, new BigFraction(0l, 2l));
+ assertFraction(0, 1, new BigFraction(0, -1));
+ assertFraction(1, 2, new BigFraction(1, 2));
+ assertFraction(1, 2, new BigFraction(2, 4));
+ assertFraction(-1, 2, new BigFraction(-1, 2));
+ assertFraction(-1, 2, new BigFraction(1, -2));
+ assertFraction(-1, 2, new BigFraction(-2, 4));
+ assertFraction(-1, 2, new BigFraction(2, -4));
+ assertFraction(11, 1, new BigFraction(11));
+ assertFraction(11, 1, new BigFraction(11l));
+ assertFraction(11, 1, new BigFraction(new BigInteger("11")));
+
+ assertFraction(0, 1, new BigFraction(0.00000000000001, 1.0e-5, 100));
+ assertFraction(2, 5, new BigFraction(0.40000000000001, 1.0e-5, 100));
+ assertFraction(15, 1, new BigFraction(15.0000000000001, 1.0e-5, 100));
+
+ Assert.assertEquals(0.00000000000001, new BigFraction(0.00000000000001).doubleValue(), 0.0);
+ Assert.assertEquals(0.40000000000001, new BigFraction(0.40000000000001).doubleValue(), 0.0);
+ Assert.assertEquals(15.0000000000001, new BigFraction(15.0000000000001).doubleValue(), 0.0);
+ assertFraction(3602879701896487l, 9007199254740992l, new BigFraction(0.40000000000001));
+ assertFraction(1055531162664967l, 70368744177664l, new BigFraction(15.0000000000001));
+ try {
+ new BigFraction(null, BigInteger.ONE);
+ Assert.fail("Expecting NullPointerException");
+ } catch (NullPointerException npe) {
+ // expected
+ }
+ try {
+ new BigFraction(BigInteger.ONE, null);
+ Assert.fail("Expecting NullPointerException");
+ } catch (NullPointerException npe) {
+ // expected
+ }
+ try {
+ new BigFraction(BigInteger.ONE, BigInteger.ZERO);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ignored) {
+ // expected
+ }
+ try {
+ new BigFraction(2.0 * Integer.MAX_VALUE, 1.0e-5, 100000);
+ Assert.fail("Expecting ArithmeticException");
+ } catch (ArithmeticException ignored) {
+ // expected
+ }
+ }
+
+ @Test(expected=FractionException.class)
+ public void testGoldenRatio() {
+ // the golden ratio is notoriously a difficult number for continuous fraction
+ new BigFraction((1 + Math.sqrt(5)) / 2, 1.0e-12, 25);
+ }
+
+ // MATH-179
+ @Test
+ public void testDoubleConstructor() throws Exception {
+ assertFraction(1, 2, new BigFraction((double) 1 / (double) 2, 1.0e-5, 100));
+ assertFraction(1, 3, new BigFraction((double) 1 / (double) 3, 1.0e-5, 100));
+ assertFraction(2, 3, new BigFraction((double) 2 / (double) 3, 1.0e-5, 100));
+ assertFraction(1, 4, new BigFraction((double) 1 / (double) 4, 1.0e-5, 100));
+ assertFraction(3, 4, new BigFraction((double) 3 / (double) 4, 1.0e-5, 100));
+ assertFraction(1, 5, new BigFraction((double) 1 / (double) 5, 1.0e-5, 100));
+ assertFraction(2, 5, new BigFraction((double) 2 / (double) 5, 1.0e-5, 100));
+ assertFraction(3, 5, new BigFraction((double) 3 / (double) 5, 1.0e-5, 100));
+ assertFraction(4, 5, new BigFraction((double) 4 / (double) 5, 1.0e-5, 100));
+ assertFraction(1, 6, new BigFraction((double) 1 / (double) 6, 1.0e-5, 100));
+ assertFraction(5, 6, new BigFraction((double) 5 / (double) 6, 1.0e-5, 100));
+ assertFraction(1, 7, new BigFraction((double) 1 / (double) 7, 1.0e-5, 100));
+ assertFraction(2, 7, new BigFraction((double) 2 / (double) 7, 1.0e-5, 100));
+ assertFraction(3, 7, new BigFraction((double) 3 / (double) 7, 1.0e-5, 100));
+ assertFraction(4, 7, new BigFraction((double) 4 / (double) 7, 1.0e-5, 100));
+ assertFraction(5, 7, new BigFraction((double) 5 / (double) 7, 1.0e-5, 100));
+ assertFraction(6, 7, new BigFraction((double) 6 / (double) 7, 1.0e-5, 100));
+ assertFraction(1, 8, new BigFraction((double) 1 / (double) 8, 1.0e-5, 100));
+ assertFraction(3, 8, new BigFraction((double) 3 / (double) 8, 1.0e-5, 100));
+ assertFraction(5, 8, new BigFraction((double) 5 / (double) 8, 1.0e-5, 100));
+ assertFraction(7, 8, new BigFraction((double) 7 / (double) 8, 1.0e-5, 100));
+ assertFraction(1, 9, new BigFraction((double) 1 / (double) 9, 1.0e-5, 100));
+ assertFraction(2, 9, new BigFraction((double) 2 / (double) 9, 1.0e-5, 100));
+ assertFraction(4, 9, new BigFraction((double) 4 / (double) 9, 1.0e-5, 100));
+ assertFraction(5, 9, new BigFraction((double) 5 / (double) 9, 1.0e-5, 100));
+ assertFraction(7, 9, new BigFraction((double) 7 / (double) 9, 1.0e-5, 100));
+ assertFraction(8, 9, new BigFraction((double) 8 / (double) 9, 1.0e-5, 100));
+ assertFraction(1, 10, new BigFraction((double) 1 / (double) 10, 1.0e-5, 100));
+ assertFraction(3, 10, new BigFraction((double) 3 / (double) 10, 1.0e-5, 100));
+ assertFraction(7, 10, new BigFraction((double) 7 / (double) 10, 1.0e-5, 100));
+ assertFraction(9, 10, new BigFraction((double) 9 / (double) 10, 1.0e-5, 100));
+ assertFraction(1, 11, new BigFraction((double) 1 / (double) 11, 1.0e-5, 100));
+ assertFraction(2, 11, new BigFraction((double) 2 / (double) 11, 1.0e-5, 100));
+ assertFraction(3, 11, new BigFraction((double) 3 / (double) 11, 1.0e-5, 100));
+ assertFraction(4, 11, new BigFraction((double) 4 / (double) 11, 1.0e-5, 100));
+ assertFraction(5, 11, new BigFraction((double) 5 / (double) 11, 1.0e-5, 100));
+ assertFraction(6, 11, new BigFraction((double) 6 / (double) 11, 1.0e-5, 100));
+ assertFraction(7, 11, new BigFraction((double) 7 / (double) 11, 1.0e-5, 100));
+ assertFraction(8, 11, new BigFraction((double) 8 / (double) 11, 1.0e-5, 100));
+ assertFraction(9, 11, new BigFraction((double) 9 / (double) 11, 1.0e-5, 100));
+ assertFraction(10, 11, new BigFraction((double) 10 / (double) 11, 1.0e-5, 100));
+ }
+
+ // MATH-181
+ @Test
+ public void testDigitLimitConstructor() throws Exception {
+ assertFraction(2, 5, new BigFraction(0.4, 9));
+ assertFraction(2, 5, new BigFraction(0.4, 99));
+ assertFraction(2, 5, new BigFraction(0.4, 999));
+
+ assertFraction(3, 5, new BigFraction(0.6152, 9));
+ assertFraction(8, 13, new BigFraction(0.6152, 99));
+ assertFraction(510, 829, new BigFraction(0.6152, 999));
+ assertFraction(769, 1250, new BigFraction(0.6152, 9999));
+
+ // MATH-996
+ assertFraction(1, 2, new BigFraction(0.5000000001, 10));
+ }
+
+ // MATH-1029
+ @Test(expected=ArithmeticException.class)
+ public void testPositiveValueOverflow() {
+ assertFraction((long) 1e10, 1, new BigFraction(1e10, 1000));
+ }
+
+ // MATH-1029
+ @Test(expected=ArithmeticException.class)
+ public void testNegativeValueOverflow() {
+ assertFraction((long) -1e10, 1, new BigFraction(-1e10, 1000));
+ }
+
+ @Test
+ public void testEpsilonLimitConstructor() throws Exception {
+ assertFraction(2, 5, new BigFraction(0.4, 1.0e-5, 100));
+
+ assertFraction(3, 5, new BigFraction(0.6152, 0.02, 100));
+ assertFraction(8, 13, new BigFraction(0.6152, 1.0e-3, 100));
+ assertFraction(251, 408, new BigFraction(0.6152, 1.0e-4, 100));
+ assertFraction(251, 408, new BigFraction(0.6152, 1.0e-5, 100));
+ assertFraction(510, 829, new BigFraction(0.6152, 1.0e-6, 100));
+ assertFraction(769, 1250, new BigFraction(0.6152, 1.0e-7, 100));
+ }
+
+ @Test
+ public void testCompareTo() {
+ BigFraction first = new BigFraction(1, 2);
+ BigFraction second = new BigFraction(1, 3);
+ BigFraction third = new BigFraction(1, 2);
+
+ Assert.assertEquals(0, first.compareTo(first));
+ Assert.assertEquals(0, first.compareTo(third));
+ Assert.assertEquals(1, first.compareTo(second));
+ Assert.assertEquals(-1, second.compareTo(first));
+
+ // these two values are different approximations of PI
+ // the first one is approximately PI - 3.07e-18
+ // the second one is approximately PI + 1.936e-17
+ BigFraction pi1 = new BigFraction(1068966896, 340262731);
+ BigFraction pi2 = new BigFraction( 411557987, 131002976);
+ Assert.assertEquals(-1, pi1.compareTo(pi2));
+ Assert.assertEquals( 1, pi2.compareTo(pi1));
+ Assert.assertEquals(0.0, pi1.doubleValue() - pi2.doubleValue(), 1.0e-20);
+
+ }
+
+ @Test
+ public void testDoubleValue() {
+ BigFraction first = new BigFraction(1, 2);
+ BigFraction second = new BigFraction(1, 3);
+
+ Assert.assertEquals(0.5, first.doubleValue(), 0.0);
+ Assert.assertEquals(1.0 / 3.0, second.doubleValue(), 0.0);
+ }
+
+ // MATH-744
+ @Test
+ public void testDoubleValueForLargeNumeratorAndDenominator() {
+ final BigInteger pow400 = BigInteger.TEN.pow(400);
+ final BigInteger pow401 = BigInteger.TEN.pow(401);
+ final BigInteger two = new BigInteger("2");
+ final BigFraction large = new BigFraction(pow401.add(BigInteger.ONE),
+ pow400.multiply(two));
+
+ Assert.assertEquals(5, large.doubleValue(), 1e-15);
+ }
+
+ // MATH-744
+ @Test
+ public void testFloatValueForLargeNumeratorAndDenominator() {
+ final BigInteger pow400 = BigInteger.TEN.pow(400);
+ final BigInteger pow401 = BigInteger.TEN.pow(401);
+ final BigInteger two = new BigInteger("2");
+ final BigFraction large = new BigFraction(pow401.add(BigInteger.ONE),
+ pow400.multiply(two));
+
+ Assert.assertEquals(5, large.floatValue(), 1e-15);
+ }
+
+ @Test
+ public void testFloatValue() {
+ BigFraction first = new BigFraction(1, 2);
+ BigFraction second = new BigFraction(1, 3);
+
+ Assert.assertEquals(0.5f, first.floatValue(), 0.0f);
+ Assert.assertEquals((float) (1.0 / 3.0), second.floatValue(), 0.0f);
+ }
+
+ @Test
+ public void testIntValue() {
+ BigFraction first = new BigFraction(1, 2);
+ BigFraction second = new BigFraction(3, 2);
+
+ Assert.assertEquals(0, first.intValue());
+ Assert.assertEquals(1, second.intValue());
+ }
+
+ @Test
+ public void testLongValue() {
+ BigFraction first = new BigFraction(1, 2);
+ BigFraction second = new BigFraction(3, 2);
+
+ Assert.assertEquals(0L, first.longValue());
+ Assert.assertEquals(1L, second.longValue());
+ }
+
+ @Test
+ public void testConstructorDouble() {
+ assertFraction(1, 2, new BigFraction(0.5));
+ assertFraction(6004799503160661l, 18014398509481984l, new BigFraction(1.0 / 3.0));
+ assertFraction(6124895493223875l, 36028797018963968l, new BigFraction(17.0 / 100.0));
+ assertFraction(1784551352345559l, 562949953421312l, new BigFraction(317.0 / 100.0));
+ assertFraction(-1, 2, new BigFraction(-0.5));
+ assertFraction(-6004799503160661l, 18014398509481984l, new BigFraction(-1.0 / 3.0));
+ assertFraction(-6124895493223875l, 36028797018963968l, new BigFraction(17.0 / -100.0));
+ assertFraction(-1784551352345559l, 562949953421312l, new BigFraction(-317.0 / 100.0));
+ for (double v : new double[] { Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY}) {
+ try {
+ new BigFraction(v);
+ Assert.fail("Expecting IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+ }
+ Assert.assertEquals(1l, new BigFraction(Double.MAX_VALUE).getDenominatorAsLong());
+ Assert.assertEquals(1l, new BigFraction(Double.longBitsToDouble(0x0010000000000000L)).getNumeratorAsLong());
+ Assert.assertEquals(1l, new BigFraction(Double.MIN_VALUE).getNumeratorAsLong());
+ }
+
+ @Test
+ public void testAbs() {
+ BigFraction a = new BigFraction(10, 21);
+ BigFraction b = new BigFraction(-10, 21);
+ BigFraction c = new BigFraction(10, -21);
+
+ assertFraction(10, 21, a.abs());
+ assertFraction(10, 21, b.abs());
+ assertFraction(10, 21, c.abs());
+ }
+
+ @Test
+ public void testReciprocal() {
+ BigFraction f = null;
+
+ f = new BigFraction(50, 75);
+ f = f.reciprocal();
+ Assert.assertEquals(3, f.getNumeratorAsInt());
+ Assert.assertEquals(2, f.getDenominatorAsInt());
+
+ f = new BigFraction(4, 3);
+ f = f.reciprocal();
+ Assert.assertEquals(3, f.getNumeratorAsInt());
+ Assert.assertEquals(4, f.getDenominatorAsInt());
+
+ f = new BigFraction(-15, 47);
+ f = f.reciprocal();
+ Assert.assertEquals(-47, f.getNumeratorAsInt());
+ Assert.assertEquals(15, f.getDenominatorAsInt());
+
+ f = new BigFraction(0, 3);
+ try {
+ f = f.reciprocal();
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ignored) {
+ }
+
+ // large values
+ f = new BigFraction(Integer.MAX_VALUE, 1);
+ f = f.reciprocal();
+ Assert.assertEquals(1, f.getNumeratorAsInt());
+ Assert.assertEquals(Integer.MAX_VALUE, f.getDenominatorAsInt());
+ }
+
+ @Test
+ public void testNegate() {
+ BigFraction f = null;
+
+ f = new BigFraction(50, 75);
+ f = f.negate();
+ Assert.assertEquals(-2, f.getNumeratorAsInt());
+ Assert.assertEquals(3, f.getDenominatorAsInt());
+
+ f = new BigFraction(-50, 75);
+ f = f.negate();
+ Assert.assertEquals(2, f.getNumeratorAsInt());
+ Assert.assertEquals(3, f.getDenominatorAsInt());
+
+ // large values
+ f = new BigFraction(Integer.MAX_VALUE - 1, Integer.MAX_VALUE);
+ f = f.negate();
+ Assert.assertEquals(Integer.MIN_VALUE + 2, f.getNumeratorAsInt());
+ Assert.assertEquals(Integer.MAX_VALUE, f.getDenominatorAsInt());
+
+ }
+
+ @Test
+ public void testAdd() {
+ BigFraction a = new BigFraction(1, 2);
+ BigFraction b = new BigFraction(2, 3);
+
+ assertFraction(1, 1, a.add(a));
+ assertFraction(7, 6, a.add(b));
+ assertFraction(7, 6, b.add(a));
+ assertFraction(4, 3, b.add(b));
+
+ BigFraction f1 = new BigFraction(Integer.MAX_VALUE - 1, 1);
+ BigFraction f2 = BigFraction.ONE;
+ BigFraction f = f1.add(f2);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(-1, 13 * 13 * 2 * 2);
+ f2 = new BigFraction(-2, 13 * 17 * 2);
+ f = f1.add(f2);
+ Assert.assertEquals(13 * 13 * 17 * 2 * 2, f.getDenominatorAsInt());
+ Assert.assertEquals(-17 - 2 * 13 * 2, f.getNumeratorAsInt());
+
+ try {
+ f.add((BigFraction) null);
+ Assert.fail("expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ }
+
+ // if this fraction is added naively, it will overflow.
+ // check that it doesn't.
+ f1 = new BigFraction(1, 32768 * 3);
+ f2 = new BigFraction(1, 59049);
+ f = f1.add(f2);
+ Assert.assertEquals(52451, f.getNumeratorAsInt());
+ Assert.assertEquals(1934917632, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MIN_VALUE, 3);
+ f2 = new BigFraction(1, 3);
+ f = f1.add(f2);
+ Assert.assertEquals(Integer.MIN_VALUE + 1, f.getNumeratorAsInt());
+ Assert.assertEquals(3, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MAX_VALUE - 1, 1);
+ f = f1.add(BigInteger.ONE);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ f = f.add(BigInteger.ZERO);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MAX_VALUE - 1, 1);
+ f = f1.add(1);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ f = f.add(0);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MAX_VALUE - 1, 1);
+ f = f1.add(1l);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ f = f.add(0l);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ }
+
+ @Test
+ public void testDivide() {
+ BigFraction a = new BigFraction(1, 2);
+ BigFraction b = new BigFraction(2, 3);
+
+ assertFraction(1, 1, a.divide(a));
+ assertFraction(3, 4, a.divide(b));
+ assertFraction(4, 3, b.divide(a));
+ assertFraction(1, 1, b.divide(b));
+
+ BigFraction f1 = new BigFraction(3, 5);
+ BigFraction f2 = BigFraction.ZERO;
+ try {
+ f1.divide(f2);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ }
+
+ f1 = new BigFraction(0, 5);
+ f2 = new BigFraction(2, 7);
+ BigFraction f = f1.divide(f2);
+ Assert.assertSame(BigFraction.ZERO, f);
+
+ f1 = new BigFraction(2, 7);
+ f2 = BigFraction.ONE;
+ f = f1.divide(f2);
+ Assert.assertEquals(2, f.getNumeratorAsInt());
+ Assert.assertEquals(7, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(1, Integer.MAX_VALUE);
+ f = f1.divide(f1);
+ Assert.assertEquals(1, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ f2 = new BigFraction(1, Integer.MAX_VALUE);
+ f = f1.divide(f2);
+ Assert.assertEquals(Integer.MIN_VALUE, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ try {
+ f.divide((BigFraction) null);
+ Assert.fail("expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ }
+
+ f1 = new BigFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ f = f1.divide(BigInteger.valueOf(Integer.MIN_VALUE));
+ Assert.assertEquals(Integer.MAX_VALUE, f.getDenominatorAsInt());
+ Assert.assertEquals(1, f.getNumeratorAsInt());
+
+ f1 = new BigFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ f = f1.divide(Integer.MIN_VALUE);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getDenominatorAsInt());
+ Assert.assertEquals(1, f.getNumeratorAsInt());
+
+ f1 = new BigFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ f = f1.divide((long) Integer.MIN_VALUE);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getDenominatorAsInt());
+ Assert.assertEquals(1, f.getNumeratorAsInt());
+
+ }
+
+ @Test
+ public void testMultiply() {
+ BigFraction a = new BigFraction(1, 2);
+ BigFraction b = new BigFraction(2, 3);
+
+ assertFraction(1, 4, a.multiply(a));
+ assertFraction(1, 3, a.multiply(b));
+ assertFraction(1, 3, b.multiply(a));
+ assertFraction(4, 9, b.multiply(b));
+
+ BigFraction f1 = new BigFraction(Integer.MAX_VALUE, 1);
+ BigFraction f2 = new BigFraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ BigFraction f = f1.multiply(f2);
+ Assert.assertEquals(Integer.MIN_VALUE, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ f = f2.multiply(Integer.MAX_VALUE);
+ Assert.assertEquals(Integer.MIN_VALUE, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ f = f2.multiply((long) Integer.MAX_VALUE);
+ Assert.assertEquals(Integer.MIN_VALUE, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ try {
+ f.multiply((BigFraction) null);
+ Assert.fail("expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ }
+
+ }
+
+ @Test
+ public void testSubtract() {
+ BigFraction a = new BigFraction(1, 2);
+ BigFraction b = new BigFraction(2, 3);
+
+ assertFraction(0, 1, a.subtract(a));
+ assertFraction(-1, 6, a.subtract(b));
+ assertFraction(1, 6, b.subtract(a));
+ assertFraction(0, 1, b.subtract(b));
+
+ BigFraction f = new BigFraction(1, 1);
+ try {
+ f.subtract((BigFraction) null);
+ Assert.fail("expecting NullPointerException");
+ } catch (NullPointerException ex) {
+ }
+
+ // if this fraction is subtracted naively, it will overflow.
+ // check that it doesn't.
+ BigFraction f1 = new BigFraction(1, 32768 * 3);
+ BigFraction f2 = new BigFraction(1, 59049);
+ f = f1.subtract(f2);
+ Assert.assertEquals(-13085, f.getNumeratorAsInt());
+ Assert.assertEquals(1934917632, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MIN_VALUE, 3);
+ f2 = new BigFraction(1, 3).negate();
+ f = f1.subtract(f2);
+ Assert.assertEquals(Integer.MIN_VALUE + 1, f.getNumeratorAsInt());
+ Assert.assertEquals(3, f.getDenominatorAsInt());
+
+ f1 = new BigFraction(Integer.MAX_VALUE, 1);
+ f2 = BigFraction.ONE;
+ f = f1.subtract(f2);
+ Assert.assertEquals(Integer.MAX_VALUE - 1, f.getNumeratorAsInt());
+ Assert.assertEquals(1, f.getDenominatorAsInt());
+
+ }
+
+ @Test
+ public void testBigDecimalValue() {
+ Assert.assertEquals(new BigDecimal(0.5), new BigFraction(1, 2).bigDecimalValue());
+ Assert.assertEquals(new BigDecimal("0.0003"), new BigFraction(3, 10000).bigDecimalValue());
+ Assert.assertEquals(new BigDecimal("0"), new BigFraction(1, 3).bigDecimalValue(BigDecimal.ROUND_DOWN));
+ Assert.assertEquals(new BigDecimal("0.333"), new BigFraction(1, 3).bigDecimalValue(3, BigDecimal.ROUND_DOWN));
+ }
+
+ @Test
+ public void testEqualsAndHashCode() {
+ BigFraction zero = new BigFraction(0, 1);
+ BigFraction nullFraction = null;
+ Assert.assertTrue(zero.equals(zero));
+ Assert.assertFalse(zero.equals(nullFraction));
+ Assert.assertFalse(zero.equals(Double.valueOf(0)));
+ BigFraction zero2 = new BigFraction(0, 2);
+ Assert.assertTrue(zero.equals(zero2));
+ Assert.assertEquals(zero.hashCode(), zero2.hashCode());
+ BigFraction one = new BigFraction(1, 1);
+ Assert.assertFalse((one.equals(zero) || zero.equals(one)));
+ Assert.assertTrue(one.equals(BigFraction.ONE));
+ }
+
+ @Test
+ public void testGetReducedFraction() {
+ BigFraction threeFourths = new BigFraction(3, 4);
+ Assert.assertTrue(threeFourths.equals(BigFraction.getReducedFraction(6, 8)));
+ Assert.assertTrue(BigFraction.ZERO.equals(BigFraction.getReducedFraction(0, -1)));
+ try {
+ BigFraction.getReducedFraction(1, 0);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {
+ // expected
+ }
+ Assert.assertEquals(BigFraction.getReducedFraction(2, Integer.MIN_VALUE).getNumeratorAsInt(), -1);
+ Assert.assertEquals(BigFraction.getReducedFraction(1, -1).getNumeratorAsInt(), -1);
+ }
+
+ @Test
+ public void testPercentage() {
+ Assert.assertEquals(50.0, new BigFraction(1, 2).percentageValue(), 1.0e-15);
+ }
+
+ @Test
+ public void testPow() {
+ Assert.assertEquals(new BigFraction(8192, 1594323), new BigFraction(2, 3).pow(13));
+ Assert.assertEquals(new BigFraction(8192, 1594323), new BigFraction(2, 3).pow(13l));
+ Assert.assertEquals(new BigFraction(8192, 1594323), new BigFraction(2, 3).pow(BigInteger.valueOf(13l)));
+ Assert.assertEquals(BigFraction.ONE, new BigFraction(2, 3).pow(0));
+ Assert.assertEquals(BigFraction.ONE, new BigFraction(2, 3).pow(0l));
+ Assert.assertEquals(BigFraction.ONE, new BigFraction(2, 3).pow(BigInteger.valueOf(0l)));
+ Assert.assertEquals(new BigFraction(1594323, 8192), new BigFraction(2, 3).pow(-13));
+ Assert.assertEquals(new BigFraction(1594323, 8192), new BigFraction(2, 3).pow(-13l));
+ Assert.assertEquals(new BigFraction(1594323, 8192), new BigFraction(2, 3).pow(BigInteger.valueOf(-13l)));
+ }
+
+ @Test
+ public void testMath340() {
+ BigFraction fractionA = new BigFraction(0.00131);
+ BigFraction fractionB = new BigFraction(.37).reciprocal();
+ BigFraction errorResult = fractionA.multiply(fractionB);
+ BigFraction correctResult = new BigFraction(fractionA.getNumerator().multiply(fractionB.getNumerator()),
+ fractionA.getDenominator().multiply(fractionB.getDenominator()));
+ Assert.assertEquals(correctResult, errorResult);
+ }
+
+ @Test
+ public void testSerial() {
+ BigFraction[] fractions = {
+ new BigFraction(3, 4), BigFraction.ONE, BigFraction.ZERO,
+ new BigFraction(17), new BigFraction(Math.PI, 1000),
+ new BigFraction(-5, 2)
+ };
+ for (BigFraction fraction : fractions) {
+ Assert.assertEquals(fraction, TestUtils.serializeAndRecover(fraction));
+ }
+ }
+}
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionFormatTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionFormatTest.java
new file mode 100644
index 0000000..1cb3d6d
--- /dev/null
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionFormatTest.java
@@ -0,0 +1,350 @@
+/*
+ * 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.numbers.fraction;
+
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class FractionFormatTest {
+
+ FractionFormat properFormat = null;
+ FractionFormat improperFormat = null;
+
+ protected Locale getLocale() {
+ return Locale.getDefault();
+ }
+
+ @Before
+ public void setUp() {
+ properFormat = FractionFormat.getProperInstance(getLocale());
+ improperFormat = FractionFormat.getImproperInstance(getLocale());
+ }
+
+ @Test
+ public void testFormat() {
+ Fraction c = new Fraction(1, 2);
+ String expected = "1 / 2";
+
+ String actual = properFormat.format(c);
+ Assert.assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testFormatNegative() {
+ Fraction c = new Fraction(-1, 2);
+ String expected = "-1 / 2";
+
+ String actual = properFormat.format(c);
+ Assert.assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testFormatZero() {
+ Fraction c = new Fraction(0, 1);
+ String expected = "0 / 1";
+
+ String actual = properFormat.format(c);
+ Assert.assertEquals(expected, actual);
+
+ actual = improperFormat.format(c);
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testFormatImproper() {
+ Fraction c = new Fraction(5, 3);
+
+ String actual = properFormat.format(c);
+ Assert.assertEquals("1 2 / 3", actual);
+
+ actual = improperFormat.format(c);
+ Assert.assertEquals("5 / 3", actual);
+ }
+
+ @Test
+ public void testFormatImproperNegative() {
+ Fraction c = new Fraction(-5, 3);
+
+ String actual = properFormat.format(c);
+ Assert.assertEquals("-1 2 / 3", actual);
+
+ actual = improperFormat.format(c);
+ Assert.assertEquals("-5 / 3", actual);
+ }
+
+ @Test
+ public void testParse() {
+ String source = "1 / 2";
+
+ try {
+ Fraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(1, c.getNumerator());
+ Assert.assertEquals(2, c.getDenominator());
+
+ c = improperFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(1, c.getNumerator());
+ Assert.assertEquals(2, c.getDenominator());
+ } catch (ParseException ex) {
+ Assert.fail(ex.getMessage());
+ }
+ }
+
+ @Test
+ public void testParseInteger() throws Exception {
+ String source = "10";
+ {
+ Fraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(10, c.getNumerator());
+ Assert.assertEquals(1, c.getDenominator());
+ }
+ {
+ Fraction c = improperFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(10, c.getNumerator());
+ Assert.assertEquals(1, c.getDenominator());
+ }
+ }
+
+ @Test
+ public void testParseOne1() throws Exception {
+ String source = "1 / 1";
+ Fraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(1, c.getNumerator());
+ Assert.assertEquals(1, c.getDenominator());
+ }
+
+ @Test
+ public void testParseOne2() throws Exception {
+ String source = "10 / 10";
+ Fraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(1, c.getNumerator());
+ Assert.assertEquals(1, c.getDenominator());
+ }
+
+ @Test
+ public void testParseZero1() throws Exception {
+ String source = "0 / 1";
+ Fraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(0, c.getNumerator());
+ Assert.assertEquals(1, c.getDenominator());
+ }
+
+ @Test
+ public void testParseZero2() throws Exception {
+ String source = "-0 / 1";
+ Fraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(0, c.getNumerator());
+ Assert.assertEquals(1, c.getDenominator());
+ // This test shows that the sign is not preserved.
+ Assert.assertEquals(Double.POSITIVE_INFINITY, 1d / c.doubleValue(), 0);
+ }
+
+ @Test
+ public void testParseInvalid() {
+ String source = "a";
+ String msg = "should not be able to parse '10 / a'.";
+ try {
+ properFormat.parse(source);
+ Assert.fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ try {
+ improperFormat.parse(source);
+ Assert.fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ @Test
+ public void testParseInvalidDenominator() {
+ String source = "10 / a";
+ String msg = "should not be able to parse '10 / a'.";
+ try {
+ properFormat.parse(source);
+ Assert.fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ try {
+ improperFormat.parse(source);
+ Assert.fail(msg);
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ @Test
+ public void testParseNegative() throws Exception {
+
+ {
+ String source = "-1 / 2";
+ Fraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(-1, c.getNumerator());
+ Assert.assertEquals(2, c.getDenominator());
+
+ c = improperFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(-1, c.getNumerator());
+ Assert.assertEquals(2, c.getDenominator());
+
+ source = "1 / -2";
+ c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(-1, c.getNumerator());
+ Assert.assertEquals(2, c.getDenominator());
+
+ c = improperFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(-1, c.getNumerator());
+ Assert.assertEquals(2, c.getDenominator());
+ }
+ }
+
+ @Test
+ public void testParseProper() throws Exception {
+ String source = "1 2 / 3";
+
+ {
+ Fraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(5, c.getNumerator());
+ Assert.assertEquals(3, c.getDenominator());
+ }
+
+ try {
+ improperFormat.parse(source);
+ Assert.fail("invalid improper fraction.");
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ @Test
+ public void testParseProperNegative() throws Exception {
+ String source = "-1 2 / 3";
+ {
+ Fraction c = properFormat.parse(source);
+ Assert.assertNotNull(c);
+ Assert.assertEquals(-5, c.getNumerator());
+ Assert.assertEquals(3, c.getDenominator());
+ }
+
+ try {
+ improperFormat.parse(source);
+ Assert.fail("invalid improper fraction.");
+ } catch (ParseException ex) {
+ // success
+ }
+ }
+
+ @Test
+ public void testParseProperInvalidMinus() {
+ String source = "2 -2 / 3";
+ try {
+ properFormat.parse(source);
+ Assert.fail("invalid minus in improper fraction.");
+ } catch (ParseException ex) {
+ // expected
+ }
+ source = "2 2 / -3";
+ try {
+ properFormat.parse(source);
+ Assert.fail("invalid minus in improper fraction.");
+ } catch (ParseException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testNumeratorFormat() {
+ NumberFormat old = properFormat.getNumeratorFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ properFormat.setNumeratorFormat(nf);
+ Assert.assertEquals(nf, properFormat.getNumeratorFormat());
+ properFormat.setNumeratorFormat(old);
+
+ old = improperFormat.getNumeratorFormat();
+ nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ improperFormat.setNumeratorFormat(nf);
+ Assert.assertEquals(nf, improperFormat.getNumeratorFormat());
+ improperFormat.setNumeratorFormat(old);
+ }
+
+ @Test
+ public void testDenominatorFormat() {
+ NumberFormat old = properFormat.getDenominatorFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ properFormat.setDenominatorFormat(nf);
+ Assert.assertEquals(nf, properFormat.getDenominatorFormat());
+ properFormat.setDenominatorFormat(old);
+
+ old = improperFormat.getDenominatorFormat();
+ nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ improperFormat.setDenominatorFormat(nf);
+ Assert.assertEquals(nf, improperFormat.getDenominatorFormat());
+ improperFormat.setDenominatorFormat(old);
+ }
+
+ @Test
+ public void testWholeFormat() {
+ ProperFractionFormat format = (ProperFractionFormat)properFormat;
+
+ NumberFormat old = format.getWholeFormat();
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setParseIntegerOnly(true);
+ format.setWholeFormat(nf);
+ Assert.assertEquals(nf, format.getWholeFormat());
+ format.setWholeFormat(old);
+ }
+
+ @Test
+ public void testLongFormat() {
+ Assert.assertEquals("10 / 1", improperFormat.format(10l));
+ }
+
+ @Test
+ public void testDoubleFormat() {
+ Assert.assertEquals("355 / 113", improperFormat.format(Math.PI));
+ }
+}
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionTest.java
new file mode 100644
index 0000000..d35092b
--- /dev/null
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionTest.java
@@ -0,0 +1,624 @@
+/*
+ * 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.numbers.fraction;
+
+import org.apache.commons.numbers.core.TestUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+/**
+ */
+public class FractionTest {
+
+ private void assertFraction(int expectedNumerator, int expectedDenominator, Fraction actual) {
+ Assert.assertEquals(expectedNumerator, actual.getNumerator());
+ Assert.assertEquals(expectedDenominator, actual.getDenominator());
+ }
+
+ @Test
+ public void testConstructor() {
+ assertFraction(0, 1, new Fraction(0, 1));
+ assertFraction(0, 1, new Fraction(0, 2));
+ assertFraction(0, 1, new Fraction(0, -1));
+ assertFraction(1, 2, new Fraction(1, 2));
+ assertFraction(1, 2, new Fraction(2, 4));
+ assertFraction(-1, 2, new Fraction(-1, 2));
+ assertFraction(-1, 2, new Fraction(1, -2));
+ assertFraction(-1, 2, new Fraction(-2, 4));
+ assertFraction(-1, 2, new Fraction(2, -4));
+
+ // overflow
+ try {
+ new Fraction(Integer.MIN_VALUE, -1);
+ Assert.fail();
+ } catch (ArithmeticException ex) {
+ // success
+ }
+ try {
+ new Fraction(1, Integer.MIN_VALUE);
+ Assert.fail();
+ } catch (ArithmeticException ex) {
+ // success
+ }
+
+ assertFraction(0, 1, new Fraction(0.00000000000001));
+ assertFraction(2, 5, new Fraction(0.40000000000001));
+ assertFraction(15, 1, new Fraction(15.0000000000001));
+ }
+
+ @Test(expected=ArithmeticException.class)
+ public void testGoldenRatio() {
+ // the golden ratio is notoriously a difficult number for continuous fraction
+ new Fraction((1 + Math.sqrt(5)) / 2, 1.0e-12, 25);
+ }
+
+ // MATH-179
+ @Test
+ public void testDoubleConstructor() throws Exception {
+ assertFraction(1, 2, new Fraction((double)1 / (double)2));
+ assertFraction(1, 3, new Fraction((double)1 / (double)3));
+ assertFraction(2, 3, new Fraction((double)2 / (double)3));
+ assertFraction(1, 4, new Fraction((double)1 / (double)4));
+ assertFraction(3, 4, new Fraction((double)3 / (double)4));
+ assertFraction(1, 5, new Fraction((double)1 / (double)5));
+ assertFraction(2, 5, new Fraction((double)2 / (double)5));
+ assertFraction(3, 5, new Fraction((double)3 / (double)5));
+ assertFraction(4, 5, new Fraction((double)4 / (double)5));
+ assertFraction(1, 6, new Fraction((double)1 / (double)6));
+ assertFraction(5, 6, new Fraction((double)5 / (double)6));
+ assertFraction(1, 7, new Fraction((double)1 / (double)7));
+ assertFraction(2, 7, new Fraction((double)2 / (double)7));
+ assertFraction(3, 7, new Fraction((double)3 / (double)7));
+ assertFraction(4, 7, new Fraction((double)4 / (double)7));
+ assertFraction(5, 7, new Fraction((double)5 / (double)7));
+ assertFraction(6, 7, new Fraction((double)6 / (double)7));
+ assertFraction(1, 8, new Fraction((double)1 / (double)8));
+ assertFraction(3, 8, new Fraction((double)3 / (double)8));
+ assertFraction(5, 8, new Fraction((double)5 / (double)8));
+ assertFraction(7, 8, new Fraction((double)7 / (double)8));
+ assertFraction(1, 9, new Fraction((double)1 / (double)9));
+ assertFraction(2, 9, new Fraction((double)2 / (double)9));
+ assertFraction(4, 9, new Fraction((double)4 / (double)9));
+ assertFraction(5, 9, new Fraction((double)5 / (double)9));
+ assertFraction(7, 9, new Fraction((double)7 / (double)9));
+ assertFraction(8, 9, new Fraction((double)8 / (double)9));
+ assertFraction(1, 10, new Fraction((double)1 / (double)10));
+ assertFraction(3, 10, new Fraction((double)3 / (double)10));
+ assertFraction(7, 10, new Fraction((double)7 / (double)10));
+ assertFraction(9, 10, new Fraction((double)9 / (double)10));
+ assertFraction(1, 11, new Fraction((double)1 / (double)11));
+ assertFraction(2, 11, new Fraction((double)2 / (double)11));
+ assertFraction(3, 11, new Fraction((double)3 / (double)11));
+ assertFraction(4, 11, new Fraction((double)4 / (double)11));
+ assertFraction(5, 11, new Fraction((double)5 / (double)11));
+ assertFraction(6, 11, new Fraction((double)6 / (double)11));
+ assertFraction(7, 11, new Fraction((double)7 / (double)11));
+ assertFraction(8, 11, new Fraction((double)8 / (double)11));
+ assertFraction(9, 11, new Fraction((double)9 / (double)11));
+ assertFraction(10, 11, new Fraction((double)10 / (double)11));
+ }
+
+ // MATH-181
+ @Test
+ public void testDigitLimitConstructor() throws Exception {
+ assertFraction(2, 5, new Fraction(0.4, 9));
+ assertFraction(2, 5, new Fraction(0.4, 99));
+ assertFraction(2, 5, new Fraction(0.4, 999));
+
+ assertFraction(3, 5, new Fraction(0.6152, 9));
+ assertFraction(8, 13, new Fraction(0.6152, 99));
+ assertFraction(510, 829, new Fraction(0.6152, 999));
+ assertFraction(769, 1250, new Fraction(0.6152, 9999));
+
+ // MATH-996
+ assertFraction(1, 2, new Fraction(0.5000000001, 10));
+ }
+
+ @Test
+ public void testIntegerOverflow() {
+ checkIntegerOverflow(0.75000000001455192);
+ checkIntegerOverflow(1.0e10);
+ checkIntegerOverflow(-1.0e10);
+ checkIntegerOverflow(-43979.60679604749);
+ }
+
+ private void checkIntegerOverflow(double a) {
+ try {
+ @SuppressWarnings("unused")
+ Fraction f = new Fraction(a, 1.0e-12, 1000);
+ //System.out.println(f.getNumerator() + "/" + f.getDenominator());
+ Assert.fail("an exception should have been thrown");
+ } catch (ArithmeticException ignored) {
+ // expected behavior
+ }
+ }
+
+ @Test
+ public void testEpsilonLimitConstructor() throws Exception {
+ assertFraction(2, 5, new Fraction(0.4, 1.0e-5, 100));
+
+ assertFraction(3, 5, new Fraction(0.6152, 0.02, 100));
+ assertFraction(8, 13, new Fraction(0.6152, 1.0e-3, 100));
+ assertFraction(251, 408, new Fraction(0.6152, 1.0e-4, 100));
+ assertFraction(251, 408, new Fraction(0.6152, 1.0e-5, 100));
+ assertFraction(510, 829, new Fraction(0.6152, 1.0e-6, 100));
+ assertFraction(769, 1250, new Fraction(0.6152, 1.0e-7, 100));
+ }
+
+ @Test
+ public void testCompareTo() {
+ Fraction first = new Fraction(1, 2);
+ Fraction second = new Fraction(1, 3);
+ Fraction third = new Fraction(1, 2);
+
+ Assert.assertEquals(0, first.compareTo(first));
+ Assert.assertEquals(0, first.compareTo(third));
+ Assert.assertEquals(1, first.compareTo(second));
+ Assert.assertEquals(-1, second.compareTo(first));
+
+ // these two values are different approximations of PI
+ // the first one is approximately PI - 3.07e-18
+ // the second one is approximately PI + 1.936e-17
+ Fraction pi1 = new Fraction(1068966896, 340262731);
+ Fraction pi2 = new Fraction( 411557987, 131002976);
+ Assert.assertEquals(-1, pi1.compareTo(pi2));
+ Assert.assertEquals( 1, pi2.compareTo(pi1));
+ Assert.assertEquals(0.0, pi1.doubleValue() - pi2.doubleValue(), 1.0e-20);
+ }
+
+ @Test
+ public void testDoubleValue() {
+ Fraction first = new Fraction(1, 2);
+ Fraction second = new Fraction(1, 3);
+
+ Assert.assertEquals(0.5, first.doubleValue(), 0.0);
+ Assert.assertEquals(1.0 / 3.0, second.doubleValue(), 0.0);
+ }
+
+ @Test
+ public void testFloatValue() {
+ Fraction first = new Fraction(1, 2);
+ Fraction second = new Fraction(1, 3);
+
+ Assert.assertEquals(0.5f, first.floatValue(), 0.0f);
+ Assert.assertEquals((float)(1.0 / 3.0), second.floatValue(), 0.0f);
+ }
+
+ @Test
+ public void testIntValue() {
+ Fraction first = new Fraction(1, 2);
+ Fraction second = new Fraction(3, 2);
+
+ Assert.assertEquals(0, first.intValue());
+ Assert.assertEquals(1, second.intValue());
+ }
+
+ @Test
+ public void testLongValue() {
+ Fraction first = new Fraction(1, 2);
+ Fraction second = new Fraction(3, 2);
+
+ Assert.assertEquals(0L, first.longValue());
+ Assert.assertEquals(1L, second.longValue());
+ }
+
+ @Test
+ public void testConstructorDouble() {
+ assertFraction(1, 2, new Fraction(0.5));
+ assertFraction(1, 3, new Fraction(1.0 / 3.0));
+ assertFraction(17, 100, new Fraction(17.0 / 100.0));
+ assertFraction(317, 100, new Fraction(317.0 / 100.0));
+ assertFraction(-1, 2, new Fraction(-0.5));
+ assertFraction(-1, 3, new Fraction(-1.0 / 3.0));
+ assertFraction(-17, 100, new Fraction(17.0 / -100.0));
+ assertFraction(-317, 100, new Fraction(-317.0 / 100.0));
+ }
+
+ @Test
+ public void testAbs() {
+ Fraction a = new Fraction(10, 21);
+ Fraction b = new Fraction(-10, 21);
+ Fraction c = new Fraction(10, -21);
+
+ assertFraction(10, 21, a.abs());
+ assertFraction(10, 21, b.abs());
+ assertFraction(10, 21, c.abs());
+ }
+
+ @Test
+ public void testPercentage() {
+ Assert.assertEquals(50.0, new Fraction(1, 2).percentageValue(), 1.0e-15);
+ }
+
+ @Test
+ public void testMath835() {
+ final int numer = Integer.MAX_VALUE / 99;
+ final int denom = 1;
+ final double percentage = 100 * ((double) numer) / denom;
+ final Fraction frac = new Fraction(numer, denom);
+ // With the implementation that preceded the fix suggested in MATH-835,
+ // this test was failing, due to overflow.
+ Assert.assertEquals(percentage, frac.percentageValue(), Math.ulp(percentage));
+ }
+
+ @Test
+ public void testMath1261() {
+ final Fraction a = new Fraction(Integer.MAX_VALUE, 2);
+ final Fraction b = a.multiply(2);
+ Assert.assertTrue(b.equals(new Fraction(Integer.MAX_VALUE)));
+
+ final Fraction c = new Fraction(2, Integer.MAX_VALUE);
+ final Fraction d = c.divide(2);
+ Assert.assertTrue(d.equals(new Fraction(1, Integer.MAX_VALUE)));
+ }
+
+ @Test
+ public void testReciprocal() {
+ Fraction f = null;
+
+ f = new Fraction(50, 75);
+ f = f.reciprocal();
+ Assert.assertEquals(3, f.getNumerator());
+ Assert.assertEquals(2, f.getDenominator());
+
+ f = new Fraction(4, 3);
+ f = f.reciprocal();
+ Assert.assertEquals(3, f.getNumerator());
+ Assert.assertEquals(4, f.getDenominator());
+
+ f = new Fraction(-15, 47);
+ f = f.reciprocal();
+ Assert.assertEquals(-47, f.getNumerator());
+ Assert.assertEquals(15, f.getDenominator());
+
+ f = new Fraction(0, 3);
+ try {
+ f = f.reciprocal();
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ignored) {}
+
+ // large values
+ f = new Fraction(Integer.MAX_VALUE, 1);
+ f = f.reciprocal();
+ Assert.assertEquals(1, f.getNumerator());
+ Assert.assertEquals(Integer.MAX_VALUE, f.getDenominator());
+ }
+
+ @Test
+ public void testNegate() {
+ Fraction f = null;
+
+ f = new Fraction(50, 75);
+ f = f.negate();
+ Assert.assertEquals(-2, f.getNumerator());
+ Assert.assertEquals(3, f.getDenominator());
+
+ f = new Fraction(-50, 75);
+ f = f.negate();
+ Assert.assertEquals(2, f.getNumerator());
+ Assert.assertEquals(3, f.getDenominator());
+
+ // large values
+ f = new Fraction(Integer.MAX_VALUE-1, Integer.MAX_VALUE);
+ f = f.negate();
+ Assert.assertEquals(Integer.MIN_VALUE+2, f.getNumerator());
+ Assert.assertEquals(Integer.MAX_VALUE, f.getDenominator());
+
+ f = new Fraction(Integer.MIN_VALUE, 1);
+ try {
+ f = f.negate();
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+ }
+
+ @Test
+ public void testAdd() {
+ Fraction a = new Fraction(1, 2);
+ Fraction b = new Fraction(2, 3);
+
+ assertFraction(1, 1, a.add(a));
+ assertFraction(7, 6, a.add(b));
+ assertFraction(7, 6, b.add(a));
+ assertFraction(4, 3, b.add(b));
+
+ Fraction f1 = new Fraction(Integer.MAX_VALUE - 1, 1);
+ Fraction f2 = Fraction.ONE;
+ Fraction f = f1.add(f2);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getNumerator());
+ Assert.assertEquals(1, f.getDenominator());
+ f = f1.add(1);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getNumerator());
+ Assert.assertEquals(1, f.getDenominator());
+
+ f1 = new Fraction(-1, 13*13*2*2);
+ f2 = new Fraction(-2, 13*17*2);
+ f = f1.add(f2);
+ Assert.assertEquals(13*13*17*2*2, f.getDenominator());
+ Assert.assertEquals(-17 - 2*13*2, f.getNumerator());
+
+ try {
+ f.add(null);
+ Assert.fail("expecting NullArgumentException");
+ } catch (NullPointerException ex) {}
+
+ // if this fraction is added naively, it will overflow.
+ // check that it doesn't.
+ f1 = new Fraction(1,32768*3);
+ f2 = new Fraction(1,59049);
+ f = f1.add(f2);
+ Assert.assertEquals(52451, f.getNumerator());
+ Assert.assertEquals(1934917632, f.getDenominator());
+
+ f1 = new Fraction(Integer.MIN_VALUE, 3);
+ f2 = new Fraction(1,3);
+ f = f1.add(f2);
+ Assert.assertEquals(Integer.MIN_VALUE+1, f.getNumerator());
+ Assert.assertEquals(3, f.getDenominator());
+
+ f1 = new Fraction(Integer.MAX_VALUE - 1, 1);
+ f2 = Fraction.ONE;
+ f = f1.add(f2);
+ Assert.assertEquals(Integer.MAX_VALUE, f.getNumerator());
+ Assert.assertEquals(1, f.getDenominator());
+
+ try {
+ f = f.add(Fraction.ONE); // should overflow
+ Assert.fail("expecting ArithmeticException but got: " + f.toString());
+ } catch (ArithmeticException ex) {}
+
+ // denominator should not be a multiple of 2 or 3 to trigger overflow
+ f1 = new Fraction(Integer.MIN_VALUE, 5);
+ f2 = new Fraction(-1,5);
+ try {
+ f = f1.add(f2); // should overflow
+ Assert.fail("expecting ArithmeticException but got: " + f.toString());
+ } catch (ArithmeticException ex) {}
+
+ try {
+ f= new Fraction(-Integer.MAX_VALUE, 1);
+ f = f.add(f);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ try {
+ f= new Fraction(-Integer.MAX_VALUE, 1);
+ f = f.add(f);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ f1 = new Fraction(3,327680);
+ f2 = new Fraction(2,59049);
+ try {
+ f = f1.add(f2); // should overflow
+ Assert.fail("expecting ArithmeticException but got: " + f.toString());
+ } catch (ArithmeticException ex) {}
+ }
+
+ @Test
+ public void testDivide() {
+ Fraction a = new Fraction(1, 2);
+ Fraction b = new Fraction(2, 3);
+
+ assertFraction(1, 1, a.divide(a));
+ assertFraction(3, 4, a.divide(b));
+ assertFraction(4, 3, b.divide(a));
+ assertFraction(1, 1, b.divide(b));
+
+ Fraction f1 = new Fraction(3, 5);
+ Fraction f2 = Fraction.ZERO;
+ try {
+ f1.divide(f2);
+ Assert.fail("expecting FractionException");
+ } catch (FractionException ex) {}
+
+ f1 = new Fraction(0, 5);
+ f2 = new Fraction(2, 7);
+ Fraction f = f1.divide(f2);
+ Assert.assertSame(Fraction.ZERO, f);
+
+ f1 = new Fraction(2, 7);
+ f2 = Fraction.ONE;
+ f = f1.divide(f2);
+ Assert.assertEquals(2, f.getNumerator());
+ Assert.assertEquals(7, f.getDenominator());
+
+ f1 = new Fraction(1, Integer.MAX_VALUE);
+ f = f1.divide(f1);
+ Assert.assertEquals(1, f.getNumerator());
+ Assert.assertEquals(1, f.getDenominator());
+
+ f1 = new Fraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ f2 = new Fraction(1, Integer.MAX_VALUE);
+ f = f1.divide(f2);
+ Assert.assertEquals(Integer.MIN_VALUE, f.getNumerator());
+ Assert.assertEquals(1, f.getDenominator());
+
+ try {
+ f.divide(null);
+ Assert.fail("NullArgumentException");
+ } catch (NullPointerException ex) {}
+
+ try {
+ f1 = new Fraction(1, Integer.MAX_VALUE);
+ f = f1.divide(f1.reciprocal()); // should overflow
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+ try {
+ f1 = new Fraction(1, -Integer.MAX_VALUE);
+ f = f1.divide(f1.reciprocal()); // should overflow
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ f1 = new Fraction(6, 35);
+ f = f1.divide(15);
+ Assert.assertEquals(2, f.getNumerator());
+ Assert.assertEquals(175, f.getDenominator());
+
+ }
+
+ @Test
+ public void testMultiply() {
+ Fraction a = new Fraction(1, 2);
+ Fraction b = new Fraction(2, 3);
+
+ assertFraction(1, 4, a.multiply(a));
+ assertFraction(1, 3, a.multiply(b));
+ assertFraction(1, 3, b.multiply(a));
+ assertFraction(4, 9, b.multiply(b));
+
+ Fraction f1 = new Fraction(Integer.MAX_VALUE, 1);
+ Fraction f2 = new Fraction(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ Fraction f = f1.multiply(f2);
+ Assert.assertEquals(Integer.MIN_VALUE, f.getNumerator());
+ Assert.assertEquals(1, f.getDenominator());
+
+ try {
+ f.multiply(null);
+ Assert.fail("expecting NullArgumentException");
+ } catch (NullPointerException ex) {}
+
+ f1 = new Fraction(6, 35);
+ f = f1.multiply(15);
+ Assert.assertEquals(18, f.getNumerator());
+ Assert.assertEquals(7, f.getDenominator());
+ }
+
+ @Test
+ public void testSubtract() {
+ Fraction a = new Fraction(1, 2);
+ Fraction b = new Fraction(2, 3);
+
+ assertFraction(0, 1, a.subtract(a));
+ assertFraction(-1, 6, a.subtract(b));
+ assertFraction(1, 6, b.subtract(a));
+ assertFraction(0, 1, b.subtract(b));
+
+ Fraction f = new Fraction(1,1);
+ try {
+ f.subtract(null);
+ Assert.fail("expecting NullArgumentException");
+ } catch (NullPointerException ex) {}
+
+ // if this fraction is subtracted naively, it will overflow.
+ // check that it doesn't.
+ Fraction f1 = new Fraction(1,32768*3);
+ Fraction f2 = new Fraction(1,59049);
+ f = f1.subtract(f2);
+ Assert.assertEquals(-13085, f.getNumerator());
+ Assert.assertEquals(1934917632, f.getDenominator());
+
+ f1 = new Fraction(Integer.MIN_VALUE, 3);
+ f2 = new Fraction(1,3).negate();
+ f = f1.subtract(f2);
+ Assert.assertEquals(Integer.MIN_VALUE+1, f.getNumerator());
+ Assert.assertEquals(3, f.getDenominator());
+
+ f1 = new Fraction(Integer.MAX_VALUE, 1);
+ f2 = Fraction.ONE;
+ f = f1.subtract(f2);
+ Assert.assertEquals(Integer.MAX_VALUE-1, f.getNumerator());
+ Assert.assertEquals(1, f.getDenominator());
+ f = f1.subtract(1);
+ Assert.assertEquals(Integer.MAX_VALUE-1, f.getNumerator());
+ Assert.assertEquals(1, f.getDenominator());
+
+ try {
+ f1 = new Fraction(1, Integer.MAX_VALUE);
+ f2 = new Fraction(1, Integer.MAX_VALUE - 1);
+ f = f1.subtract(f2);
+ Assert.fail("expecting ArithmeticException"); //should overflow
+ } catch (ArithmeticException ex) {}
+
+ // denominator should not be a multiple of 2 or 3 to trigger overflow
+ f1 = new Fraction(Integer.MIN_VALUE, 5);
+ f2 = new Fraction(1,5);
+ try {
+ f = f1.subtract(f2); // should overflow
+ Assert.fail("expecting ArithmeticException but got: " + f.toString());
+ } catch (ArithmeticException ex) {}
+
+ try {
+ f= new Fraction(Integer.MIN_VALUE, 1);
+ f = f.subtract(Fraction.ONE);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ try {
+ f= new Fraction(Integer.MAX_VALUE, 1);
+ f = f.subtract(Fraction.ONE.negate());
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ex) {}
+
+ f1 = new Fraction(3,327680);
+ f2 = new Fraction(2,59049);
+ try {
+ f = f1.subtract(f2); // should overflow
+ Assert.fail("expecting ArithmeticException but got: " + f.toString());
+ } catch (ArithmeticException ex) {}
+ }
+
+ @Test
+ public void testEqualsAndHashCode() {
+ Fraction zero = new Fraction(0,1);
+ Fraction nullFraction = null;
+ Assert.assertTrue( zero.equals(zero));
+ Assert.assertFalse(zero.equals(nullFraction));
+ Assert.assertFalse(zero.equals(Double.valueOf(0)));
+ Fraction zero2 = new Fraction(0,2);
+ Assert.assertTrue(zero.equals(zero2));
+ Assert.assertEquals(zero.hashCode(), zero2.hashCode());
+ Fraction one = new Fraction(1,1);
+ Assert.assertFalse((one.equals(zero) ||zero.equals(one)));
+ }
+
+ @Test
+ public void testGetReducedFraction() {
+ Fraction threeFourths = new Fraction(3, 4);
+ Assert.assertTrue(threeFourths.equals(Fraction.getReducedFraction(6, 8)));
+ Assert.assertTrue(Fraction.ZERO.equals(Fraction.getReducedFraction(0, -1)));
+ try {
+ Fraction.getReducedFraction(1, 0);
+ Assert.fail("expecting ArithmeticException");
+ } catch (ArithmeticException ignored) {
+ // expected
+ }
+ Assert.assertEquals(Fraction.getReducedFraction
+ (2, Integer.MIN_VALUE).getNumerator(),-1);
+ Assert.assertEquals(Fraction.getReducedFraction
+ (1, -1).getNumerator(), -1);
+ }
+
+ @Test
+ public void testToString() {
+ Assert.assertEquals("0", new Fraction(0, 3).toString());
+ Assert.assertEquals("3", new Fraction(6, 2).toString());
+ Assert.assertEquals("2 / 3", new Fraction(18, 27).toString());
+ }
+
+ @Test
+ public void testSerial() {
+ Fraction[] fractions = {
+ new Fraction(3, 4), Fraction.ONE, Fraction.ZERO,
+ new Fraction(17), new Fraction(Math.PI, 1000),
+ new Fraction(-5, 2)
+ };
+ for (Fraction fraction : fractions) {
+ Assert.assertEquals(fraction, TestUtils.serializeAndRecover(fraction));
+ }
+ }
+}
diff --git a/commons-numbers-quaternion/pom.xml b/commons-numbers-quaternion/pom.xml
index 76a59e3..1817528 100644
--- a/commons-numbers-quaternion/pom.xml
+++ b/commons-numbers-quaternion/pom.xml
@@ -46,7 +46,6 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-numbers-core</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
diff --git a/pom.xml b/pom.xml
index 9d683ae..4485994 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,9 +68,27 @@
<contributors>
<contributor>
<name>Raymond DeCampo</name>
+ <email>ray at decampo dot org</email>
</contributor>
</contributors>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-numbers-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-numbers-core</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
<dependencies>
<dependency>
<groupId>junit</groupId>
@@ -543,7 +561,7 @@
<module>commons-numbers-core</module>
<module>commons-numbers-complex</module>
<module>commons-numbers-quaternion</module>
- <!-- <module>commons-numbers-fraction</module> -->
+ <module>commons-numbers-fraction</module>
<!-- <module>commons-numbers-continuedfraction</module> -->
</modules>