Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 7AA57200C22 for ; Mon, 6 Feb 2017 13:52:27 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 79318160B6B; Mon, 6 Feb 2017 12:52:27 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 6B6ED160B62 for ; Mon, 6 Feb 2017 13:52:24 +0100 (CET) Received: (qmail 22244 invoked by uid 500); 6 Feb 2017 12:52:23 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 22198 invoked by uid 99); 6 Feb 2017 12:52:22 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 06 Feb 2017 12:52:22 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id B8372DFBDB; Mon, 6 Feb 2017 12:52:22 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: raydecampo@apache.org To: commits@commons.apache.org Date: Mon, 06 Feb 2017 12:52:23 -0000 Message-Id: <87903983b6574cb79aa34303dbc2dc08@git.apache.org> In-Reply-To: <565b4309f95a498c9852aa968f17777a@git.apache.org> References: <565b4309f95a498c9852aa968f17777a@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [02/13] commons-numbers git commit: NUMBERS-6: Create structure for fraction module within commons-numbers. archived-at: Mon, 06 Feb 2017 12:52:27 -0000 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. + *

+ * This class is a singleton. + *

+ * @see Fraction + * @since 2.0 + */ +public class FractionField implements /*Field, */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> getRuntimeClass() { + return Fraction.class; + } +*/ + // CHECKSTYLE: stop HideUtilityClassConstructor + /** Holder for the instance. + *

We use here the Initialization On Demand Holder Idiom.

+ */ + 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. obj 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 obj 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. + *

+ * 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 ParseException.

+ * + * @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. + *

+ * 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 ParseException.

+ * + * @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. + *

+ * 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 ParseException.

+ * + * @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. + *

+ * 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 ParseException.

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