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));
+ }
+
+}