You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ra...@apache.org on 2017/02/06 12:52:23 UTC

[02/13] commons-numbers git commit: NUMBERS-6: Create structure for fraction module within commons-numbers.

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionConversionException.java
----------------------------------------------------------------------
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionConversionException.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionConversionException.java
new file mode 100644
index 0000000..07db867
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionConversionException.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+/**
+ * Error thrown when a double value cannot be converted to a fraction
+ * in the allowed number of iterations.
+ *
+ * @since 1.2
+ */
+public class FractionConversionException extends FractionException {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 201701181859L;
+
+    /**
+     * Constructs an exception with specified formatted detail message.
+     * Message formatting is delegated to {@link java.text.MessageFormat}.
+     * @param value double value to convert
+     * @param maxIterations maximal number of iterations allowed
+     */
+    public FractionConversionException(double value, int maxIterations) {
+        super("Unable to convert {0} to fraction after {1} iterations", value, maxIterations);
+    }
+
+    /**
+     * Constructs an exception with specified formatted detail message.
+     * Message formatting is delegated to {@link java.text.MessageFormat}.
+     * @param value double value to convert
+     * @param p current numerator
+     * @param q current denominator
+     */
+    public FractionConversionException(double value, long p, long q) {
+        super("Overflow trying to convert {0} to fraction ({1}/{2})", value, p, q);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionException.java
----------------------------------------------------------------------
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..6080981
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionException.java
@@ -0,0 +1,44 @@
+/*
+ * 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.NumbersArithmeticException;
+
+/**
+ * Base class for all exceptions thrown in the module.
+ */
+public class FractionException extends NumbersArithmeticException {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 201701191744L;
+
+    protected Object[] formatArguments;
+
+    public FractionException() {
+    }
+
+    public FractionException(String message, Object... formatArguments) {
+        super(message);
+        this.formatArguments = formatArguments;
+    }
+
+    public FractionException(String message, Throwable cause, Object... formatArguments) {
+        super(message, cause);
+        this.formatArguments = formatArguments;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionField.java
----------------------------------------------------------------------
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionField.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionField.java
new file mode 100644
index 0000000..ad0ab23
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionField.java
@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+/**
+ * Representation of the fractional numbers field.
+ * <p>
+ * This class is a singleton.
+ * </p>
+ * @see Fraction
+ * @since 2.0
+ */
+public class FractionField implements /*Field<Fraction>, */Serializable  {
+
+    /** Serializable version identifier */
+    private static final long serialVersionUID = -1257768487499119313L;
+
+    /** Private constructor for the singleton.
+     */
+    private FractionField() {
+    }
+
+    /** Get the unique instance.
+     * @return the unique instance
+     */
+    public static FractionField getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    /** {@inheritDoc} */
+    public Fraction getOne() {
+        return Fraction.ONE;
+    }
+
+    /** {@inheritDoc} */
+    public Fraction getZero() {
+        return Fraction.ZERO;
+    }
+
+    /** {@inheritDoc} */
+/*
+    @Override
+    public Class<? extends FieldElement<Fraction>> getRuntimeClass() {
+        return Fraction.class;
+    }
+*/
+    // CHECKSTYLE: stop HideUtilityClassConstructor
+    /** Holder for the instance.
+     * <p>We use here the Initialization On Demand Holder Idiom.</p>
+     */
+    private static class LazyHolder {
+        /** Cached field instance. */
+        private static final FractionField INSTANCE = new FractionField();
+    }
+    // CHECKSTYLE: resume HideUtilityClassConstructor
+
+    /** Handle deserialization of the singleton.
+     * @return the singleton instance
+     */
+    private Object readResolve() {
+        // return the singleton instance
+        return LazyHolder.INSTANCE;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionFormat.java
----------------------------------------------------------------------
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..3cd05af
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionFormat.java
@@ -0,0 +1,261 @@
+/*
+ * 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;
+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.
+ *
+ * @since 1.1
+ */
+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 FractionConversionException 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)
+        throws FractionConversionException, IllegalArgumentException {
+        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 FractionParseException if the beginning of the specified string
+     *            cannot be parsed.
+     */
+    @Override
+    public Fraction parse(final String source) throws FractionParseException {
+        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());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionOverflowException.java
----------------------------------------------------------------------
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionOverflowException.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionOverflowException.java
new file mode 100644
index 0000000..08d6f81
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionOverflowException.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+/**
+ * Error thrown when evaluating a fraction causes an overflow.
+ */
+public class FractionOverflowException extends FractionException {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 201701181869L;
+
+    /**
+     * Constructs an exception the default formatted detail message.
+     * Message formatting is delegated to {@link java.text.MessageFormat}.
+     * @param p current numerator
+     * @param q current denominator
+     */
+    public FractionOverflowException(long p, long q) {
+        super("overflow in fraction {0}/{1}, cannot negate", p, q);
+    }
+
+    /**
+     * Constructs an exception with specified formatted detail message.
+     * Message formatting is delegated to {@link java.text.MessageFormat}.
+     * @param message  the custom message
+     * @param formatArguments  arguments when formatting the message
+     */
+    public FractionOverflowException(String message, Object... formatArguments) {
+        super(message, formatArguments);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionParseException.java
----------------------------------------------------------------------
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..9c1d416
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/FractionParseException.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+/**
+ * Error thrown when a string cannot be parsed into a fraction.
+ */
+public class FractionParseException extends FractionException {
+
+    /** 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
+     */
+    public FractionParseException(String source, int position, Class<?> type) {
+        super("string \"{0}\" unparseable (from position {1}) as an object of type {2}", source, position, type);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ProperBigFractionFormat.java
----------------------------------------------------------------------
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..cd52420
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ProperBigFractionFormat.java
@@ -0,0 +1,235 @@
+/*
+ * 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>
+ *
+ * @since 1.1
+ */
+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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ProperFractionFormat.java
----------------------------------------------------------------------
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..24158fa
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ProperFractionFormat.java
@@ -0,0 +1,227 @@
+/*
+ * 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>
+ *
+ * @since 1.1
+ */
+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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ZeroDenominatorException.java
----------------------------------------------------------------------
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ZeroDenominatorException.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ZeroDenominatorException.java
new file mode 100644
index 0000000..e8940a3
--- /dev/null
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/ZeroDenominatorException.java
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+/**
+ * Error thrown when a fraction with a zero denominator is created.
+ */
+public class ZeroDenominatorException extends FractionException {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 201701191752L;
+
+    public ZeroDenominatorException() {
+        super("denominator must be different from 0");
+    }
+
+    /**
+     * Constructs an exception with specified formatted detail message.
+     * Message formatting is delegated to {@link java.text.MessageFormat}.
+     * @param p current numerator
+     * @param q current denominator
+     */
+    public ZeroDenominatorException(long p, long q) {
+        super("zero denominator in fraction {0}/{1}", p, q);
+    }
+
+    /**
+     * Constructs an exception with specified formatted detail message.
+     * Message formatting is delegated to {@link java.text.MessageFormat}.
+     * @param p current numerator
+     * @param q current denominator
+     */
+    public ZeroDenominatorException(BigInteger p, BigInteger q) {
+        super("zero denominator in fraction {0}/{1}", p, q);
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/package-info.java
----------------------------------------------------------------------
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;

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionFieldTest.java
----------------------------------------------------------------------
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionFieldTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionFieldTest.java
new file mode 100644
index 0000000..53f1dee
--- /dev/null
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionFieldTest.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 org.apache.commons.numbers.core.TestUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BigFractionFieldTest {
+
+    @Test
+    public void testZero() {
+        Assert.assertEquals(BigFraction.ZERO, BigFractionField.getInstance().getZero());
+    }
+
+    @Test
+    public void testOne() {
+        Assert.assertEquals(BigFraction.ONE, BigFractionField.getInstance().getOne());
+    }
+
+    @Test
+    public void testSerial() {
+        // deserializing the singleton should give the singleton itself back
+        BigFractionField field = BigFractionField.getInstance();
+        Assert.assertTrue(field == TestUtils.serializeAndRecover(field));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionFormatTest.java
----------------------------------------------------------------------
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..fac1f54
--- /dev/null
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionFormatTest.java
@@ -0,0 +1,330 @@
+/*
+ * 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.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() {
+        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() {
+        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 (FractionParseException ex) {
+            // success
+        }
+        try {
+            improperFormat.parse(source);
+            Assert.fail(msg);
+        } catch (FractionParseException 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 (FractionParseException ex) {
+            // success
+        }
+        try {
+            improperFormat.parse(source);
+            Assert.fail(msg);
+        } catch (FractionParseException ex) {
+            // success
+        }
+    }
+
+    @Test
+    public void testParseNegative() {
+
+        {
+            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() {
+        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 (FractionParseException ex) {
+            // success
+        }
+    }
+
+    @Test
+    public void testParseProperNegative() {
+        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 (FractionParseException ex) {
+            // success
+        }
+    }
+
+    @Test
+    public void testParseProperInvalidMinus() {
+        String source = "2 -2 / 3";
+        try {
+            properFormat.parse(source);
+            Assert.fail("invalid minus in improper fraction.");
+        } catch (FractionParseException ex) {
+            // expected
+        }
+        source = "2 2 / -3";
+        try {
+            properFormat.parse(source);
+            Assert.fail("invalid minus in improper fraction.");
+        } catch (FractionParseException ex) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testParseBig() {
+        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));
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java
----------------------------------------------------------------------
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..c4c3e12
--- /dev/null
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java
@@ -0,0 +1,635 @@
+/*
+ * 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.NumbersArithmeticException;
+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 ZeroDenominatorException");
+        } catch (ZeroDenominatorException npe) {
+            // expected
+        }
+        try {
+            new BigFraction(2.0 * Integer.MAX_VALUE, 1.0e-5, 100000);
+            Assert.fail("Expecting FractionConversionException");
+        } catch (FractionConversionException fce) {
+            // 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=FractionConversionException.class)
+    public void testPositiveValueOverflow() {
+        assertFraction((long) 1e10, 1, new BigFraction(1e10, 1000));
+    }
+
+    // MATH-1029
+    @Test(expected=FractionConversionException.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 ZeroDenominatorException");
+        } catch (ZeroDenominatorException ex) {
+        }
+
+        // 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 NumbersArithmeticException");
+        } catch (NumbersArithmeticException 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 ZeroDenominatorException");
+        } catch (ZeroDenominatorException 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() throws FractionConversionException {
+        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));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/ca71f355/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionFieldTest.java
----------------------------------------------------------------------
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionFieldTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionFieldTest.java
new file mode 100644
index 0000000..f33209a
--- /dev/null
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionFieldTest.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 org.apache.commons.numbers.core.TestUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class FractionFieldTest {
+
+    @Test
+    public void testZero() {
+        Assert.assertEquals(Fraction.ZERO, FractionField.getInstance().getZero());
+    }
+
+    @Test
+    public void testOne() {
+        Assert.assertEquals(Fraction.ONE, FractionField.getInstance().getOne());
+    }
+
+    @Test
+    public void testSerial() {
+        // deserializing the singleton should give the singleton itself back
+        FractionField field = FractionField.getInstance();
+        Assert.assertTrue(field == TestUtils.serializeAndRecover(field));
+    }
+
+}