Java tutorial
/* * Universal Media Server, for streaming any media to DLNA * compatible renderers based on the http://www.ps3mediaserver.org. * Copyright (C) 2012 UMS developers. * * This program is a free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package net.pms.util; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.ParseException; import java.util.Locale; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; /** * This is an immutable class for holding a rational number without loss of * precision. As a consequence, a new instance is generated for every * mathematical operation. The reduced rationale is generated during * construction and cached for later retrieval. * <p> * This class has methods for doing basic operations mathematical operations, * and constructors for various number formats. This class implements * {@link Number} for easy conversion to other number formats, and * {@link Comparable} so two values can be compared and the values can be * sorted. In addition to {@link Comparable#compareTo}, there are * {@link #compareTo} methods that accepts most primitive number types and any * class implementing {@link Number}. * <p> * All the {@link #compareTo} methods can be used as a "replacement" for the six * boolean comparison operators ({@literal <}, ==, {@literal >}, {@literal >=}, * !=, {@literal <=}) since these are not supported for "custom" number types. * The suggested idiom for performing these comparisons is: * {@code (x.compareTo(y)} <<i>op</i>> {@code 0)}, where <<i>op</i>> * is one of the six comparison operators. * <p> * {@link Rational} supports {@link #NaN} (Not a Number) and positive and * negative infinity. Arithmetic operations involving these special values * follow the rules defined in <cite>IEEE Standard 754 Floating Point * Numbers</cite> for "special operations". * <p> * Static methods for finding the greatest common divisor and the least common * multiple for two integers are also provided with * {@link #getGreatestCommonDivisor} and {@link #getLeastCommonMultiple}. * * This class is "inspired" by the following: * <ul> * <li>http://introcs.cs.princeton.edu/java/32class/Rational.java.html</li> * <li>com.drew.lang.Rational</li> * <li>org.apache.commons.lang.math.Fraction</li> * <li>com.twelvemonkeys.imageio.metadata.exif.Rational</li> * <li>java.math.BigInteger</li> * <li>java.math.BigDecimal</li> * <li>java.math.MutableBigInteger</li> * </ul> * * @author Nadahar */ public class Rational extends Number implements Comparable<Rational> { private static final long serialVersionUID = 2L; /** * The locale-insensitive {@link DecimalFormat} used for * {@link #toDecimalString} and {@link #toDebugString}. */ protected static final DecimalFormat DECIMALFORMAT = new DecimalFormat("#0.####################", DecimalFormatSymbols.getInstance(Locale.ROOT)); /** The static instance representing the value 0 */ public static final Rational ZERO = new Rational(BigInteger.ZERO, BigInteger.ONE, BigInteger.ONE, BigInteger.ZERO, BigInteger.ONE); /** The static instance representing the value 1 */ public static final Rational ONE = new Rational(BigInteger.ONE, BigInteger.ONE, BigInteger.ONE, BigInteger.ONE, BigInteger.ONE); /** The static instance representing positive infinity */ public static final Rational POSITIVE_INFINITY = new Rational(BigInteger.ONE, BigInteger.ZERO, BigInteger.ZERO, BigInteger.ONE, BigInteger.ZERO); /** The static instance representing negative infinity */ public static final Rational NEGATIVE_INFINITY = new Rational(BigInteger.ONE.negate(), BigInteger.ZERO, BigInteger.ZERO, BigInteger.ONE.negate(), BigInteger.ZERO); /** The static instance representing negative infinity */ @SuppressWarnings("checkstyle:ConstantName") public static final Rational NaN = new Rational(BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO); /** The numerator, which also holds the sign of this {@link Rational}. */ protected final BigInteger numerator; /** The denominator, which is never negative by definition. */ protected final BigInteger denominator; /** * The numerator of the reduced {@link Rational}, which also holds the sign. */ protected final BigInteger reducedNumerator; /** * The denominator of the reduced {@link Rational}, which is never negative * by definition. */ protected final BigInteger reducedDenominator; /** * The greatest common divisor of {@code numerator} and {@code denominator}. * This is the number by which {@code numerator} and {@code denominator} are * divided to produce {@code reducedNumerator} and {@code reducedDenominator} */ protected final BigInteger greatestCommonDivisor; /** The cached hashCode */ protected final int hashCode; /** * Creates a new instance with the specified parameters. * * @param numerator the numerator. * @param denominator the denominator. * @param greatestCommonDivisor the greatest common divisor of numerator and * denominator. * @param reducedNumerator the reduced numerator. * @param reducedDenominator the reduced denominator. */ protected Rational(BigInteger numerator, BigInteger denominator, BigInteger greatestCommonDivisor, BigInteger reducedNumerator, BigInteger reducedDenominator) { this.numerator = numerator; this.denominator = denominator; this.greatestCommonDivisor = greatestCommonDivisor; this.reducedNumerator = reducedNumerator; this.reducedDenominator = reducedDenominator; hashCode = calculateHashCode(); } /** * Returns an instance that represents the value of {@code value}. * * @param value the value. * @return An instance that represents the value of {@code value}. */ @Nonnull public static Rational valueOf(double value) { if (value == Double.POSITIVE_INFINITY) { return POSITIVE_INFINITY; } if (value == Double.NEGATIVE_INFINITY) { return NEGATIVE_INFINITY; } if (Double.isNaN(value)) { return NaN; } return valueOf(BigDecimal.valueOf(value)); } /** * Returns an instance that represents the value of {@code value}. * * @param value the value. * @return An instance that represents the value of {@code value}. */ @Nonnull public static Rational valueOf(float value) { if (value == Float.POSITIVE_INFINITY) { return POSITIVE_INFINITY; } if (value == Float.NEGATIVE_INFINITY) { return NEGATIVE_INFINITY; } if (Float.isNaN(value)) { return NaN; } return valueOf(BigDecimal.valueOf(value)); } /** * Returns an instance that represents the value of {@code value}. * * @param value the value. * @return An instance that represents the value of {@code value}. */ @Nullable public static Rational valueOf(@Nullable BigDecimal value) { if (value == null) { return null; } BigInteger numerator; BigInteger denominator; if (value.signum() == 0) { return ZERO; } if (BigDecimal.ONE.equals(value)) { return ONE; } if (value.scale() > 0) { BigInteger unscaled = value.unscaledValue(); BigInteger tmpDenominator = BigInteger.TEN.pow(value.scale()); BigInteger tmpGreatestCommonDivisor = unscaled.gcd(tmpDenominator); numerator = unscaled.divide(tmpGreatestCommonDivisor); denominator = tmpDenominator.divide(tmpGreatestCommonDivisor); } else { numerator = value.toBigIntegerExact(); denominator = BigInteger.ONE; } return new Rational(numerator, denominator, BigInteger.ONE, numerator, denominator); } /** * Returns an instance by parsing the specified {@link String}. The format * must be either {@code number/number}, {@code number:number} or * {@code number}. Signs are understood for both numerator and denominator. * If {@code value} is blank or {@code null}, {@code null} is returned. * "Standard formatted" numbers are expected (no grouping, {@code .} as * decimal separator etc.). If {@code value} can't be parsed, a * {@link NumberFormatException} is thrown. * * @param value the {@link String} value to parse. * @return An instance that represents the value of {@code value}. * @throws NumberFormatException If {@code value} cannot be parsed. */ @Nullable public static Rational valueOf(@Nullable String value) { return valueOf(value, (NumberFormat) null); } /** * Returns an instance by parsing the specified {@link String}. The format * must be either {@code number/number}, {@code number:number} or * {@code number}. Signs are understood for both numerator and denominator. * If {@code value} is blank or {@code null}, {@code null} is returned. If * {@code value} can't be parsed, a {@link NumberFormatException} is thrown. * * @param value the {@link String} value to parse. * @param locale the {@link Locale} to use when parsing numbers. If * {@code null}, "standard formatted" numbers are expected (no * grouping, {@code .} as decimal separator etc.). * @return An instance that represents the value of {@code value}. * @throws NumberFormatException If {@code value} cannot be parsed. */ @Nullable public static Rational valueOf(@Nullable String value, @Nullable Locale locale) { return valueOf(value, locale == null ? null : NumberFormat.getInstance(locale)); } /** * Returns an instance by parsing the specified {@link String}. The format * must be either {@code number/number}, {@code number:number} or * {@code number}. Signs are understood for both numerator and denominator. * If {@code value} is blank or {@code null}, {@code null} is returned. If * {@code value} can't be parsed, a {@link NumberFormatException} is thrown. * * @param value the {@link String} value to parse. * @param numberFormat the {@link NumberFormat} to use when parsing numbers. * If {@code null} or not an instance of {@link DecimalFormat}, * "standard formatted" numbers are expected (no grouping, * {@code .} as decimal separator etc.). * @return An instance that represents the value of {@code value}. * @throws NumberFormatException If {@code value} cannot be parsed. */ @Nullable public static Rational valueOf(@Nullable String value, @Nullable NumberFormat numberFormat) { if (StringUtils.isBlank(value)) { return null; } String[] numbers = value.trim().split("\\s*(?:/|:)\\s*", 2); DecimalFormat decimalFormat = numberFormat instanceof DecimalFormat ? (DecimalFormat) numberFormat : null; BigDecimal decimalNumerator = parseBigDecimal(numbers[0], decimalFormat); if (decimalNumerator == null) { return null; } BigDecimal decimalDenominator; if (numbers.length > 1) { decimalDenominator = parseBigDecimal(numbers[1], decimalFormat); } else { decimalDenominator = BigDecimal.ONE; } if (decimalDenominator == null) { return null; } return valueOf(decimalNumerator, decimalDenominator); } /** * Returns an instance that represents the value of {@code value}. * * @param value the value. * @return An instance that represents the value of {@code value}. */ @Nonnull public static Rational valueOf(int value) { return valueOf((long) value); } /** * Returns an instance that represents the value of {@code value}. * * @param value the value. * @return An instance that represents the value of {@code value}. */ @Nonnull public static Rational valueOf(long value) { BigInteger numerator = BigInteger.valueOf(value); return new Rational(numerator, BigInteger.ONE, BigInteger.ONE, numerator, BigInteger.ONE); } /** * Returns an instance that represents the value of {@code value}. * * @param value the value. * @return An instance that represents the value of {@code value}. */ @Nullable public static Rational valueOf(@Nullable BigInteger value) { if (value == null) { return null; } return new Rational(value, BigInteger.ONE, BigInteger.ONE, value, BigInteger.ONE); } /** * Returns an instance with the given {@code numerator} and * {@code denominator}. * * @param numerator the numerator. * @param denominator the denominator. * @return An instance that represents the value of {@code numerator}/ * {@code denominator}. */ @Nonnull public static Rational valueOf(int numerator, int denominator) { return valueOf((long) numerator, (long) denominator); } /** * Returns an instance with the given {@code numerator} and * {@code denominator}. * * @param numerator the numerator. * @param denominator the denominator. * @return An instance that represents the value of {@code numerator}/ * {@code denominator}. */ @Nonnull public static Rational valueOf(long numerator, long denominator) { if (numerator == 0 && denominator == 0) { return NaN; } if (denominator == 0) { return numerator > 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (numerator == 0) { return ZERO; } if (numerator == denominator) { return ONE; } BigInteger biNumerator; BigInteger biDenominator; if (denominator < 0) { biNumerator = BigInteger.valueOf(-numerator); biDenominator = BigInteger.valueOf(-denominator); } else { biNumerator = BigInteger.valueOf(numerator); biDenominator = BigInteger.valueOf(denominator); } long gcd = calculateGreatestCommonDivisor(numerator, denominator); BigInteger greatestCommonDivisor = BigInteger.valueOf(gcd); BigInteger reducedNumerator; BigInteger reducedDenominator; if (gcd == 1) { reducedNumerator = biNumerator; reducedDenominator = biDenominator; } else { reducedNumerator = biNumerator.divide(greatestCommonDivisor); reducedDenominator = biDenominator.divide(greatestCommonDivisor); } return new Rational(biNumerator, biDenominator, greatestCommonDivisor, reducedNumerator, reducedDenominator); } /** * Returns an instance with the given {@code numerator} and * {@code denominator}. * * @param numerator the numerator. * @param denominator the denominator. * @return An instance that represents the value of {@code numerator}/ * {@code denominator}. */ @Nullable public static Rational valueOf(@Nullable BigInteger numerator, @Nullable BigInteger denominator) { if (numerator == null || denominator == null) { return null; } if (numerator.signum() == 0 && denominator.signum() == 0) { return NaN; } if (denominator.signum() == 0) { return numerator.signum() > 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (numerator.signum() == 0) { return ZERO; } if (numerator.equals(denominator)) { return ONE; } if (denominator.signum() < 0) { numerator = numerator.negate(); denominator = denominator.negate(); } BigInteger reducedNumerator; BigInteger reducedDenominator; BigInteger greatestCommonDivisor = calculateGreatestCommonDivisor(numerator, denominator); if (BigInteger.ONE.equals(greatestCommonDivisor)) { reducedNumerator = numerator; reducedDenominator = denominator; } else { reducedNumerator = numerator.divide(greatestCommonDivisor); reducedDenominator = denominator.divide(greatestCommonDivisor); } return new Rational(numerator, denominator, greatestCommonDivisor, reducedNumerator, reducedDenominator); } /** * Returns an instance with the given {@code numerator} and * {@code denominator}. * * @param numerator the numerator. * @param denominator the denominator. * @return An instance that represents the value of {@code numerator}/ * {@code denominator}. */ @Nullable public static Rational valueOf(@Nullable BigDecimal numerator, @Nullable BigDecimal denominator) { if (numerator == null || denominator == null) { return null; } if (numerator.signum() == 0 && denominator.signum() == 0) { return NaN; } if (denominator.signum() == 0) { return numerator.signum() > 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (numerator.signum() == 0) { return ZERO; } if (numerator.equals(denominator)) { return ONE; } if (denominator.signum() < 0) { numerator = numerator.negate(); denominator = denominator.negate(); } int scale = Math.max(numerator.scale(), denominator.scale()); if (scale > 0) { numerator = numerator.scaleByPowerOfTen(scale); denominator = denominator.scaleByPowerOfTen(scale); } BigInteger biNumerator = numerator.toBigIntegerExact(); BigInteger biDenominator = denominator.toBigIntegerExact(); BigInteger reducedNumerator; BigInteger reducedDenominator; BigInteger greatestCommonDivisor = calculateGreatestCommonDivisor(biNumerator, biDenominator); if (BigInteger.ONE.equals(greatestCommonDivisor)) { reducedNumerator = biNumerator; reducedDenominator = biDenominator; } else { reducedNumerator = biNumerator.divide(greatestCommonDivisor); reducedDenominator = biDenominator.divide(greatestCommonDivisor); } return new Rational(biNumerator, biDenominator, greatestCommonDivisor, reducedNumerator, reducedDenominator); } // Operations /** * Returns a {@link Rational} whose value is {@code (this * value)}. * * @param value the value to be multiplied by this {@link Rational}. * @return The multiplication result. */ @Nonnull public Rational multiply(int value) { return multiply(BigInteger.valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this * value)}. * * @param value the value to be multiplied by this {@link Rational}. * @return The multiplication result. */ @Nonnull public Rational multiply(long value) { return multiply(BigInteger.valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this * value)}. * * @param value the value to be multiplied by this {@link Rational}. * @return The multiplication result. */ @Nullable public Rational multiply(@Nullable BigInteger value) { if (value == null) { return null; } if (isNaN()) { return NaN; } if (isInfinite()) { if (value.signum() == 0) { return NaN; // Infinity by zero } return numerator.signum() == value.signum() ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (value.signum() == 0) { return ZERO; } if (BigInteger.ONE.equals(value.abs())) { return value.signum() < 0 ? negate() : this; } return valueOf(reducedNumerator.multiply(value), reducedDenominator); } /** * Returns a {@link Rational} whose value is {@code (this * value)}. * * @param value the value to be multiplied by this {@link Rational}. * @return The multiplication result. */ @Nonnull public Rational multiply(float value) { if (isNaN() || Float.isNaN(value)) { return NaN; } if (isInfinite() || Float.isInfinite(value)) { if (signum() == 0 || value == 0f) { return NaN; // Infinity by zero } if (value > 0) { return signum() > 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } return signum() < 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (value == 0f) { return ZERO; } return multiply(valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this * value)}. * * @param value the value to be multiplied by this {@link Rational}. * @return The multiplication result. */ @Nonnull public Rational multiply(double value) { if (isNaN() || Double.isNaN(value)) { return NaN; } if (isInfinite() || Double.isInfinite(value)) { if (signum() == 0 || value == 0f) { return NaN; // Infinity by zero } if (value > 0) { return signum() > 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } return signum() < 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (value == 0f) { return ZERO; } return multiply(valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this * value)}. * * @param value the value to be multiplied by this {@link Rational}. * @return The multiplication result. */ @Nullable public Rational multiply(@Nullable BigDecimal value) { if (value == null) { return null; } return multiply(valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this * value)}. * * @param value the value to be multiplied by this {@link Rational}. * @return The multiplication result. */ @Nullable public Rational multiply(@Nullable Rational value) { if (value == null) { return null; } if (isNaN() || value.isNaN()) { return NaN; } if (isInfinite() || value.isInfinite()) { if (signum() == 0 || value.signum() == 0) { return NaN; // Infinity by zero } return numerator.signum() == value.signum() ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (value.signum() == 0) { return ZERO; } if (value.numerator.abs().equals(value.denominator)) { return value.signum() < 0 ? this.negate() : this; } BigInteger newNumerator = reducedNumerator.multiply(value.reducedNumerator); BigInteger newDenominator = reducedDenominator.multiply(value.reducedDenominator); BigInteger gcd = newNumerator.gcd(newDenominator); return valueOf(newNumerator.divide(gcd), newDenominator.divide(gcd)); } /** * Returns a {@link Rational} whose value is {@code (this - value)}. * * @param value the value to be subtracted from this {@link Rational}. * @return The subtraction result. */ @Nonnull public Rational subtract(int value) { return add(-value); } /** * Returns a {@link Rational} whose value is {@code (this - value)}. * * @param value the value to be subtracted from this {@link Rational}. * @return The subtraction result. */ @Nonnull public Rational subtract(long value) { return add(-value); } /** * Returns a {@link Rational} whose value is {@code (this - value)}. * * @param value the value to be subtracted from this {@link Rational}. * @return The subtraction result. */ @Nullable public Rational subtract(@Nullable BigInteger value) { if (value == null) { return null; } return add(value.negate()); } /** * Returns a {@link Rational} whose value is {@code (this - value)}. * * @param value the value to be subtracted from this {@link Rational}. * @return The subtraction result. */ @Nonnull public Rational subtract(float value) { if (isNaN() || Float.isNaN(value)) { return NaN; } return add(-value); } /** * Returns a {@link Rational} whose value is {@code (this - value)}. * * @param value the value to be subtracted from this {@link Rational}. * @return The subtraction result. */ @Nonnull public Rational subtract(double value) { if (isNaN() || Double.isNaN(value)) { return NaN; } return add(-value); } /** * Returns a {@link Rational} whose value is {@code (this - value)}. * * @param value the value to be subtracted from this {@link Rational}. * @return The subtraction result. */ @Nullable public Rational subtract(@Nullable BigDecimal value) { if (value == null) { return null; } return add(value.negate()); } /** * Returns a {@link Rational} whose value is {@code (this - value)}. * * @param value the value to be subtracted from this {@link Rational}. * @return The subtraction result. */ @Nullable public Rational subtract(@Nullable Rational value) { if (value == null) { return null; } if (isNaN() || value.isNaN()) { return NaN; } if (value.signum() == 0) { return this; } return add(value.negate()); } /** * Returns a {@link Rational} whose value is {@code (this + value)}. * * @param value the value to be added to this {@link Rational}. * @return The addition result. */ @Nonnull public Rational add(int value) { return add((long) value); } /** * Returns a {@link Rational} whose value is {@code (this + value)}. * * @param value the value to be added to this {@link Rational}. * @return The addition result. */ @Nonnull public Rational add(long value) { return add(BigInteger.valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this + value)}. * * @param value the value to be added to this {@link Rational}. * @return The addition result. */ @Nullable public Rational add(@Nullable BigInteger value) { if (value == null) { return null; } if (isNaN()) { return NaN; } if (isInfinite() || value.signum() == 0) { return this; } if (BigInteger.ONE.equals(denominator)) { return valueOf(numerator.add(value), denominator); } return valueOf(numerator.add(value.multiply(denominator)), denominator); } /** * Returns a {@link Rational} whose value is {@code (this + value)}. * * @param value the value to be added to this {@link Rational}. * @return The addition result. */ @Nonnull public Rational add(float value) { if (isNaN() || Float.isNaN(value)) { return NaN; } if (value == 0f) { return this; } if (isInfinite()) { if (Float.isInfinite(value) && signum() != Math.signum(value)) { return NaN; // Infinity minus infinity } return this; } return add(valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this + value)}. * * @param value the value to be added to this {@link Rational}. * @return The addition result. */ @Nonnull public Rational add(double value) { if (isNaN() || Double.isNaN(value)) { return NaN; } if (value == 0d) { return this; } if (isInfinite()) { if (Double.isInfinite(value) && signum() != Math.signum(value)) { return NaN; // Infinity minus infinity } return this; } return add(valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this + value)}. * * @param value the value to be added to this {@link Rational}. * @return The addition result. */ @Nullable public Rational add(@Nullable BigDecimal value) { if (value == null) { return null; } if (isNaN()) { return NaN; } if (isInfinite() || value.signum() == 0) { return this; } return add(valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this + value)}. * * @param value the value to be added to this {@link Rational}. * @return The addition result. */ @Nullable public Rational add(@Nullable Rational value) { if (value == null) { return null; } if (isNaN() || value.isNaN()) { return NaN; } if (value.numerator.signum() == 0) { return this; } if (this.numerator.signum() == 0) { return value; } if (isInfinite()) { if (value.isInfinite() && signum() != value.signum()) { return NaN; // Infinity minus infinity } return this; } BigInteger lcm = calculateLeastCommonMultiple(denominator, value.denominator); return valueOf(numerator.multiply(lcm.divide(denominator)) .add(value.numerator.multiply(lcm.divide(value.denominator))), lcm); } /** * Returns a {@link Rational} whose value is the reciprocal of this * {@code (1 / this)}. * * @return The reciprocal result. */ @Nonnull public Rational reciprocal() { if (isNaN()) { return NaN; } if (isInfinite()) { return ZERO; } if (numerator.signum() == 0) { return NaN; } return valueOf(denominator, numerator); } /** * Returns a {@link Rational} whose value is {@code (this / value)}. * * @param value the value by which this {@link Rational} is to be divided. * @return The division result. * @throws ArithmeticException if {@code value} is zero. */ @Nonnull public Rational divide(int value) { return divide((long) value); } /** * Returns a {@link Rational} whose value is {@code (this / value)}. * * @param value the value by which this {@link Rational} is to be divided. * @return The division result. */ @Nonnull public Rational divide(long value) { if (isNaN()) { return NaN; } if (value == 0) { if (signum() == 0) { return NaN; } return signum() > 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (signum() == 0 || isInfinite() || 1 == Math.abs(value)) { return value < 0 ? negate() : this; } // Keep the sign in the numerator and the denominator positive if (value < 0) { return valueOf(reducedNumerator.negate(), reducedDenominator.multiply(BigInteger.valueOf(-value))); } return valueOf(reducedNumerator, reducedDenominator.multiply(BigInteger.valueOf(value))); } /** * Returns a {@link Rational} whose value is {@code (this / value)}. * * @param value the value by which this {@link Rational} is to be divided. * @return The division result. */ @Nullable public Rational divide(@Nullable BigInteger value) { if (value == null) { return null; } if (isNaN()) { return NaN; } if (value.signum() == 0) { if (signum() == 0) { return NaN; } return signum() > 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (signum() == 0 || isInfinite() || BigInteger.ONE.equals(value.abs())) { return value.signum() < 0 ? negate() : this; } // Keep the sign in the numerator and the denominator positive if (value.signum() < 0) { return valueOf(reducedNumerator.negate(), reducedDenominator.multiply(value.negate())); } return valueOf(reducedNumerator, reducedDenominator.multiply(value)); } /** * Returns a {@link Rational} whose value is {@code (this / value)}. * * @param value the value by which this {@link Rational} is to be divided. * @return The division result. */ @Nonnull public Rational divide(float value) { if (isNaN() || Float.isNaN(value) || isInfinite() && Float.isInfinite(value)) { return NaN; } if (value == 0f) { if (signum() == 0) { return NaN; } return signum() > 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (Float.isInfinite(value)) { return ZERO; } if (signum() == 0 || isInfinite() || 1f == Math.abs(value)) { return value < 0f ? negate() : this; } return divide(valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this / value)}. * * @param value the value by which this {@link Rational} is to be divided. * @return The division result. */ @Nonnull public Rational divide(double value) { if (isNaN() || Double.isNaN(value) || isInfinite() && Double.isInfinite(value)) { return NaN; } if (value == 0d) { if (signum() == 0) { return NaN; } return signum() > 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (Double.isInfinite(value)) { return ZERO; } if (signum() == 0 || isInfinite() || 1d == Math.abs(value)) { return value < 0d ? negate() : this; } return divide(valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this / value)}. * * @param value the value by which this {@link Rational} is to be divided. * @return The division result. */ @Nullable public Rational divide(@Nullable BigDecimal value) { if (value == null) { return null; } if (isNaN()) { return NaN; } if (value.signum() == 0) { if (signum() == 0) { return NaN; } return signum() > 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (signum() == 0 || isInfinite() || BigDecimal.ONE.equals(value.abs())) { return value.signum() < 0 ? negate() : this; } return divide(valueOf(value)); } /** * Returns a {@link Rational} whose value is {@code (this / value)}. * * @param value the value by which this {@link Rational} is to be divided. * @return The division result. */ @Nullable public Rational divide(@Nullable Rational value) { if (value == null) { return null; } if (isNaN() || value.isNaN() || isInfinite() && value.isInfinite()) { return NaN; } if (value.signum() == 0) { if (signum() == 0) { return NaN; } return signum() > 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY; } if (value.isInfinite()) { return ZERO; } if (signum() == 0 || isInfinite() || ONE.equals(value.abs())) { return value.signum() < 0 ? negate() : this; } return numerator.signum() == 0 ? this : multiply(value.reciprocal()); } /** * Returns a {@link Rational} whose value is {@code (-this)}. * * @return The negated result. */ @Nonnull public Rational negate() { if (numerator.signum() == 0) { return this; } return valueOf(numerator.negate(), denominator); } /** * Returns a {@link Rational} whose value is the absolute value of this * {@code (abs(this))}. * * @return The absolute result. */ @Nonnull public Rational abs() { return numerator.signum() < 0 ? valueOf(numerator.negate(), denominator) : this; } /** * Returns a {@link Rational} whose value is * <tt>(this<sup>exponent</sup>)</tt>. * * @param exponent exponent to which this {@link Rational} is to be raised. * @return <tt>this<sup>exponent</sup></tt> */ @Nonnull public Rational pow(int exponent) { if (isNaN()) { return NaN; } if (exponent == 0) { return signum() == 0 || denominator.signum() == 0 ? NaN : ONE; } if (exponent == 1) { return this; } if (isInfinite()) { if (exponent < 0) { return ZERO; } return (exponent & 1) == 0 ? abs() : this; } if (signum() == 0) { return exponent > 0 ? ZERO : POSITIVE_INFINITY; } if (exponent < 0) { if (exponent == Integer.MIN_VALUE) { return reciprocal().pow(2).pow(-(exponent / 2)); } return reciprocal().pow(-exponent); } Rational result = multiply(this); if ((exponent & 1) == 0) { return result.pow(exponent / 2); } return result.pow(exponent / 2).multiply(this); } /** * Returns the signum function of this {@link Rational}. * * @return -1, 0 or 1 as the value of this {@link Rational} is negative, * zero or positive. As a special case, signum also returns 0 for * {@code NaN} which means that <b>{@link #isNaN()} must always be * checked before {@link #signum()}</b> is used to determine if the * value is {@link #ZERO}. */ public int signum() { return numerator.signum(); } /** * Returns {@code true} if this {@link Rational} can be expressed as an * integer value, {@code false} otherwise. * * @return {@code true} if this can be expressed as an integer value, * {@code false} otherwise. */ public boolean isInteger() { return !isNaN() && !isInfinite() && BigInteger.ONE.equals(reducedDenominator); } /** * Returns {@code true} if this is a Not-a-Number (NaN) value, {@code false} * otherwise. * * @return {@code true} if this is {@code NaN}, {@code false} otherwise. */ public boolean isNaN() { return numerator.signum() == 0 && denominator.signum() == 0; } /** * Returns {@code true} if this is infinitely large in magnitude, * {@code false} otherwise. * * @return {@code true} if this is positive infinity or negative infinity, * {@code false} otherwise. */ public boolean isInfinite() { return numerator.signum() != 0 && denominator.signum() == 0; } /** * Returns {@code true} if this is infinitely positive, {@code false} * otherwise. * * @return {@code true} if this is positive infinity, {@code false} * otherwise. */ public boolean isInfinitePositive() { return numerator.signum() > 0 && denominator.signum() == 0; } /** * Returns {@code true} if this is infinitely negative, {@code false} * otherwise. * * @return {@code true} if this is negative infinity, {@code false} * otherwise. */ public boolean isInfiniteNegative() { return numerator.signum() < 0 && denominator.signum() == 0; } // Getters /** * @return The numerator. * @throws ArithmeticException If this is {@code NaN} or infinite. */ public BigInteger getNumerator() { if (isNaN() || isInfinite()) { throw new ArithmeticException("Numerator is undefined"); } return numerator; } /** * @return The denominator. * @throws ArithmeticException If this is {@code NaN} or infinite. */ public BigInteger getDenominator() { if (isNaN() || isInfinite()) { throw new ArithmeticException("Denominator is undefined"); } return denominator; } /** * The reduced numerator is the numerator divided by * {@link #getGreatestCommonDivisor}. * * @return The reduced numerator. * @throws ArithmeticException If this is {@code NaN} or infinite. */ public BigInteger getReducedNumerator() { if (isNaN() || isInfinite()) { throw new ArithmeticException("Numerator is undefined"); } return reducedNumerator; } /** * The reduced denominator is the denominator divided by * {@link #getGreatestCommonDivisor}. * * @return The reduced denominator. * @throws ArithmeticException If this is {@code NaN} or infinite. */ public BigInteger getReducedDenominator() { if (isNaN() || isInfinite()) { throw new ArithmeticException("Denominator is undefined"); } return reducedDenominator; } /** * @return The greatest common divisor of the numerator and the denominator. * @throws ArithmeticException If this is {@code NaN} or infinite. */ public BigInteger getGreatestCommonDivisor() { if (isNaN() || isInfinite()) { throw new ArithmeticException("Greatest common divisor is undefined"); } return greatestCommonDivisor; } // String methods /** * Returns a string representation of this {@link Rational} in its reduced * rational form {@code (1/2, 4 or 16/9)}. The reduced form is when both * numerator and denominator have been divided by the greatest common * divisor. * * @return The reduced {@link String} representation. */ @Override public String toString() { return generateRationalString(reducedNumerator, reducedDenominator); } /** * Returns a string representation of this {@link Rational} in its original * rational form (not reduced) {@code (2/4, 4 or 32/18)}. * * @return The {@link String} representation. */ @Nonnull public String toUnreducedString() { return generateRationalString(numerator, denominator); } /** * Returns a decimal representation of this {@link Rational}. The decimal * representation is limited to 20 decimals using * {@link RoundingMode#HALF_EVEN} and is formatted with {@link Locale#ROOT} * without grouping. * * @return The decimal {@link String} representation. */ @Nonnull public String toDecimalString() { return toDecimalString(null); } /** * Returns a decimal representation of this {@link Rational}. The decimal * representation is limited to 20 decimals using * {@link RoundingMode#HALF_EVEN} and is formatted with the specified * {@link DecimalFormat}. * * @param decimalFormat the {@link DecimalFormat} to use. * @return The decimal {@link String} representation. */ @Nonnull public String toDecimalString(@Nullable DecimalFormat decimalFormat) { if (decimalFormat == null) { decimalFormat = DECIMALFORMAT; } return generateDecimalString(reducedNumerator, reducedDenominator, decimalFormat); } /** * Returns a debug string representation of this {@link Rational}. The debug * representation is a combination of {@link #toString}, * {@link #toReducedString} and {@link #toDecimalString}. * * @return The debug {@link String} representation. */ @Nonnull public String toDebugString() { StringBuilder sb = new StringBuilder(); sb.append("Value: ").append(generateRationalString(numerator, denominator)).append(", Reduced: ") .append(generateRationalString(reducedNumerator, reducedDenominator)).append(", Decimal: ") .append(generateDecimalString(reducedNumerator, reducedDenominator, DECIMALFORMAT)); return sb.toString(); } /** * Returns a hexadecimal string representation of this {@link Rational} in * its reduced rational form {@code (5, ff or 16/c)}. The reduced form is * when both numerator and denominator have been divided by the greatest * common divisor. * * @return The reduced hexadecimal {@link String} representation. */ @Nonnull public String toHexString() { return generateRationalHexString(reducedNumerator, reducedDenominator); } /** * Returns a hexadecimal string representation of this {@link Rational} in * its original rational form (not reduced) {@code (a/2, ff or 16/c)}. * * @return The hexadecimal {@link String} representation. */ @Nonnull public String toUnreducedHexString() { return generateRationalHexString(numerator, denominator); } /** * Returns a string representation of this {@link Rational} in the form * {@code numerator:denominator} while respecting convention and adjusting * for small inaccuracies. * * @return The aspect ratio {@link String} representation. */ @Nonnull public String toAspectRatio() { if (numerator.signum() == 0 || denominator.signum() == 0) { // Return 1:1 for undefined values and 0 return "1:1"; } double value = doubleValue(); if (value >= 11.9 && value <= 12.1) { return "12.00:1"; } else if (value >= 3.9 && value <= 4.1) { return "4.00:1"; } else if (value >= 2.75 && value <= 2.77) { return "2.76:1"; } else if (value >= 2.65 && value <= 2.67) { return "24:9"; } else if (value >= 2.58 && value <= 2.6) { return "2.59:1"; } else if (value >= 2.54 && value <= 2.56) { return "2.55:1"; } else if (value >= 2.38 && value <= 2.41) { return "2.39:1"; } else if (value > 2.36 && value < 2.38) { return "2.37:1"; } else if (value >= 2.34 && value <= 2.36) { return "2.35:1"; } else if (value >= 2.33 && value < 2.34) { return "21:9"; } else if (value > 2.1 && value < 2.3) { return "11:5"; } else if (value > 1.9 && value < 2.1) { return "2.00:1"; } else if (value > 1.87 && value <= 1.9) { return "1.896:1"; } else if (value >= 1.83 && value <= 1.87) { return "1.85:1"; } else if (value >= 1.76 && value <= 1.8) { return "16:9"; } else if (value > 1.74 && value < 1.76) { return "7:4"; } else if (value >= 1.65 && value <= 1.67) { return "15:9"; } else if (value >= 1.59 && value <= 1.61) { return "16:10"; } else if (value >= 1.54 && value <= 1.56) { return "14:9"; } else if (value >= 1.49 && value <= 1.51) { return "3:2"; } else if (value > 1.42 && value < 1.44) { return "1.43:1"; } else if (value > 1.372 && value < 1.4) { return "11:8"; } else if (value > 1.35 && value <= 1.372) { return "1.37:1"; } else if (value >= 1.3 && value <= 1.35) { return "4:3"; } else if (value > 1.2 && value < 1.3) { return "5:4"; } else if (value >= 1.18 && value <= 1.195) { return "19:16"; } else if (value > 0.99 && value < 1.1) { return "1:1"; } else { return reducedNumerator + ":" + reducedDenominator; } } // Conversion to other Number formats /** * Converts this {@link Rational} to an {@code int}. This conversion is * analogous to the <i>narrowing primitive conversion</i> from * {@code double} to {@code int} as defined in section 5.1.3 of <cite>The * Java™ Language Specification</cite>: any fractional part of this * {@link Rational} will be discarded, and if the resulting " * {@link BigInteger}" is too big to fit in an {@code int}, only the * low-order 32 bits are returned. * <p> * Note that this conversion can lose information about the overall * magnitude and precision of this {@link Rational} value as well as return * a result with the opposite sign. * * @return This {@link Rational} converted to an {@code int}. */ @Override public int intValue() { if (isNaN()) { return 0; } if (isInfinite()) { return numerator.signum() > 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE; } return new BigDecimal(reducedNumerator).divide(new BigDecimal(reducedDenominator), RoundingMode.DOWN) .intValue(); } /** * * Converts this {@link Rational} to a {@code long}. This conversion is * analogous to the <i>narrowing primitive conversion</i> from * {@code double} to {@code long} as defined in section 5.1.3 of <cite>The * Java™ Language Specification</cite>: any fractional part of this * {@link Rational} will be discarded, and if the resulting " * {@link BigInteger}" is too big to fit in a {@code long}, only the * low-order 64 bits are returned. * <p> * Note that this conversion can lose information about the overall * magnitude and precision of this {@link Rational} value as well as return * a result with the opposite sign. * * @return This {@link Rational} converted to a {@code long}. */ @Override public long longValue() { if (isNaN()) { return 0; } if (isInfinite()) { return numerator.signum() > 0 ? Long.MAX_VALUE : Long.MIN_VALUE; } return new BigDecimal(reducedNumerator).divide(new BigDecimal(reducedDenominator), RoundingMode.DOWN) .longValue(); } /** * Converts this {@link Rational} to a {@link BigInteger}. This conversion * is analogous to the <i>narrowing primitive conversion</i> from * {@code double} to {@code long} as defined in section 5.1.3 of <cite>The * Java™ Language Specification</cite>: any fractional part of this * {@link Rational} will be discarded. * * @return This {@link Rational} converted to a {@link BigInteger}. * @throws ArithmeticException If this is {@code NaN} or infinite. */ @Nonnull public BigInteger bigIntegerValue() { if (isNaN()) { throw new ArithmeticException("Impossible to express NaN as BigInteger"); } if (isInfinite()) { throw new ArithmeticException("Impossible to express infinity as BigInteger"); } return new BigDecimal(reducedNumerator).divide(new BigDecimal(reducedDenominator), RoundingMode.DOWN) .toBigInteger(); } /** * Converts this {@link Rational} to a {@code float}. This conversion is * similar to the <i>narrowing primitive conversion</i> from {@code double} * to {@code float} as defined in section 5.1.3 of <cite>The Java™ * Language Specification</cite>: if this {@link Rational} has too great a * magnitude to represent as a {@code float}, it will be converted to * {@link Float#NEGATIVE_INFINITY} or {@link Float#POSITIVE_INFINITY} as * appropriate. * <p> * Note that even when the return value is finite, this conversion can lose * information about the precision of the {@link Rational} value. * * @return This {@link Rational} converted to a {@code float}. */ @Override public float floatValue() { if (isNaN()) { return Float.NaN; } if (isInfinitePositive()) { return Float.POSITIVE_INFINITY; } if (isInfiniteNegative()) { return Float.NEGATIVE_INFINITY; } return new BigDecimal(reducedNumerator).divide(new BigDecimal(reducedDenominator), MathContext.DECIMAL32) .floatValue(); } /** * Converts this {@link Rational} to a {@code double}. This conversion is * similar to the <i>narrowing primitive conversion</i> from {@code double} * to {@code float} as defined in section 5.1.3 of <cite>The Java™ * Language Specification</cite>: if this {@link Rational} has too great a * magnitude to represent as a {@code double}, it will be converted to * {@link Double#NEGATIVE_INFINITY} or {@link Double#POSITIVE_INFINITY} as * appropriate. * <p> * Note that even when the return value is finite, this conversion can lose * information about the precision of the {@link Rational} value. * * @return This {@link Rational} converted to a {@code double}. */ @Override public double doubleValue() { if (isNaN()) { return Double.NaN; } if (isInfinitePositive()) { return Double.POSITIVE_INFINITY; } if (isInfiniteNegative()) { return Double.NEGATIVE_INFINITY; } return new BigDecimal(reducedNumerator).divide(new BigDecimal(reducedDenominator), MathContext.DECIMAL64) .doubleValue(); } /** * Converts this {@link Rational} to a {@link BigDecimal}. This may involve * rounding. The conversion is limited to 100 decimals and uses * {@link RoundingMode#HALF_EVEN}. * <p> * For explicit control over the conversion, use one of the overloaded * methods. * * @return This {@link Rational} converted to a {@link BigDecimal}. * @throws ArithmeticException If this is {@code NaN} or infinite. * * @see #bigDecimalValue(MathContext) * @see #bigDecimalValue(RoundingMode) * @see #bigDecimalValue(int, RoundingMode) */ @Nonnull public BigDecimal bigDecimalValue() { if (isNaN()) { throw new ArithmeticException("Impossible to express NaN as BigDecimal"); } if (isInfinite()) { throw new ArithmeticException("Impossible to express infinity as BigDecimal"); } if (BigInteger.ONE.equals(reducedDenominator)) { return new BigDecimal(reducedNumerator); } return new BigDecimal(reducedNumerator).divide(new BigDecimal(reducedDenominator), 100, RoundingMode.HALF_EVEN); } /** * Converts this {@link Rational} to a {@link BigDecimal}. This may involve * rounding. The conversion is limited to 100 decimals and uses * {@code roundingMode}. * * @param roundingMode the {@link RoundingMode} to apply. * @return This {@link Rational} converted to a {@link BigDecimal}. * @throws ArithmeticException If this is {@code NaN} or infinite or if * {@code roundingMode} is {@link RoundingMode#UNNECESSARY} and * the specified scale is insufficient to represent the result * of the division exactly. * * @see #bigDecimalValue() * @see #bigDecimalValue(MathContext) * @see #bigDecimalValue(int, RoundingMode) */ @Nonnull public BigDecimal bigDecimalValue(RoundingMode roundingMode) { if (isNaN()) { throw new ArithmeticException("Impossible to express NaN as BigDecimal"); } if (isInfinite()) { throw new ArithmeticException("Impossible to express infinity as BigDecimal"); } if (BigInteger.ONE.equals(reducedDenominator)) { return new BigDecimal(reducedNumerator); } return new BigDecimal(reducedNumerator).divide(new BigDecimal(reducedDenominator), 100, roundingMode); } /** * Converts this {@link Rational} to a {@link BigDecimal}. This may involve * rounding. * <p> * Use {@code scale == 0} and * {@code roundingMode == RoundingMode.UNNECESSARY} to achieve absolute * precision. This will throw an {@link ArithmeticException} if the exact * quotient cannot be represented (because it has a non-terminating decimal * expansion). * * @param scale the scale of the {@link BigDecimal} quotient to be returned. * @param roundingMode the {@link RoundingMode} to apply. * @return This {@link Rational} converted to a {@link BigDecimal}. * @throws ArithmeticException If this is {@code NaN} or infinite or if * {@code roundingMode} is {@link RoundingMode#UNNECESSARY} and * the specified scale is insufficient to represent the result * of the division exactly. * * @see #bigDecimalValue() * @see #bigDecimalValue(MathContext) * @see #bigDecimalValue(RoundingMode) */ @Nonnull public BigDecimal bigDecimalValue(int scale, RoundingMode roundingMode) { if (isNaN()) { throw new ArithmeticException("Impossible to express NaN as BigDecimal"); } if (isInfinite()) { throw new ArithmeticException("Impossible to express infinity as BigDecimal"); } if (BigInteger.ONE.equals(reducedDenominator)) { return new BigDecimal(reducedNumerator); } return new BigDecimal(reducedNumerator).divide(new BigDecimal(reducedDenominator), scale, roundingMode); } /** * Converts this {@link Rational} to a {@link BigDecimal} using the given * {@link MathContext}. This may involve rounding. * <p> * * @param mathContext the {@link MathContext} to use. * @return This {@link Rational} converted to a {@link BigDecimal}. * @throws ArithmeticException If this is {@code NaN} or infinite or if the * result is inexact but the rounding mode is * {@code UNNECESSARY} or {@code mathContext.precision == 0} and * the quotient has a non-terminating decimal expansion. * * @see #bigDecimalValue() * @see #bigDecimalValue(RoundingMode) * @see #bigDecimalValue(int, RoundingMode) */ @Nonnull public BigDecimal bigDecimalValue(MathContext mathContext) { if (isNaN()) { throw new NumberFormatException("Impossible to express NaN as BigDecimal"); } if (isInfinite()) { throw new NumberFormatException("Impossible to express infinity as BigDecimal"); } if (BigInteger.ONE.equals(reducedDenominator)) { return new BigDecimal(reducedNumerator); } return new BigDecimal(reducedNumerator).divide(new BigDecimal(reducedDenominator), mathContext); } // Comparison methods /** * Compares this {@link Rational} with {@code other}. Two {@link Rational} * instances that are equal in value but have different numerators and * denominators are considered equal by this methods (like {@code 1/2} and * {@code 2/4}). * <p> * This method is provided in preference to individual methods for each of * the six boolean comparison operators ({@literal <}, ==, {@literal >}, * {@literal >=}, !=, {@literal <=}). The suggested idiom for performing * these comparisons is: {@code (x.compareTo(y)} <<i>op</i>> * {@code 0)}, where <<i>op</i>> is one of the six comparison * operators. * <p> * <b>Note:</b> {@code NaN} can't be compared by value and is considered * greater than anything but itself as defined for {@link Double}. * * @param other the {@link Rational} to which this {@link Rational} is to be * compared. * @return A negative integer, zero, or a positive integer as this * {@link Rational} is numerically less than, equal to, or greater * than {@code other}. */ @Override public int compareTo(@Nonnull Rational other) { // NaN comparison is done according to the rules for Double. if (isNaN()) { return other.isNaN() ? 0 : 1; } if (other.isNaN()) { return -1; } if (isInfinite()) { if (other.isInfinite()) { return signum() - other.signum(); } return this.signum(); } if (other.isInfinite()) { return -other.signum(); } if (signum() != other.signum()) { return signum() - other.signum(); } BigInteger[] multipliers = getMultipliers(other); return reducedNumerator.multiply(multipliers[0]).compareTo(other.reducedNumerator.multiply(multipliers[1])); } /** * Compares this {@link Rational} by value with a {@code int}. * <p> * This method is provided in preference to individual methods for each of * the six boolean comparison operators ({@literal <}, ==, {@literal >}, * {@literal >=}, !=, {@literal <=}). The suggested idiom for performing * these comparisons is: {@code (x.compareTo(y)} <<i>op</i>> * {@code 0)}, where <<i>op</i>> is one of the six comparison * operators. * * @param value the {@code int} to which this {@link Rational}'s value is to * be compared. * @return A negative integer, zero, or a positive integer as this * {@link Rational} is numerically less than, equal to, or greater * than {@code value}. */ public int compareTo(int value) { return compareTo(Integer.valueOf(value)); } /** * Compares this {@link Rational} by value with a {@code long}. * <p> * This method is provided in preference to individual methods for each of * the six boolean comparison operators ({@literal <}, ==, {@literal >}, * {@literal >=}, !=, {@literal <=}). The suggested idiom for performing * these comparisons is: {@code (x.compareTo(y)} <<i>op</i>> * {@code 0)}, where <<i>op</i>> is one of the six comparison * operators. * * @param value the {@code long} to which this {@link Rational}'s value * is to be compared. * @return A negative integer, zero, or a positive integer as this * {@link Rational} is numerically less than, equal to, or greater * than {@code value}. */ public int compareTo(long value) { return compareTo(Long.valueOf(value)); } /** * Compares this {@link Rational} by value with a {@code float}. * <p> * This method is provided in preference to individual methods for each of * the six boolean comparison operators ({@literal <}, ==, {@literal >}, * {@literal >=}, !=, {@literal <=}). The suggested idiom for performing * these comparisons is: {@code (x.compareTo(y)} <<i>op</i>> * {@code 0)}, where <<i>op</i>> is one of the six comparison * operators. * * @param value the {@code float} to which this {@link Rational}'s value * is to be compared. * @return A negative integer, zero, or a positive integer as this * {@link Rational} is numerically less than, equal to, or greater * than {@code value}. */ public int compareTo(float value) { return compareTo(Float.valueOf(value)); } /** * Compares this {@link Rational} by value with a {@code double}. * <p> * This method is provided in preference to individual methods for each of * the six boolean comparison operators ({@literal <}, ==, {@literal >}, * {@literal >=}, !=, {@literal <=}). The suggested idiom for performing * these comparisons is: {@code (x.compareTo(y)} <<i>op</i>> * {@code 0)}, where <<i>op</i>> is one of the six comparison * operators. * * @param value the {@code double} to which this {@link Rational}'s value * is to be compared. * @return A negative integer, zero, or a positive integer as this * {@link Rational} is numerically less than, equal to, or greater * than {@code value}. */ public int compareTo(double value) { return compareTo(Double.valueOf(value)); } /** * Compares this {@link Rational} by value with any class implementing * {@link Number}. * <p> * This method is provided in preference to individual methods for each of * the six boolean comparison operators ({@literal <}, ==, {@literal >}, * {@literal >=}, !=, {@literal <=}). The suggested idiom for performing * these comparisons is: {@code (x.compareTo(y)} <<i>op</i>> * {@code 0)}, where <<i>op</i>> is one of the six comparison * operators. * <p> * <b>Note:</b> {@code NaN} can't be compared by value and is considered * greater than anything but itself as defined for {@link Double}. * * @param number the {@link Number} to which this {@link Rational}'s value * is to be compared. * @return A negative integer, zero, or a positive integer as this * {@link Rational} is numerically less than, equal to, or greater * than {@code number}. */ public int compareTo(@Nonnull Number number) { // Establish special cases boolean numberIsNaN; boolean numberIsInfinite; int numberSignum; if (number instanceof Rational) { numberIsNaN = Rational.isNaN((Rational) number); numberIsInfinite = Rational.isInfinite((Rational) number); numberSignum = ((Rational) number).numerator.signum(); } else if (number instanceof Float) { numberIsNaN = Float.isNaN(number.floatValue()); numberIsInfinite = Float.isInfinite(number.floatValue()); numberSignum = (int) Math.signum(number.floatValue()); } else if (number instanceof Double) { numberIsNaN = Double.isNaN(number.doubleValue()); numberIsInfinite = Double.isInfinite(number.doubleValue()); numberSignum = (int) Math.signum(number.doubleValue()); } else { numberIsNaN = false; numberIsInfinite = false; long l = number.longValue(); numberSignum = l == 0 ? 0 : l > 0 ? 1 : -1; } // NaN comparison is done according to the rules for Double. if (isNaN()) { return numberIsNaN ? 0 : 1; } if (numberIsNaN) { return -1; } if (isInfinite()) { if (numberIsInfinite) { return signum() - numberSignum; } return this.signum(); } if (numberIsInfinite) { return -numberSignum; } // List known integer types for faster and more accurate comparison if (number instanceof BigInteger) { if (isInteger()) { return bigIntegerValue().compareTo((BigInteger) number); } return bigDecimalValue(2, RoundingMode.HALF_EVEN).compareTo(new BigDecimal((BigInteger) number)); } if (number instanceof AtomicInteger || number instanceof AtomicLong || number instanceof Byte || number instanceof Integer || number instanceof Long || number instanceof Short) { if (isInteger()) { return bigIntegerValue().compareTo(BigInteger.valueOf(number.longValue())); } return bigDecimalValue(2, RoundingMode.HALF_EVEN).compareTo(new BigDecimal(number.longValue())); } if (number instanceof BigDecimal) { Rational other = valueOf((BigDecimal) number); return compareTo(other); } return bigDecimalValue().compareTo(new BigDecimal(number.doubleValue())); } @Override public int hashCode() { return hashCode; } /** * Used internally to calculate the {@code hashCode} for caching. * * @return The calculated {@code hashCode}. */ protected int calculateHashCode() { final int prime = 31; int result = 1; result = prime * result + ((reducedDenominator == null) ? 0 : reducedDenominator.hashCode()); result = prime * result + ((reducedNumerator == null) ? 0 : reducedNumerator.hashCode()); return result; } /** * Indicates whether this instance and {@code object} are mathematically * equivalent, given that {@code other} implements {@link Number}. * <p> * 1/2 and 4/8 are considered equal by this method, as are 4/2 and 2. * <p> * Equality is determined by equality of {@code reducedNumerator} and * {@code reducedDenominator} if {@code object} is another {@link Rational}. * If not, but {@code object} implements {@link Number}, * {@code compareTo(Number) == 0} is used to determine equality. * <p> * To test equal representation, use {@link #equalsExact}. * * @param object the reference object with which to compare. * @return {@code true} if {@code object} is a {@link Rational} and are * mathematically equivalent, {@code false} otherwise. */ @Override public boolean equals(Object object) { if (this == object) { return true; } if (object == null) { return false; } if (!(object instanceof Number)) { return false; } if (object instanceof Rational) { Rational other = (Rational) object; if (reducedDenominator == null) { if (other.reducedDenominator != null) { return false; } } else if (!reducedDenominator.equals(other.reducedDenominator)) { return false; } if (reducedNumerator == null) { if (other.reducedNumerator != null) { return false; } } else if (!reducedNumerator.equals(other.reducedNumerator)) { return false; } } else { if (reducedNumerator == null || reducedDenominator == null) { // Should be impossible throw new AssertionError("reducedNumerator or reducedDenominator is null"); } return compareTo((Number) object) == 0; } return true; } /** * Indicates whether this instance and {@code other} have identical * numerator and denominator. * <p> * 1/2, 4/8, 4/2 and 2 are all considered non-equal by this method. * <p> * To test mathematically equivalence, use {@link #equals}. * * @param other the {@link Rational} with which to compare. * @return {@code true} if this instance and {@code other} have an identical * representation. */ public boolean equalsExact(Rational other) { return other != null && numerator.equals(other.numerator) && denominator.equals(other.denominator); } /** * Used internally to find by which factor to multiply the reduced * numerators when comparing two {@link Rational}s. * * @param other the {@link Rational} to which this {@link Rational}'s value * is to be compared. * @return An array of {@link BigInteger} multipliers. * @throws ArithmeticException if either part is {@code NaN} or infinite. */ @Nonnull protected BigInteger[] getMultipliers(@Nonnull Rational other) { if (isNaN() || isInfinite() || other.isNaN() || other.isInfinite()) { throw new ArithmeticException("Can't calculate multipliers for NaN or infinity"); } BigInteger[] result = new BigInteger[2]; BigInteger lcm = calculateLeastCommonMultiple(reducedDenominator, other.reducedDenominator); result[0] = lcm.divide(reducedDenominator); result[1] = lcm.divide(other.reducedDenominator); return result; } // Static methods /** * Calculates the greatest common divisor for two {@link Long}s using * "binary GDC" with some optimizations borrowed from * {@link org.apache.commons.lang.math.Fraction#greatestCommonDivisor}. * * @param u the first number. * @param v the second number. * @return The GDC, always 1 or greater. * @throws ArithmeticException if GDC is greater than {@link Long#MAX_VALUE}. */ public static long calculateGreatestCommonDivisor(long u, long v) { if (Math.abs(u) <= 1 || Math.abs(v) <= 1) { return 1; } // keep u and v negative, as negative integers range down to // -2^63, while positive numbers can only be as large as 2^63-1. if (u > 0) { u = -u; } if (v > 0) { v = -v; } int k = 0; while ((u & 1) == 0 && (v & 1) == 0 && k < 63) { u >>= 1; v >>= 1; k++; } if (k == 63) { throw new ArithmeticException("Overflow: gcd is 2^63"); } long t = ((u & 1) == 1) ? v : -(u >> 1); do { while ((t & 1) == 0) { t >>= 1; } if (t > 0) { u = -t; } else { v = t; } t = (v - u) >> 1; } while (t != 0); return -u * (1 << k); } /** * Calculates the greatest common divisor for two {@link BigInteger}s using * {@link BigInteger#gcd}. * * @param u the first number. * @param v the second number. * @return The GDC, always 1 or greater. */ @Nullable public static BigInteger calculateGreatestCommonDivisor(@Nullable BigInteger u, @Nullable BigInteger v) { if (u == null || v == null) { return null; } if (u.abs().compareTo(BigInteger.ONE) <= 0 || v.abs().compareTo(BigInteger.ONE) <= 0) { return BigInteger.ONE; } return u.gcd(v); } /** * Calculates the least common multiple for two {@link BigInteger}s using the formula * {@code u * v / gcd(u, v)} where {@code gcd} is the greatest common divisor for the two. * * @param u the first number. * @param v the second number. * @return The LCM, always 1 or greater. */ @Nullable public static BigInteger calculateLeastCommonMultiple(@Nullable BigInteger u, @Nullable BigInteger v) { if (u == null || v == null) { return null; } if (u.signum() == 0 && v.signum() == 0) { return BigInteger.ONE; } u = u.abs(); v = v.abs(); if (u.signum() == 0) { return v; } if (v.signum() == 0) { return u; } return u.divide(calculateGreatestCommonDivisor(u, v)).multiply(v); } /** * Tests if the specified {@link Rational} is {@code null}, * {@link Rational#ZERO} or {@link Rational#NaN}. * * @param rational the {@link Rational} to evaluate. * @return {@code true} if {@code rational} is {@code null} or its value is * zero or {@code NaN}, {@code false} otherwise. */ public static boolean isBlank(@Nullable Rational rational) { return rational == null || rational.signum() == 0; } /** * Tests if the specified {@link Rational} is not {@code null}, not * {@link Rational#ZERO} and not {@link Rational#NaN}. * * @param rational the {@link Rational} to evaluate. * @return {@code true} if {@code rational} is not {@code null} and its * value isn't zero or {@code NaN}, {@code false} otherwise. */ public static boolean isNotBlank(@Nullable Rational rational) { return rational != null && rational.numerator.signum() != 0; } /** * Tests if the specified {@link Rational} is a "regular" number, which * means not {@code null}, not {@code NaN} and not infinite. * * @param rational the {@link Rational} to evaluate. * @return {@code true} if {@code rational} is not {@code null}, not * {@code NaN} and not infinite; {@code false} otherwise. */ public static boolean isRegular(@Nullable Rational rational) { return rational != null && rational.denominator != null; } /** * Returns {@code true} if the specified {@link Rational} can be expressed * as an integer value, {@code false} otherwise. * * @param rational the {@link Rational} to evaluate. * @return {@code true} if {@code rational} can be expressed as an integer * value, {@code false} otherwise. */ public static boolean isInteger(@Nullable Rational rational) { return rational == null ? false : rational.isInteger(); } /** * Returns {@code true} if the specified {@link Rational} has a Not-a-Number * ({@code NaN}) value, {@code false} otherwise. * * @param rational the {@link Rational} to be tested. * @return {@code true} if {@code rational} is {@code NaN}, {@code false} * otherwise. */ public static boolean isNaN(@Nullable Rational rational) { return rational == null ? false : rational.isNaN(); } /** * Returns {@code true} if the specified {@link Rational} is infinitely * large in magnitude, {@code false} otherwise. * * @param rational the {@link Rational} to be tested. * @return {@code true} if the value of the argument is positive infinity or * negative infinity, {@code false} otherwise. */ public static boolean isInfinite(@Nullable Rational rational) { return rational == null ? false : rational.isInfinite(); } /** * Returns {@code true} if the specified {@link Rational} is infinitely * positive, {@code false} otherwise. * * @param rational the {@link Rational} to be tested. * @return {@code true} if value of the argument is positive infinity, * {@code false} otherwise. */ public static boolean isInfinitePositive(@Nullable Rational rational) { return rational == null ? false : rational.isInfinitePositive(); } /** * Returns {@code true} if the specified {@link Rational} is infinitely negative, {@code false} * otherwise. * * @param rational the {@link Rational} to be tested. * @return {@code true} if value of the argument is negative infinity, {@code false} * otherwise. */ public static boolean isInfiniteNegative(@Nullable Rational rational) { return rational == null ? false : rational.isInfiniteNegative(); } /** * Parses the specified {@link String} and returns a {@link BigDecimal} * using the specified {@link DecimalFormat}. {@code value} is expected to * be without leading or trailing whitespace. If {@code value} is blank, * {@code null} will be returned. * * @param value the {@link String} to parse. * @param decimalFormat the {@link DecimalFormat} to use when parsing. * @return The resulting {@link BigDecimal}. * @throws NumberFormatException If {@code value} cannot be parsed. */ @Nullable public static BigDecimal parseBigDecimal(@Nullable String value, @Nullable DecimalFormat decimalFormat) { if (StringUtils.isBlank(value)) { return null; } if (decimalFormat != null) { decimalFormat.setParseBigDecimal(true); try { return (BigDecimal) decimalFormat.parseObject(value); } catch (ParseException e) { throw new NumberFormatException("Unable to parse \"" + value + "\": " + e.getMessage()); } } return new BigDecimal(value); } /** * Used internally to generate a hexadecimal rational string representation * from two {@link BigInteger}s. * * @param numerator the numerator. * @param denominator the denominator. * @return The hexadecimal rational string representation. */ @Nonnull protected static String generateRationalHexString(@Nonnull BigInteger numerator, @Nonnull BigInteger denominator) { if (denominator.signum() == 0) { if (numerator.signum() == 0) { return "NaN"; } return numerator.signum() > 0 ? "\u221e" : "-\u221e"; } if (BigInteger.ONE.equals(denominator)) { return numerator.toString(16); } return numerator.toString(16) + "/" + denominator.toString(16); } /** * Used internally to generate a rational string representation from two * {@link BigInteger}s. * * @param numerator the numerator. * @param denominator the denominator. * @return The rational string representation. */ @Nonnull protected static String generateRationalString(@Nonnull BigInteger numerator, @Nonnull BigInteger denominator) { if (denominator.signum() == 0) { if (numerator.signum() == 0) { return "NaN"; } return numerator.signum() > 0 ? "\u221e" : "-\u221e"; } if (BigInteger.ONE.equals(denominator)) { return numerator.toString(); } return numerator.toString() + "/" + denominator.toString(); } /** * Used internally to generate a decimal string representation from two * {@link BigInteger}s. The decimal representation is limited to 20 * decimals. * * @param numerator the numerator. * @param denominator the denominator. * @param decimalFormat the {@link DecimalFormat} instance to use for * formatting. * @return The string representation. */ protected static String generateDecimalString(@Nonnull BigInteger numerator, @Nonnull BigInteger denominator, @Nonnull DecimalFormat decimalFormat) { if (denominator.signum() == 0) { if (numerator.signum() == 0) { return "NaN"; } return numerator.signum() > 0 ? "\u221e" : "-\u221e"; } if (BigInteger.ONE.equals(denominator)) { return numerator.toString(); } BigDecimal decimalValue = new BigDecimal(numerator).divide(new BigDecimal(denominator), 20, RoundingMode.HALF_EVEN); return decimalFormat.format(decimalValue); } }