/* * Copyright (c) 2013, SRI International * All rights reserved. * Licensed under the The BSD 3-Clause License; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://opensource.org/licenses/BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the aic-util nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ /* <b>Note:</b> Closely based on freely available version 'BigRational.java', developed * by Eric Laroche, which can be found at: <a * href="http://www.lrdev.com/lr/java/">http://www.lrdev.com/lr/java/</a> * BigRational.java -- dynamically sized big rational numbers. ** ** Copyright (C) 2002-2010 Eric Laroche. All rights reserved. ** ** @author Eric Laroche <laroche@lrdev.com> ** @version @(#)$Id: BigRational.java,v 1.3 2010/03/24 20:11:34 laroche Exp $ ** ** This program is free software; ** you can redistribute it and/or modify it. ** ** 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. ** */ package com.sri.ai.util.math; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import com.google.common.annotations.Beta; import com.sri.ai.util.AICUtilConfiguration; /** * Rational implements dynamically sized arbitrary precision immutable rational * numbers.<br> * <br> * * <p> * Dynamically sized means that (provided enough memory) the Rational numbers * can't overflow (nor underflow); this characteristic is different from Java's * data types int and long, but common with BigInteger (which implements only * integer numbers, i.e. no fractions) and BigDecimal (which only implements * precisely rational numbers with denominators that are products of 2 and 5 * [and implied 10]). Arbitrary precision means that there is no loss of * precision with common arithmetic operations such as addition, subtraction, * multiplication, and division (BigDecimal loses precision with division, if * factors other than 2 or 5 are involved). [Doubles and floats can overflow and * underflow, have a limited precision, and only implement precisely rationals * with a denominator that is a power of 2.] * * <p> * Rational provides most operations needed in rational number space * calculations, including addition, subtraction, multiplication, division, * integer power, remainder/modulus, comparison, and different roundings. * * <p> * Rational provides conversion methods to and from the native types long, int, * short, and byte (including support for unsigned values), double (binary64), * float (binary32), quad (binary128, quadruple), half (binary16), and * BigInteger. Rational can parse and print many string representations: * rational, dot notations, with exponent, even mixtures thereof, and supports * different radixes/bases (2 to typically 36 [limited by BigInteger parsing * implementation]). * * <p> * Rational uses java.math.BigInteger (JDK 1.1 and later) internally. * * <p> * Binary operations (e.g. add, multiply) calculate their results from a * Rational object ('this') and one [Rational or long] argument, returning a new * immutable Rational object. Both the original object and the argument are left * unchanged (hence immutable). Unary operations (e.g. negate, invert) calculate * their result from the Rational object ('this'), returning a new immutable * Rational object. The original object is left unchanged. * * <p> * Most operations are precise (i.e. without loss of precision); exceptions are * the conversion methods to limited precision types (doubleValue, floatValue), * rounding (round), truncation (bigIntegerValue, floor, ceiling, truncate), as * well as obviously the printing methods that include a precision parameter * (toStringDot, toStringDotRelative, toStringExponent). * * <p> * Rational doesn't provide a notion of "infinity" ([+-]Infinity) and * "not a number" (NaN); IEEE 754 floating point Infinity and NaN are rejected * (throwing a NumberFormatException). Operations such as 0/0 result in an * ArithmeticException. * * <p> * Rational internally uses private proxy functions for BigInteger * functionality, including scanning and multiplying, to enhance speed and to * realize fast checks for common values (1, 0, etc.). * * <p> * Constructor samples: normal rational form, abbreviated form, fixed point * form, abbreviated fixed point form, [exponential] floating point form, * different radixes/bases different from 10, doubles/floats: * * <pre> * Rational("-21/35") : rational -3/5 * Rational("/3") : rational 1/3 * Rational("3.4") : rational 17/5 * Rational(".7") : 0.7, rational 7/10 * Rational("-65.4E-3") : -327/5000 * Rational("f/37", 0x10) : 3/11 * Rational("f.37", 0x10) : 3895/256 * Rational("-dcba.efgh", 23) : -46112938320/279841 * Rational("1011101011010110", 2) : 47830 * Rational(StrictMath.E) : 6121026514868073/2251799813685248 * Rational((float)StrictMath.E) : 2850325/1048576 * </pre> * * <p> * Also accepted are denormalized representations such as: * * <pre> * Rational("2.5/-3.5"): -5/7 * Rational("12.34E-1/-56.78E-1") : -617/2839 * </pre> * * <p> * Printing: rational form, fixed point (dot) forms with different absolute * precisions (including negative precision), with relative precisions, * exponential form, different radix: * * <pre> * Rational("1234.5678") : "6172839/5000" * Rational("1234.5678").toStringDot(6) : "1234.567800" * Rational("1234.5678").toStringDot(2) : "1234.57" * Rational("1234.5678").toStringDot(-2) : "1200" * Rational("1234.5678").toStringDotRelative(6) : "1234.57" * Rational("0.00012345678").toStringDotRelative(3) : "0.000123" * Rational("1234.5678").toStringExponent(2) : "1.2E3" * Rational("1011101011010110", 2).toString(0x10) : "bad6" * </pre> * * <p> * Usage: Rational operations can be conveniently chained (sample from Rational * internal conversion from IEEE 754 bits): * * <pre> * Rational.valueOf(2).power(exponent) * .multiply(fraction.add(Rational.valueOfUnsigned(mantissa))) * .multiply(sign); * </pre> * * @author Eric Laroche <laroche@lrdev.com> * @author oreilly * */ @Beta public class Rational extends Number implements Cloneable, Comparable<Object> { // // PRIVATE CONSTANTS // Note: Need to declare first as some of the public // constants are dependent on these being initialized beforehand. private static final long serialVersionUID = 1L; // // Note: following constants can't be constructed using // Rational.bigIntegerValueOf(). // That one _uses_ the constants (avoid circular dependencies). /** * Constant internally used, for convenience and speed. Used as zero * numerator. Used for fast checks. */ private static BigIntegerNumber BIG_INTEGER_ZERO; /** * Constant internally used, for convenience and speed. Used as neutral * denominator. Used for fast checks. */ private static BigIntegerNumber BIG_INTEGER_ONE; /** * Constant internally used, for convenience and speed. Used for fast * checks. */ private static BigIntegerNumber BIG_INTEGER_MINUS_ONE; /** * Constant internally used, for convenience and speed. Used in rounding * zero numerator. _Not_ used for checks. */ private static BigIntegerNumber BIG_INTEGER_TWO; /** * Constant internally used, for convenience and speed. _Not_ used for * checks. */ private static BigIntegerNumber BIG_INTEGER_MINUS_TWO; /** * Constant internally used, for convenience and speed. Corresponds to * DEFAULT_RADIX, used in reading, scaling and printing. _Not_ used for * checks. */ private static BigIntegerNumber BIG_INTEGER_TEN; /** * Constant internally used, for convenience and speed. Used in reading, * scaling and printing. _Not_ used for checks. */ private static BigIntegerNumber BIG_INTEGER_SIXTEEN; /** * The constant two to the power of 64 (18446744073709551616). Used is * slicing larger [than double size] IEEE 754 values. */ private static BigIntegerNumber BIG_INTEGER_TWO_POWER_64; // some more constants, often used as radixes/bases /** * The constant two (2). */ private static Rational TWO; /** * The constant ten (10). */ private static Rational TEN; /** * The constant sixteen (16). */ private static Rational SIXTEEN; /** * The constant two to the power of 64 (18446744073709551616). Used is * slicing larger [than double size] IEEE 754 values. */ private static Rational TWO_POWER_64; /** * Constant internally used, for speed. */ // calculated via Rational((float)(StrictMath.log(10)/StrictMath.log(2))) // note: don't use float/double operations in this code though (except for // test()) private static Rational LOGARITHM_TEN_GUESS; /** * Constant internally used, for speed. */ private static Rational LOGARITHM_SIXTEEN; /** * Number of explicit fraction bits in an IEEE 754 double (binary64) float, * 52. */ private final static int DOUBLE_FLOAT_FRACTION_SIZE = 52; /** * Number of exponent bits in an IEEE 754 double (binary64) float, 11. */ private final static int DOUBLE_FLOAT_EXPONENT_SIZE = 11; /** * Number of explicit fraction bits in an IEEE 754 single (binary32) float, * 23. */ private final static int SINGLE_FLOAT_FRACTION_SIZE = 23; /** * Number of exponent bits in an IEEE 754 single (binary32) float, 8. */ private final static int SINGLE_FLOAT_EXPONENT_SIZE = 8; /** * Number of explicit fraction bits in an IEEE 754 half (binary16) float, * 10. */ private final static int HALF_FLOAT_FRACTION_SIZE = 10; /** * Number of exponent bits in an IEEE 754 half (binary16) float, 5. */ private final static int HALF_FLOAT_EXPONENT_SIZE = 5; /** * Number of explicit fraction bits in an IEEE 754 quad (binary128, * quadruple) float, 112. */ private final static int QUAD_FLOAT_FRACTION_SIZE = 112; /** * Number of exponent bits in an IEEE 754 quad (binary128, quadruple) float, * 15. */ private final static int QUAD_FLOAT_EXPONENT_SIZE = 15; // // log() related constants private static int POS_MAX_SMALL_INT_EXPONENT_VALUE = (Integer.MAX_VALUE-1); // NOTE: we subtract 1, so the 'negate' logic in pow does not overflow private static int NEG_MAX_SMALL_INT_EXPONENT_VALUE = -(Integer.MIN_VALUE-1); // private static BigIntegerNumber BIGINT_POS_MAX_SMALL_INT_EXPONENT_VALUE; private static BigIntegerNumber BIGINT_NEG_MAX_SMALL_INT_EXPONENT_VALUE; // private static Rational RATIONAL_POS_MAX_SMALL_INT_EXPONENT_VALUE; private static Rational RATIONAL_LOG_DOUBLE_MAX_VALUE; private static Rational RATIONAL_DOUBLE_MAX_VALUE; // // /** * Numerator. Numerator may be negative. Numerator may be zero, in which * case denominator must be one. [Conditions are put in place by normalize().] */ private BigIntegerNumber numerator; /** * Denominator (quotient). Denominator is never negative and never zero. * [Conditions are put in place by normalize().] */ private BigIntegerNumber denominator; // optimization, as instances are immmutable only // calculate once when needed private int hashCode = 0; // // PUBLIC CONSTANTS // /** * Default radix, used in string printing and scanning, 10 (i.e. decimal by * default). */ public final static int DEFAULT_RADIX = 10; // Note: don't use valueOf() here; valueOf implementations use the constants /** * The constant zero (0). */ // [Constant name: see class BigInteger.] public static Rational ZERO; /** * The constant one (1). */ // [Name: see class BigInteger.] public static Rational ONE; /** * The constant minus-one (-1). */ public static Rational MINUS_ONE; /** * Rounding mode to round away from zero. */ public final static int ROUND_UP = 0; /** * Rounding mode to round towards zero. */ public final static int ROUND_DOWN = 1; /** * Rounding mode to round towards positive infinity. */ public final static int ROUND_CEILING = 2; /** * Rounding mode to round towards negative infinity. */ public final static int ROUND_FLOOR = 3; /** * Rounding mode to round towards nearest neighbor unless both neighbors are * equidistant, in which case to round up. */ public final static int ROUND_HALF_UP = 4; /** * Rounding mode to round towards nearest neighbor unless both neighbors are * equidistant, in which case to round down. */ public final static int ROUND_HALF_DOWN = 5; /** * Rounding mode to round towards the nearest neighbor unless both neighbors * are equidistant, in which case to round towards the even neighbor. */ public final static int ROUND_HALF_EVEN = 6; /** * Rounding mode to assert that the requested operation has an exact result, * hence no rounding is necessary. If this rounding mode is specified on an * operation that yields an inexact result, an ArithmeticException is * thrown. */ public final static int ROUND_UNNECESSARY = 7; /** * Rounding mode to round towards nearest neighbor unless both neighbors are * equidistant, in which case to round ceiling. */ public final static int ROUND_HALF_CEILING = 8; /** * Rounding mode to round towards nearest neighbor unless both neighbors are * equidistant, in which case to round floor. */ public final static int ROUND_HALF_FLOOR = 9; /** * Rounding mode to round towards the nearest neighbor unless both neighbors * are equidistant, in which case to round towards the odd neighbor. */ public final static int ROUND_HALF_ODD = 10; /** * Default round mode, ROUND_HALF_UP. */ public final static int DEFAULT_ROUND_MODE = ROUND_HALF_UP; // private static int _toStringDotRoundingMode = ROUND_HALF_UP; // // Approximation settings static { // Assign the defaults up front. Rational.resetApproximationConfigurationFromAICUtilConfiguration(); } // // PUBLIC METHODS // // // START - Constructors /** * Construct a Rational from numerator and denominator. Both numerator and * denominator may be negative. numerator/denominator may be denormalized * (i.e. have common factors, or denominator being negative). * * @param numerator * the rational's numerator. * @param denominator * the rational's denominator (quotient) */ public Rational(BigIntegerNumber numerator, BigIntegerNumber denominator) { this(numerator, denominator, true); } private Rational(BigIntegerNumber numerator, BigIntegerNumber denominator, boolean isGCDComputationRequired) { // note: check for denominator==null done later if (denominator != null && bigIntegerIsZero(denominator)) { throw new NumberFormatException("Denominator zero"); } normalizeFrom(numerator, denominator, isGCDComputationRequired); } /** * Construct a Rational from a numerator only, denominator is defaulted to * 1. * * @param numerator * the rational's numerator. */ public Rational(BigIntegerNumber numerator) { this(numerator, BIG_INTEGER_ONE); } /** * Construct a Rational from long fix number integers representing numerator * and denominator. * * @param numerator * the rational's numerator. * @param denominator * the rational's denominator (quotient) */ public Rational(long numerator, long denominator) { this(bigIntegerValueOf(numerator), bigIntegerValueOf(denominator)); } /** * Construct a Rational from a long fix number integer representing * numerator, denominator is defaulted to 1. * * @param numerator * the rational's numerator. */ public Rational(long numerator) { this(bigIntegerValueOf(numerator), BIG_INTEGER_ONE); } /** * Clone a Rational. * <p> * [As Rationals are immutable, this copy-constructor is not that useful.] * * @param that * a rational value that the newly constructed rational is to be a clone of. */ public Rational(Rational that) { normalizeFrom(that); } /** * Construct a Rational from a string representation. * * <pre> * The supported string formats are: * "[+-]numerator" * "[+-]numerator/[+-]denominator" * "[+-]i.f" * "[+-]i" * "[+-]iE[+-]e" * "[+-]i.fE[+-]e" * (latter two only with radix <= 10, due to possible ambiguities); * numerator and denominator can be any of the * latter (i.e. mixed representations such as "-1.2E-3/-4.5E-6" are * supported). * * Samples: "-21/35", "3.4", "-65.4E-3", "f/37" (base 16), * "1011101011010110" (base 2). * </pre> * * @param strRational * a string prepresentation of a Rational number. * @param radix * the radix the string representation is meant to be in. */ public Rational(String strRational, int radix) { if (strRational == null) { throw new NumberFormatException("null"); } // For simplicity remove leading and trailing white spaces. strRational = strRational.trim(); // Within AIC-SMF we do not want rational to consider these // as special legal default formats (original BigRational allowed these). if (strRational.equals("+") || strRational.equals("-") || strRational.equals("/") || strRational.equals(".") || strRational.equals("") || strRational.startsWith("E") || strRational.startsWith("e")) { throw new NumberFormatException("underspecificed rational:"+strRational); } // '/': least precedence, and left-to-right associative // (therefore lastIndexOf and not indexOf: last slash has least // precedence) final int slash = strRational.lastIndexOf('/'); if (slash != -1) { // "[+-]numerator/[+-]denominator" String strNumerator = strRational.substring(0, slash); String strDenominator = strRational.substring(slash + 1); // suppress recursion: make stack-overflow attacks infeasible if (strNumerator.indexOf('/') != -1) { throw new NumberFormatException("can't nest '/'"); } // handle "/x" as "1/x" // [note: "1" and "-1" are treated specially and optimized // in Rational.bigIntegerValueOf(String,int).] if (strNumerator.equals("") || strNumerator.equals("+")) { strNumerator = "1"; } else if (strNumerator.equals("-")) { strNumerator = "-1"; } // handle "x/" as "x" // [note: "1" and "-1" are treated special and optimized // in Rational.bigIntegerValueOf(String,int).] if (strDenominator.equals("") || strDenominator.equals("+")) { strDenominator = "1"; } else if (strDenominator.equals("-")) { strDenominator = "-1"; } // [recursion] // [divide()'s outcome is already normalized, // so there would be no need to normalize [again]] normalizeFrom((new Rational(strNumerator, radix)) .divide(new Rational(strDenominator, radix))); return; } checkRadix(radix); // catch Java's string representations of // doubles/floats unsupported by Rational checkNaNAndInfinity(strRational, radix); // [if radix<=10:] 'E': next higher precedence, not associative // or right-to-left associative int exp = -1; // note: a distinction of exponent-'E' from large-base-digit-'e' // would be unintuitive, since case doesn't matter with both uses if (radix <= 10) { // handle both [upper/lower] cases final int exp1 = strRational.indexOf('E'); final int exp2 = strRational.indexOf('e'); exp = (exp1 == -1 || (exp2 != -1 && exp2 < exp1) ? exp2 : exp1); } if (exp != -1) { String strMantissa = strRational.substring(0, exp); String strExponent = strRational.substring(exp + 1); // suppress recursion: make stack-overflow attacks infeasible if (strExponent.indexOf('E') != -1 || strExponent.indexOf('e') != -1) { throw new NumberFormatException("can't nest 'E'"); } // skip '+' if (strExponent.length() > 0 && strExponent.charAt(0) == '+') { strExponent = strExponent.substring(1); } // handle '-' boolean negateTheExponent = false; if (strExponent.length() > 0 && strExponent.charAt(0) == '-') { negateTheExponent = true; strExponent = strExponent.substring(1); } // handle "xE", "xE+", "xE-", as "xE0" aka "x" if (strExponent.equals("")) { strExponent = "0"; } // [recursion] Rational exponent = new Rational(strExponent, radix); final int iexponent; // transform possible [overflow/fraction] exception try { iexponent = exponent.intValueExact(); } catch (ArithmeticException e) { final NumberFormatException e2 = new NumberFormatException( e.getMessage()); // make sure this operation doesn't shadow the exception to be // thrown try { e2.initCause(e); } catch (Throwable e3) { throw e2; } throw e2; } exponent = valueOf(radix).pow(iexponent); if (negateTheExponent) { exponent = exponent.invert(); } // handle "Ex", "+Ex", "-Ex", as "1Ex" if (strMantissa.equals("") || strMantissa.equals("+")) { strMantissa = "1"; } else if (strMantissa.equals("-")) { strMantissa = "-1"; } // [multiply()'s outcome is already normalized, // so there would be no need to normalize [again]] normalizeFrom((new Rational(strMantissa, radix)).multiply(exponent)); return; } // '.': next higher precedence, not associative // (can't have more than one dot) String strIntegerPart, strFractionPart; final int dot = strRational.indexOf('.'); if (dot != -1) { // "[+-]i.f" strIntegerPart = strRational.substring(0, dot); strFractionPart = strRational.substring(dot + 1); } else { // "[+-]i". [not just delegating to BigInteger.] strIntegerPart = strRational; strFractionPart = ""; } // check for multiple signs or embedded signs checkNumberFormat(strIntegerPart); // skip '+' // skip '+'. [BigInteger [likely] doesn't handle these.] if (strIntegerPart.length() > 0 && strIntegerPart.charAt(0) == '+') { strIntegerPart = strIntegerPart.substring(1); } // handle '-' boolean negativeIntegerPart = false; if (strIntegerPart.length() > 0 && strIntegerPart.charAt(0) == '-') { negativeIntegerPart = true; strIntegerPart = strIntegerPart.substring(1); } // handle ".x" as "0.x" ("." as "0.0") // handle "" as "0" // note: "0" is treated specially and optimized // in Rational.bigIntegerValueOf(String,int). if (strIntegerPart.equals("")) { strIntegerPart = "0"; } BigIntegerNumber numerator = bigIntegerValueOf(strIntegerPart, radix); BigIntegerNumber denominator; // includes the cases "x." and "." if (!strFractionPart.equals("")) { // check for signs checkFractionFormat(strFractionPart); final BigIntegerNumber fraction = bigIntegerValueOf(strFractionPart, radix); final int scale = strFractionPart.length(); denominator = bigIntegerPower(bigIntegerValueOf(radix), scale); numerator = bigIntegerMultiply(numerator, denominator) .add(fraction); } else { denominator = BIG_INTEGER_ONE; } if (negativeIntegerPart) { numerator = numerator.negate(); } normalizeFrom(numerator, denominator); } /** * Construct a Rational from a string representation using DEFAULT_RADIX. * * <pre> * The supported string formats are: * "[+-]numerator" * "[+-]numerator/[+-]denominator" * "[+-]i.f" * "[+-]i" * "[+-]iE[+-]e" * "[+-]i.fE[+-]e" * (latter two only with radix <= 10, due to possible ambiguities); * numerator and denominator can be any of the * latter (i.e. mixed representations such as "-1.2E-3/-4.5E-6" are * supported). * * Samples: "-21/35", "3.4", "-65.4E-3", "f/37" (base 16), * "1011101011010110" (base 2). * </pre> * * @param strRational * a string prepresentation of a Rational number. */ public Rational(String strRational) { this(strRational, DEFAULT_RADIX); } /** * Construct a Rational from an unscaled value and a scale value. * * @param unscaledValue * an unscaled value representation of a Rational. * @param scale * the scale to be associated with the unscaledValue * @param radix * the radix the rational is meant to be in. */ public Rational(BigIntegerNumber unscaledValue, int scale, int radix) { if (unscaledValue == null) { throw new NumberFormatException("null"); } final boolean negate = (scale < 0); if (negate) { scale = -scale; } checkRadix(radix); final BigIntegerNumber scaleValue = bigIntegerPower(bigIntegerValueOf(radix), scale); normalizeFrom((negate ? bigIntegerMultiply(unscaledValue, scaleValue) : unscaledValue), (negate ? BIG_INTEGER_ONE : scaleValue)); } /** * Construct a Rational from an unscaled value and a scale value, default * radix (10). * * @param unscaledValue * an unscaled value representation of a Rational. * @param scale * the scale to be associated with the unscaledValue */ public Rational(BigIntegerNumber unscaledValue, int scale) { this(unscaledValue, scale, DEFAULT_RADIX); } /** * Construct a Rational from an unscaled fix number value and a scale value. * * @param unscaledValue * an unscaled value representation of a Rational. * @param scale * the scale to be associated with the unscaledValue * @param radix * the radix the rational is meant to be in. */ public Rational(long unscaledValue, int scale, int radix) { this(bigIntegerValueOf(unscaledValue), scale, radix); } /** * Construct a Rational from a [IEEE 754] double [size/precision] floating * point number. * * @param value * double value from which a Rational is to be constructed. */ public Rational(double value) { normalizeFrom(valueOfDoubleBits(Double.doubleToLongBits(value))); } /** * Construct a Rational from a [IEEE 754] single [size/precision] floating * point number. * * @param value * double value from which a Rational is to be constructed. */ public Rational(float value) { normalizeFrom(valueOfFloatBits(Float.floatToIntBits(value))); } // END - Constructors // public BigIntegerNumber getNumerator() { return numerator; } public BigIntegerNumber getDenominator() { return denominator; } /** * Set the toStringDot() rounding mode to be used. * @param roundingMode * the rounding mode to use when toStringDot() is called. * @return the previously set toStringDot() rounding mode. */ public static int setToStringDotRoundingMode(int roundingMode) { int result = _toStringDotRoundingMode; _toStringDotRoundingMode = roundingMode; return result; } /** * Positive predicate. * <p> * Indicates whether this Rational is larger than zero. Zero is not * positive. * <p> * [For convenience.] * * @return true if the rational is larger than zero. */ public boolean isPositive() { return (signum() > 0); } /** * Negative predicate. * <p> * Indicates whether this Rational is smaller than zero. Zero isn't negative * either. * <p> * [For convenience.] * * @return true is the rational is smaller than zero. */ public boolean isNegative() { return (signum() < 0); } /** * Zero predicate. * <p> * Indicates whether this Rational is zero. * <p> * [For convenience and speed.] * * @return true if the rational is zero. */ public boolean isZero() { // optimization, first test is for speed. if (this == ZERO || numerator == BIG_INTEGER_ZERO) { return true; } // well, this is also optimized for speed a bit. return (signum() == 0); } /** * One predicate. * <p> * Indicates whether this Rational is 1. * <p> * [For convenience and speed.] * * @return true if the rational is 1. */ public boolean isOne() { // optimization // first test is for speed. if (this == ONE) { return true; } return equals(ONE); } /** * Minus-one predicate. * <p> * Indicates whether this Rational is -1. * <p> * [For convenience and speed.] * * @return true if the rational is -1; */ public boolean isMinusOne() { // optimization // first test is for speed. if (this == MINUS_ONE) { return true; } return equals(MINUS_ONE); } /** * Integer predicate. * <p> * Indicates whether this Rational convertible to a BigInteger without loss * of precision. True iff denominator/quotient is one. * * @return true if the rational is an integer. */ public boolean isInteger() { return bigIntegerIsOne(denominator); } /** * Rational string representation, format "[-]numerator[/denominator]". * <p> * Sample output: "6172839/5000". * * @param radix * the radix to use. * @return a string representation of the rational. */ public String toString(int radix) { checkRadixArgument(radix); final String s = stringValueOf(numerator, radix); if (isInteger()) { return s; } return s + "/" + stringValueOf(denominator, radix); } /** * Rational string representation, format "[-]numerator[/denominator]", * default radix (10). * <p> * Default string representation, as rational, not using an exponent. * <p> * Sample output: "6172839/5000". * <p> * Overwrites Object.toString(). * * @return a default string representation of the rational. */ @Override public String toString() { return toString(DEFAULT_RADIX); } /** * Fixed dot-format "[-]i.f" string representation, with a precision. * <p> * Precision may be negative, in which case the rounding affects digits left * of the dot, i.e. the integer part of the number, as well. * <p> * Sample output: "1234.567800". * <p> * Possible loss of precision. * * @param precision * the precision to use. * @param radix * the radix to use. * @return a fixed dot-format string representation of the rational. */ // @PrecisionLoss public String toStringDot(int precision, int radix) { return toStringDot(precision, radix, false); } /** * Dot-format "[-]i.f" string representation, with a precision, default * radix (10). Precision may be negative. * <p> * Sample output: "1234.567800". * <p> * Possible loss of precision. * * @param precision * the precision to use. * @return a fixed dot-format string representation of the rational. */ // @PrecisionLoss public String toStringDot(int precision) { // [possible loss of precision step] return toStringDot(precision, DEFAULT_RADIX, false); } // note: there is no 'default' precision. /** * Dot-format "[-]i.f" string representation, with a relative precision. * <p> * If the relative precision is zero or negative, "0" will be returned (i.e. * total loss of precision). * <p> * Possible loss of precision. * * @param precision * the precision to use. * @param radix * the radix to use. * @return a fixed dot-format string representation of the rational. */ // @PrecisionLoss public String toStringDotRelative(int precision, int radix) { // kind of expensive, due to expensive logarithm implementation // (with unusual radixes), and post processing checkRadixArgument(radix); // zero doesn't have any significant digits if (isZero()) { return "0"; } // relative precision zero [or less means]: no significant digits at // all, i.e. 0 // [loss of precision step] if (precision <= 0) { return "0"; } // guessed [see below: rounding issues] length: sign doesn't matter; // one digit more than logarithm final int guessedLength = abs().logarithm(radix) + 1; // [possible loss of precision step] String s = toStringDot(precision - guessedLength, radix); // [floor of] logarithm and [arithmetic] rounding [half-up] // need post-processing: // find first significant digit and check for dot boolean dot = false; int i; for (i = 0; i < s.length(); i++) { final char c = s.charAt(i); if (c == '.') { dot = true; } // expecting nothing than '-', '.', and digits if (c != '-' && c != '.' && c != '0') { break; } } // count digits / [still] check for dot int digits = 0; for (; i < s.length(); i++) { if (s.charAt(i) == '.') { dot = true; } else { digits++; } } // cut excess zeros // expecting at most 1 excess zero, e.g. for "0.0099999" final int excess = digits - precision; if (dot && excess > 0) { s = s.substring(0, s.length() - excess); } return s; } /** * Dot-format "[-]i.f" string representation, with a relative precision, * default radix (10). * <p> * If the relative precision is zero or negative, "0" will be returned (i.e. * total loss of precision). * <p> * Possible loss of precision. * * @param precision * the precision to use. * @return a fixed dot-format string representation of the rational. */ // @PrecisionLoss public String toStringDotRelative(int precision) { // [possible loss of precision step] return toStringDotRelative(precision, DEFAULT_RADIX); } /** * Exponent-format string representation, with a relative precision, * "[-]i[.f]E[-]e" (where i is one digit other than 0 exactly; f has no * trailing 0); * <p> * Sample output: "1.2E3". * <p> * Possible loss of precision. * @param precision * the precision to use. * @param radix * the radix to use. * @return an exponent-format string representation of the rational. */ // @PrecisionLoss public String toStringExponent(int precision, int radix) { checkRadixArgument(radix); // zero doesn't have any significant digits if (isZero()) { return "0"; } // relative precision zero [or less means]: no significant digits at // all, i.e. 0 // [loss of precision step] if (precision <= 0) { return "0"; } // guessed [see below: rounding issues] length: sign doesn't matter; // one digit more than logarithm final int guessedLength = abs().logarithm(radix) + 1; // [possible loss of precision step] final String s = toStringDot(precision - guessedLength, radix, true); return toExponentRepresentation(s, radix); } /** * Exponent-format string representation, with a relative precision, default * radix (10), "[-]i[.f]E[-]e" (where i is one digit other than 0 exactly; f * has no trailing 0); * <p> * Sample output: "1.2E3". * <p> * Possible loss of precision. * @param precision * the precision to use. * @return an exponent-format string representation of the rational. */ // @PrecisionLoss public String toStringExponent(int precision) { // [possible loss of precision step] return toStringExponent(precision, DEFAULT_RADIX); } /** * Add a Rational to this Rational and return a new Rational. * <p> * If one of the operands is zero, [as an optimization] the other Rational * is returned. * * @param that * the rational to be added to this rational. * @return a new Rational that is the addition of this and that. */ // [Name: see class BigInteger.] public Rational add(Rational that) { // optimization: second operand is zero (i.e. neutral element). if (that.isZero()) { return this; } // optimization: first operand is zero (i.e. neutral element). if (isZero()) { return that; } // note: not checking for that.equals(negate()), // since that would involve creation of a temporary object // note: the calculated numerator/denominator may be denormalized, // implicit normalize() is needed. // optimization: same denominator. if (bigIntegerEquals(denominator, that.denominator)) { return new Rational(numerator.add(that.numerator), denominator); } // optimization: second operand is an integer. if (that.isInteger()) { return new Rational(numerator.add(that.numerator .multiply(denominator)), denominator); } // optimization: first operand is an integer. if (isInteger()) { return new Rational(numerator.multiply(that.denominator).add( that.numerator), that.denominator); } // default case. [this would handle all cases.] return new Rational(numerator.multiply(that.denominator).add( that.numerator.multiply(denominator)), denominator.multiply(that.denominator)); } /** * Add a long fix number integer to this Rational and return a new Rational. * * @param that * a long to be added to this rational. * @return a new Rational that is the addition of this and that. */ public Rational add(long that) { return add(valueOf(that)); } /** * Subtract a Rational from this Rational and return a new Rational. * <p> * If the second operand is zero, [as an optimization] this Rational is * returned. * * @param that * the rational to be subtracted from this rational. * @return a new Rational that is the result of subtracting that from this. */ // [Name: see class BigInteger.] public Rational subtract(Rational that) { // optimization: second operand is zero. if (that.isZero()) { return this; } // optimization: first operand is zero if (isZero()) { return that.negate(); } // optimization: operands are equal if (equals(that)) { return ZERO; } // note: the calculated n/q may be denormalized, // implicit normalize() is needed. // optimization: same denominator. if (bigIntegerEquals(denominator, that.denominator)) { return new Rational(numerator.subtract(that.numerator), denominator); } // optimization: second operand is an integer. if (that.isInteger()) { return new Rational(numerator.subtract(that.numerator .multiply(denominator)), denominator); } // optimization: first operand is an integer. if (isInteger()) { return new Rational(numerator.multiply(that.denominator).subtract( that.numerator), that.denominator); } // default case. [this would handle all cases.] return new Rational(numerator.multiply(that.denominator).subtract( that.numerator.multiply(denominator)), denominator.multiply(that.denominator)); } /** * Subtract a long fix number integer from this Rational and return a new * Rational. * * @param that * the long value to be subtracted from this rational. * @return a new Rational that is the result of subtracting that from this. */ public Rational subtract(long that) { return subtract(valueOf(that)); } /** * Multiply a Rational to this Rational and return a new Rational. * <p> * If one of the operands is one, [as an optimization] the other Rational is * returned. * * @param that * the rational to be multiplied with this rational. * @return a new Rational that is the result of multiplying this with that. */ // [Name: see class BigInteger.] public Rational multiply(Rational that) { // optimization: one or both operands are zero. if (that.isZero() || isZero()) { return ZERO; } // optimization: second operand is 1. if (that.isOne()) { return this; } // optimization: first operand is 1. if (isOne()) { return that; } // optimization: second operand is -1. if (that.isMinusOne()) { return negate(); } // optimization: first operand is -1. if (isMinusOne()) { return that.negate(); } // note: the calculated numerator/denominator may be denormalized, // implicit normalize() is needed. return new Rational(bigIntegerMultiply(numerator, that.numerator), bigIntegerMultiply(denominator, that.denominator)); } /** * Multiply a long fix number integer to this Rational and return a new * Rational. * * @param that * the long to be multiplied with this rational. * @return a new Rational that is the result of multiplying this with that. */ public Rational multiply(long that) { return multiply(valueOf(that)); } /** * Divide this Rational through another Rational and return a new Rational. * <p> * If the second operand is one, [as an optimization] this Rational is * returned. * * @param that * the rational to be divided by this rational. * @return a new Rational that is the result of dividing that with this. */ // [Name: see class BigInteger.] public Rational divide(Rational that) { if (that.isZero()) { throw new ArithmeticException("division by zero"); } // optimization: first operand is zero. if (isZero()) { return ZERO; } // optimization: second operand is 1. if (that.isOne()) { return this; } // optimization: first operand is 1. if (isOne()) { return that.invert(); } // optimization: second operand is -1. if (that.isMinusOne()) { return negate(); } // optimization: first operand is -1. if (isMinusOne()) { return that.invert().negate(); } // note: the calculated numerator/denominator may be denormalized, // implicit normalize() is needed. return new Rational(bigIntegerMultiply(numerator, that.denominator), bigIntegerMultiply(denominator, that.numerator)); } /** * Divide this Rational through a long fix number integer and return a new * Rational. * * @param that * the long to be divided by this rational. * @return a new Rational that is the result of dividing that with this. */ public Rational divide(long that) { return divide(valueOf(that)); } /** * Calculate this Rational's integer power and return a new Rational. * <p> * The integer exponent may be negative. * <p> * If the exponent is one, [as an optimization] this Rational is returned. * * @param exponent * the exponent to raise this rational by * @return a new Rational that is the result of raising this rational by the * given power. */ // [Name: see classes Math, BigInteger.] public Rational pow(int exponent) { Rational result; if (exponent > POS_MAX_SMALL_INT_EXPONENT_VALUE || exponent < NEG_MAX_SMALL_INT_EXPONENT_VALUE) { result = powLargeIntegerExponent(BigIntegerNumberFactory.valueOf(exponent)); } else { result = powSmallIntExponent(exponent); } return result; } public Rational pow(Rational exponent) { Rational result; if (exponent.isInteger()) { result = pow(exponent.bigIntegerValue()); } else { // Fractional Case (i.e. nth root) result = powFractionalExponent(exponent); } return result; } public Rational pow(BigIntegerNumber exponent) { Rational result; if (isMagnitudeWithinSmallIntExponent(exponent)) { // Supported by pow(int) result = powSmallIntExponent(exponent.intValueExact()); } else { // Magnitude exponent > Integer.MAX_VALUE result = powLargeIntegerExponent(exponent); } return result; } private Rational powSmallIntExponent(int exponent) { final boolean zero = isZero(); if (zero) { if (exponent == 0) { throw new ArithmeticException("zero exp zero"); } if (exponent < 0) { throw new ArithmeticException("division by zero"); } } // optimization if (exponent == 0) { return ONE; } // optimization // test for exponent<=0 already done if (zero) { return ZERO; } // optimization if (exponent == 1) { return this; } // optimization if (exponent == -1) { return invert(); } final boolean negate = (exponent < 0); if (negate) { exponent = -exponent; } final BigIntegerNumber numerator = bigIntegerPower(this.numerator, exponent); final BigIntegerNumber denominator = bigIntegerPower(this.denominator, exponent); // note: the calculated numerator/denominator are not denormalized in // the sense of having common factors, but numerator might be negative // (and become denominator below) boolean isGCDComputationRequired = BigIntegerNumberFactory.APPROXIMATION_ENABLED; return new Rational((negate ? denominator : numerator), (negate ? numerator : denominator), isGCDComputationRequired); } // NOTE: this logic will take an extremely long time to execute if running in exact mode (intended for approximate use only). private Rational powLargeIntegerExponent(BigIntegerNumber exponent) { Rational result; if (exponent.signum() < 0) { // (n/m)^-e = (m/n)^e result = invert().powLargeIntegerExponent(exponent.abs()); } else { // NOTE: // m = Max positive exponent value allowed. // e = Exponent (is positive in this case) // b = Base (i.e. this) // if e > m : // (b^m)^(e / m) * b^(e % m) BigIntegerNumber[] exponentQuotientAndRemainder = exponent.divideAndRemainder(BIGINT_POS_MAX_SMALL_INT_EXPONENT_VALUE); // b^m Rational quotientBase = pow(RATIONAL_POS_MAX_SMALL_INT_EXPONENT_VALUE); // (b^m)^(e / m) Rational commonFactorsPow = quotientBase.pow(exponentQuotientAndRemainder[0].abs()); // b^(e % m) Rational basePowExpRemainder = pow(exponentQuotientAndRemainder[1]); // (b^m)^(e / m) * b^(e % m) result = commonFactorsPow.multiply(basePowExpRemainder); } return result; } // NOTE: // b = Base (i.e. this) // n = Numerator // d = Denominator // b^(n/d) = (b^n)^(1/d) = root(d, b^n) private Rational powFractionalExponent(Rational exponent) { Rational result; // For simplicity always work with a positive exponent if (exponent.isNegative()) { Rational reciprocal = invert(); result = reciprocal.pow(exponent.negate()); } else { // Exponent is positive // b^(n/d) = (b^n)^(1/d) // b^n Rational basePowNumerator = pow(exponent.getNumerator()); // root(d, b^n) result = nthRoot(exponent.getDenominator(), basePowNumerator); } return result; } private static boolean isMagnitudeWithinSmallIntExponent(BigIntegerNumber bigInteger) { boolean result = bigInteger.compareTo(BIGINT_POS_MAX_SMALL_INT_EXPONENT_VALUE) <= 0 && bigInteger.compareTo(BIGINT_NEG_MAX_SMALL_INT_EXPONENT_VALUE) >= 0; return result; } // NOTE: Initially, for simplicity we are going to compute the root in log space // and leverage existing supporting routines as opposed to implementing the algorithm // from scratch, e.g: https://en.wikipedia.org/wiki/Nth_root_algorithm private Rational nthRoot(BigIntegerNumber n, Rational b) { // TODO - log(b) of a negative b is not supported but you can compute roots of negative numbers // i.e. if n is odd, no solution for even n, see: https://en.wikipedia.org/wiki/Exponentiation#Rational_exponents // root(n, b) = exp(log(root(n, b))) = exp(log(b)/n) Rational logB = log(b); Rational logBDividedByN = logB.divide(new Rational(n)); Rational result; // log(Double.MAX_VALUE) = 709.782712893384 if (logBDividedByN.compareTo(RATIONAL_LOG_DOUBLE_MAX_VALUE) <= 0) { // The result will be <= Double.MAX_VALUE so we can use Math.pow() directly. double pow = Math.pow(Math.E, logBDividedByN.doubleValue()); result = new Rational(pow); } else { // if logBDividedByN > log(Double.MAX_VALUE): // // We will exceed Double.MAX_VALUE in our result so break it down as: // Double.MAX_VALUE^(logBDividedByN / Math.log(Double.MAX_VALUE)) * e^(logBDividedByN % Math.log(Double.MAX_VALUE)) Rational quotient = logBDividedByN.divide(RATIONAL_DOUBLE_MAX_VALUE); Rational[] quotientIntegerAndFractionalPart = quotient.integerAndFractionalPart(); Rational doubleMaxValuePowIntQuotient = RATIONAL_DOUBLE_MAX_VALUE.pow(quotientIntegerAndFractionalPart[0].bigIntegerValue()); // double moduloExponent = Double.MAX_VALUE * quotientIntegerAndFractionalPart[0].doubleValue(); double ePowModuloExponent = Math.pow(Math.E, moduloExponent); result = doubleMaxValuePowIntQuotient.multiply(new Rational(ePowModuloExponent)); } return result; } private Rational log(Rational b) { Rational result; if (b.isInteger()) { result = log(b.bigIntegerValue()); } else { // log(x/y) = log(x) - log(y) Rational logNumerator = log(b.getNumerator()); Rational logDenominator = log(b.getDenominator()); result = logNumerator.subtract(logDenominator); } return result; } private Rational log(BigIntegerNumber b) { BigDecimal log = b.log(BigIntegerNumberFactory.APPROXIMATION_MATH_CONTEXT); Rational result = BigIntegerNumberFactory.rationalValueOf(log); return result; } /** * Calculate the remainder of this Rational and another Rational and return * a new Rational. * <p> * The remainder result may be negative. * <p> * The remainder is based on round down (towards zero) / truncate. 5/3 == 1 * + 2/3 (remainder 2), 5/-3 == -1 + 2/-3 (remainder 2), -5/3 == -1 + -2/3 * (remainder -2), -5/-3 == 1 + -2/-3 (remainder -2). * * @param that * the rational to be divided by this rational to get a * remainder. * @return a new Rational that is the result of obtaining the remainder from * dividing that with this. */ // [Name: see class BigInteger.] public Rational remainder(Rational that) { final int thisSignum = signum(); final int thatSignum = that.signum(); if (thatSignum == 0) { throw new ArithmeticException("division by zero"); } Rational a = this; if (thisSignum < 0) { a = a.negate(); } // divisor's sign doesn't matter, as stated above. // this is also BigInteger's behavior, but don't let us be // dependent of a change in that. Rational b = that; if (thatSignum < 0) { b = b.negate(); } Rational r = a.remainderOrModulusOfPositive(b); if (thisSignum < 0) { r = r.negate(); } return r; } /** * Calculate the remainder of this Rational and a long fix number integer * and return a new Rational. * * @param that * the long to be divided by this rational to get a * remainder. * @return a new Rational that is the result of obtaining the remainder from * dividing that with this. */ public Rational remainder(long that) { return remainder(valueOf(that)); } /** * Calculate the modulus of this Rational and another Rational and return a * new Rational. * <p> * The modulus result may be negative. * <p> * Modulus is based on round floor (towards negative). 5/3 == 1 + 2/3 * (modulus 2), 5/-3 == -2 + -1/-3 (modulus -1), -5/3 == -2 + 1/3 (modulus * 1), -5/-3 == 1 + -2/-3 (modulus -2). * * @param that * the rational to be divided by this rational to get a * mod. * @return a new Rational that is the result of obtaining the mod from * dividing that with this. */ // [Name: see class BigInteger.] public Rational mod(Rational that) { final int thisSignum = signum(); final int thatSignum = that.signum(); if (thatSignum == 0) { throw new ArithmeticException("division by zero"); } Rational a = this; if (thisSignum < 0) { a = a.negate(); } Rational b = that; if (thatSignum < 0) { b = b.negate(); } Rational r = a.remainderOrModulusOfPositive(b); if (thisSignum < 0 && thatSignum < 0) { r = r.negate(); } else if (thatSignum < 0) { r = r.subtract(b); } else if (thisSignum < 0) { r = b.subtract(r); } return r; } /** * Calculate the modulus of this Rational and a long fix number integer and * return a new Rational. * * @param that * the long to be divided by this rational to get a * mod. * @return a new Rational that is the result of obtaining the mod from * dividing that with this. */ public Rational mod(long that) { return mod(valueOf(that)); } /** * Signum. -1, 0, or 1. * * @return If this Rational is negative, -1 is returned; if it is zero, 0 is * returned; if it is positive, 1 is returned. */ // [Name: see class BigInteger.] public int signum() { // note: denominator is positive. return numerator.signum(); } /** * @return a new Rational with the absolute value of this Rational. * If this Rational is zero or positive, [as an optimization] this Rational * is returned. */ // [Name: see classes Math, BigInteger.] public Rational abs() { if (signum() >= 0) { return this; } // optimization if (isMinusOne()) { return ONE; } // note: the calculated numerator/denominator are not denormalized, // implicit normalize() would not be needed. return new Rational(numerator.negate(), denominator, false); } /** * @return a new Rational with the negative value of this. * */ // [Name: see class BigInteger.] public Rational negate() { // optimization if (isZero()) { return ZERO; } // optimization if (isOne()) { return MINUS_ONE; } // optimization if (isMinusOne()) { return ONE; } // note: the calculated numerator/denominator are not denormalized, // implicit normalize() would not be needed. return new Rational(numerator.negate(), denominator, false); } /** * @return a new Rational with the inverted (reciprocal) value of this. */ public Rational invert() { if (isZero()) { throw new ArithmeticException("division by zero"); } // optimization if (isOne() || isMinusOne()) { return this; } // note: the calculated numerator/denominator are not denormalized in // the sense of having common factors, but numerator might be negative // (and become denominator below) return new Rational(denominator, numerator, false); } /** * Calculate the minimal value of two Rationals. * * @param that * the other rational to test. * @return the minimal value of two Rationals. */ // [Name: see classes Math, BigInteger.] public Rational min(Rational that) { return (compareTo(that) <= 0 ? this : that); } /** * Return the minimal value of a Rational and a long fix number integer. * * @param that * the other long to test. * @return the minimal value of this and that. */ public Rational min(long that) { return min(valueOf(that)); } /** * Return the maximal value of two Rationals. * * @param that * the other rational to test. * @return the maximal value of two Rationals. */ // [Name: see classes Math, BigInteger.] public Rational max(Rational that) { return (compareTo(that) >= 0 ? this : that); } /** * Return the maximum value of a Rational and a long fix number integer. * @param that * the other long to test. * @return the maximum value of a Rational and a long fix number integer. */ public Rational max(long that) { return max(valueOf(that)); } /** * Compare object for equality. Overwrites Object.equals(). Semantics is * that only Rationals can be equal. Never throws. * <p> * Overwrites Object.equals(Object). * * @param object * the object to be compared with this for equality. * @return true if object is a rational and this and the object have the * same value, false otherwise. */ @Override public boolean equals(Object object) { // optimization if (object == this) { return true; } // test includes null if (!(object instanceof Rational)) { return false; } final Rational that = (Rational) object; // optimization if (that.numerator == numerator && that.denominator == denominator) { return true; } boolean result = bigIntegerEquals(that.numerator, numerator) && bigIntegerEquals(that.denominator, denominator); return result; } /** * Hash code. Overwrites Object.hashCode(). * <p> * Overwrites Object.hashCode(). * * @return {@inheritDoc} */ @Override public int hashCode() { // lazy init for optimization if (hashCode == 0) { hashCode = ((numerator.hashCode() + 1) * (denominator.hashCode() + 2)); } return hashCode; } /** * Compare this Rational to another Rational. * * @param that * the Rational value to be compared to. * @return a negative integer, zero, or a positive integer as this object is * less than, equal to, or greater than the specified object. */ public int compareTo(Rational that) { // optimization if (that == this) { return 0; } final int thisSignum = signum(); final int thatSignum = that.signum(); if (thisSignum != thatSignum) { return (thisSignum < thatSignum ? -1 : 1); } // optimization: both zero. if (thisSignum == 0) { return 0; } // note: both denominators are positive. return bigIntegerMultiply(numerator, that.denominator) .compareTo( bigIntegerMultiply(that.numerator, denominator)); } /** * Compare this Rational to a BigInteger. * * @param that * the BigInteger value to be compared to. * @return a negative integer, zero, or a positive integer as this object is * less than, equal to, or greater than the specified object. */ public int compareTo(BigIntegerNumber that) { return compareTo(valueOf(that)); } /** * Compare this Rational to a long. * * @param that * the long value to be compared to. * @return a negative integer, zero, or a positive integer as this object is * less than, equal to, or greater than the specified object. */ public int compareTo(long that) { return compareTo(valueOf(that)); } /** * Compare this Rational to an Object. * <p> * Object can be Rational/BigInteger/Long/Integer/Short/Byte. * <p> * Implements Comparable.compareTo(Object) (JDK 1.2 and later). * <p> * A sample use is with a sorted map or set, e.g. TreeSet. * <p> * Only Rational/BigInteger/Long/Integer objects allowed, method will throw * otherwise. * <p> * For backward compatibility reasons we keep compareTo(Object) additionally * to compareTo(Rational). Comparable<Object> is declared to be * implemented rather than Comparable<Rational>. * * @return {@inheritDoc} */ @Override public int compareTo(Object object) { if (object instanceof Byte) { return compareTo(((Byte) object).longValue()); } if (object instanceof Short) { return compareTo(((Short) object).longValue()); } if (object instanceof Integer) { return compareTo(((Integer) object).longValue()); } if (object instanceof Long) { return compareTo(((Long) object).longValue()); } if (object instanceof BigIntegerNumber) { return compareTo((BigIntegerNumber) object); } // now assuming that it's either 'instanceof Rational' // or it'll throw a ClassCastException. return compareTo((Rational) object); } /** * Convert to BigInteger, by rounding. * <p> * Possible loss of precision. * * @return a BigInteger representation of this rational, with possible rounding. */ // @PrecisionLoss public BigIntegerNumber bigIntegerValue() { // [rounding step, possible loss of precision step] return round().numerator; } /** * Convert to long, by rounding and delegating to BigInteger. Implements * Number.longValue(). As described with BigInteger.longValue(), this just * returns the low-order [64] bits (losing information about magnitude and * sign). * <p> * Possible loss of precision. * <p> * Overwrites Number.longValue(). * * @return a long representation of this rational, with possible rounding. */ // @PrecisionLoss @Override public long longValue() { // delegate to BigInteger. // [rounding step, possible loss of precision step] return bigIntegerValue().longValue(); } /** * Convert to int, by rounding and delegating to BigInteger. Implements * Number.intValue(). As described with BigInteger.longValue(), this just * returns the low-order [32] bits (losing information about magnitude and * sign). * <p> * Possible loss of precision. * <p> * Overwrites Number.intValue(). * * @return an int representation of this rational, with possible rounding. */ // @PrecisionLoss @Override public int intValue() { // delegate to BigInteger. // [rounding step, possible loss of precision step] return bigIntegerValue().intValue(); } /** * Convert to double floating point value. Implements Number.doubleValue(). * <p> * Possible loss of precision. * <p> * Overwrites Number.doubleValue(). * * @return a double representation of this rational, with possible rounding. */ // @PrecisionLoss @Override public double doubleValue() { return Double.longBitsToDouble( // [rounding step, possible loss of precision step] doubleBitsValue()); } /** * Convert to single floating point value. Implements Number.floatValue(). * <p> * Note that Rational's [implicit] [default] rounding mode that applies * [too] on indirect double to Rational to float rounding (round-half-up) * may differ from what's done in a direct cast/coercion from double to * float (e.g. round-half-even). * <p> * Possible loss of precision. * <p> * Overwrites Number.floatValue(). * * @return a float representation of this rational, with possible rounding. */ // @PrecisionLoss @Override public float floatValue() { return Float.intBitsToFloat( // [rounding step, possible loss of precision step] floatBitsValue()); } /** * Convert to IEEE 754 double float bits. The bits can be converted to a * double by Double.longBitsToDouble(). * <p> * Possible loss of precision. * * @return a double bits value representation of this rational, with possible rounding. */ // @PrecisionLoss public long doubleBitsValue() { // [rounding step, possible loss of precision step] return (toIEEE754(this, DOUBLE_FLOAT_FRACTION_SIZE, DOUBLE_FLOAT_EXPONENT_SIZE)[0]); } /** * Convert to IEEE 754 single float bits. The bits can be converted to a * float by Float.intBitsToFloat(). * <p> * Possible loss of precision. * * @return a float bits value representation of this rational, with possible rounding. */ // @PrecisionLoss public int floatBitsValue() { // [rounding step, possible loss of precision step] return (int) (toIEEE754(this, SINGLE_FLOAT_FRACTION_SIZE, SINGLE_FLOAT_EXPONENT_SIZE)[0]); } /** * Convert this Rational to IEEE 754 half float (binary16) bits. * <p> * As a short value is returned rather than a int, care has to be taken no * unwanted sign expansion happens in subsequent operations, e.g. by masking * (x.halfBitsValue()&0xffffl) or similar * (x.halfBitsValue()==(short)0xbc00). * <p> * Possible loss of precision. * * @return a half bits representation of this rational, with possible rounding. */ // @PrecisionLoss public short halfBitsValue() { // [rounding step, possible loss of precision step] return (short) (toIEEE754(this, HALF_FLOAT_FRACTION_SIZE, HALF_FLOAT_EXPONENT_SIZE)[0]); } /** * Convert this Rational to IEEE 754 quad float (binary128, quadruple) bits. * <p> * The bits are returned in an array of two longs, big endian (higher * significant long first). * <p> * Possible loss of precision. * * @return a quad bits representation of this rational, with possible rounding. */ // @PrecisionLoss public long[] quadBitsValue() { // [rounding step, possible loss of precision step] return toIEEE754(this, QUAD_FLOAT_FRACTION_SIZE, QUAD_FLOAT_EXPONENT_SIZE); } /** * Convert this Rational to a long integer, either returning an exact result * (no rounding or truncation needed), or throw an ArithmeticException. * * @return an exact long representation of this rational. */ public long longValueExact() { final long i = longValue(); // test is kind-of costly if (!equals(valueOf(i))) { throw new ArithmeticException(isInteger() ? "overflow" : "rounding necessary"); } return i; } /** * Convert this Rational to an int, either returning an exact result (no * rounding or truncation needed), or throw an ArithmeticException. * * @return an exact int representation of this rational. */ public int intValueExact() { final int i = intValue(); // test is kind-of costly if (!equals(valueOf(i))) { throw new ArithmeticException(isInteger() ? "overflow" : "rounding necessary"); } return i; } /** * Convert this Rational to its constant (ONE, ZERO, MINUS_ONE) if possible. * * @param value * the value to be converted if possible. * @return the constrant representation of the given value if one exists, * other the input value. */ public static Rational valueOf(Rational value) { if (value == null) { throw new NumberFormatException("null"); } // note: these tests are quite expensive, // but they are minimized to a reasonable amount. // priority in the tests: 1, 0, -1; // two phase testing. // cheap tests first. // optimization if (value == ONE) { return value; } // optimization if (value == ZERO) { return value; } // optimization if (value == MINUS_ONE) { return value; } // more expensive tests later. // optimization if (value.equals(ONE)) { return ONE; } // optimization if (value.equals(ZERO)) { return ZERO; } // optimization if (value.equals(MINUS_ONE)) { return MINUS_ONE; } // not a known constant return value; } /** * Build a Rational from a String. * <p> * [Roughly] equivalent to <CODE>new Rational(value)</CODE>. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOf(String value) { if (value == null) { throw new NumberFormatException("null"); } // optimization if (value.equals("0")) { return ZERO; } // optimization if (value.equals("1")) { return ONE; } // optimization if (value.equals("-1")) { return MINUS_ONE; } return new Rational(value); } /** * Build a Rational from a BigInteger. * <p> * Equivalent to <CODE>new Rational(value)</CODE>. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOf(BigIntegerNumber value) { return new Rational(value); } /** * Build a Rational from a long fix number integer. * <p> * [Roughly] equivalent to <CODE>new Rational(value)</CODE>. * <p> * As an optimization, commonly used numbers are returned as a reused * constant. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOf(long value) { // return the internal constants if possible // optimization // check whether it's outside int range. // actually check a much narrower range, fitting the switch below. if (value >= -16 && value <= 16) { // note: test above needed to make the cast below safe // jump table, for speed switch ((int) value) { case 0: return ZERO; case 1: return ONE; case -1: return MINUS_ONE; case 2: return TWO; case 10: return TEN; case 16: return SIXTEEN; } } return new Rational(value); } // note: byte/short/int implicitly upgraded to long, // so strictly the additional implementations aren't needed; // with unsigned (below) they however are /** * Build a Rational from an int. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOf(int value) { return valueOf((long) value); } /** * Build a Rational from a short. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOf(short value) { return valueOf((long) value); } /** * Build a Rational from a byte. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOf(byte value) { return valueOf((long) value); } /** * Build a Rational from a [IEEE 754] double [size/precision] floating point * number. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOf(double value) { return new Rational(value); } /** * Build a Rational from a [IEEE 754] single [size/precision] floating point * number. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOf(float value) { return new Rational(value); } /** * Build a Rational from an unsigned long fix number integer. * <p> * The resulting Rational is positive, i.e. the negative longs are mapped to * 2**63..2**64 (exclusive). * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOfUnsigned(long value) { final Rational b = valueOf(value); // mind the long being unsigned with highest significant // bit (bit#63) set (interpreted as negative by valueOf(long)) return (b.isNegative() ? b.add(TWO_POWER_64) : b); } /** * Build a Rational from an unsigned int. * <p> * The resulting Rational is positive, i.e. the negative ints are mapped to * 2**31..2**32 (exclusive). * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOfUnsigned(int value) { // masking: suppress sign expansion return valueOf(value & 0xffffffffl); } /** * Build a Rational from an unsigned short. * <p> * The resulting Rational is positive, i.e. the negative shorts are mapped * to 2**15..2**16 (exclusive). * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOfUnsigned(short value) { // masking: suppress sign expansion return valueOf(value & 0xffffl); } /** * Build a Rational from an unsigned byte. * <p> * The resulting Rational is positive, i.e. the negative bytes are mapped to * 2**7..2**8 (exclusive). * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOfUnsigned(byte value) { // masking: suppress sign expansion return valueOf(value & 0xffl); } /** * Build a Rational from an IEEE 754 double size (double precision, * binary64) floating point number represented as long. * <p> * An IEEE 754 double size (binary64) number uses 1 bit for the sign, 11 * bits for the exponent, and 52 bits (plus 1 implicit bit) for the * fraction/mantissa. The minimal exponent encodes subnormal nubers; the * maximal exponent encodes Infinities and NaNs. * <p> * Infinities and NaNs are not supported as Rationals. * <p> * The conversion from the bits to a Rational is done without loss of * precision. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOfDoubleBits(long value) { return fromIEEE754(new long[] { value, }, DOUBLE_FLOAT_FRACTION_SIZE, DOUBLE_FLOAT_EXPONENT_SIZE); } /** * Build a Rational from an IEEE 754 single size (single precision, * binary32) floating point number represented as int. * <p> * An IEEE 754 single size (binary32) number uses 1 bit for the sign, 8 bits * for the exponent, and 23 bits (plus 1 implicit bit) for the * fraction/mantissa. The minimal exponent encodes subnormal nubers; the * maximal exponent encodes Infinities and NaNs. * <p> * Infinities and NaNs are not supported as Rationals. * <p> * The conversion from the bits to a Rational is done without loss of * precision. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOfFloatBits(int value) { // [masking: suppress sign expansion, that leads to excess bits, // that's not accepted by fromIeee754()] return fromIEEE754(new long[] { value & 0xffffffffl, }, SINGLE_FLOAT_FRACTION_SIZE, SINGLE_FLOAT_EXPONENT_SIZE); } /** * Build a Rational from an IEEE 754 half size (half precision, binary16) * floating point number represented as short. * <p> * An IEEE 754 half size (binary16) number uses 1 bit for the sign, 5 bits * for the exponent, and 10 bits (plus 1 implicit bit) for the * fraction/mantissa. The minimal exponent encodes subnormal nubers; the * maximal exponent encodes Infinities and NaNs. * <p> * Infinities and NaNs are not supported as Rationals. * <p> * The conversion from the bits to a Rational is done without loss of * precision. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOfHalfBits(short value) { // [masking: suppress sign expansion, that leads to excess bits, // that's not accepted by fromIeee754()] return fromIEEE754(new long[] { value & 0xffffl, }, HALF_FLOAT_FRACTION_SIZE, HALF_FLOAT_EXPONENT_SIZE); } /** * Build a Rational from an IEEE 754 quad size (quadruple precision, * binary128) floating point number represented as an array of two longs * (big endian; higher significant long first). * <p> * An IEEE 754 quad size (binary128, quadruple) number uses 1 bit for the * sign, 15 bits for the exponent, and 112 bits (plus 1 implicit bit) for * the fraction/mantissa. The minimal exponent encodes subnormal nubers; the * maximal exponent encodes Infinities and NaNs. * <p> * Infinities and NaNs are not supported as Rationals. * <p> * The conversion from the bits to a Rational is done without loss of * precision. * * @param value * the value to be converted to a rational. * @return a rational representation of the given value. */ public static Rational valueOfQuadBits(long[] value) { return fromIEEE754(value, QUAD_FLOAT_FRACTION_SIZE, QUAD_FLOAT_EXPONENT_SIZE); } /** * Compare two IEEE 754 quad size (quadruple precision, binary128) floating * point numbers (each represented as two longs). NaNs are not considered; * comparison is done by bits. [Convenience method.] * * @param a * the first value to compare. * @param b * the second value to compare. * @return true if both values are the same. */ // note: especially due the NaN issue commented above // (a NaN maps to many bits representations), // we call this method quadBitsEqual rather than quadEqual public static boolean quadBitsEqual(long[] a, long[] b) { if (a == null || b == null) { throw new NumberFormatException("null"); } if (a.length != 2 || b.length != 2) { throw new NumberFormatException("not a quad"); } return (a[1] == b[1] && a[0] == b[0]); } /** * Round. * <p> * Round mode is one of { * <code>ROUND_UP, ROUND_DOWN, ROUND_CEILING, ROUND_FLOOR, * ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_HALF_EVEN, * ROUND_HALF_CEILING, ROUND_HALF_FLOOR, ROUND_HALF_ODD, * ROUND_UNNECESSARY, DEFAULT_ROUND_MODE (==ROUND_HALF_UP)</code> . * <p> * If rounding isn't necessary, i.e. this Rational is an integer, [as an * optimization] this Rational is returned. * <p> * Possible loss of precision. * * @param roundMode * the rounding mode to use. * @return a new Rational based on rounding this rational using the given * rounding mode. */ // @PrecisionLoss public Rational round(int roundMode) { // optimization // return self if we don't need to round, independent of rounding mode if (isInteger()) { return this; } return new Rational( // [rounding step, possible loss of precision step] roundToBigInteger(roundMode)); } /** * Round by default mode (ROUND_HALF_UP). * <p> * Possible loss of precision. * * @return a new Rational based on rounding this rational using the ROUND_HALF_UP * rounding mode. */ // @PrecisionLoss public Rational round() { // [rounding step, possible loss of precision step] return round(DEFAULT_ROUND_MODE); } /** * Floor, round towards negative infinity. * <p> * Possible loss of precision. * * @return a new rational representing the floor value of this rational. */ // @PrecisionLoss public Rational floor() { // [rounding step, possible loss of precision step] return round(ROUND_FLOOR); } /** * Ceiling, round towards positive infinity. * <p> * Possible loss of precision. * * @return a new rational representing the ceiling value of this rational. */ // [Name: see class Math.] // @PrecisionLoss public Rational ceil() { // [rounding step, possible loss of precision step] return round(ROUND_CEILING); } /** * Truncate, round towards zero. * <p> * Possible loss of precision. * * @return a new rational representing the truncated value of this rational. */ // @PrecisionLoss public Rational truncate() { // [rounding step, possible loss of precision step] return round(ROUND_DOWN); } /** * Integer part. * <p> * Possible loss of precision. * * @return a new rational representing the integer part value of this rational. */ // @PrecisionLoss public Rational integerPart() { // [rounding step, possible loss of precision step] return round(ROUND_DOWN); } /** * Fractional part. * <p> * Possible loss of precision. * * @return a new rational representing the fractional part of this rational. */ // @PrecisionLoss public Rational fractionalPart() { // this==ip+fp; sign(fp)==sign(this) // [possible loss of precision step] return subtract(integerPart()); } /** * Return an array of Rationals with both integer and fractional part. * <p> * Integer part is returned at offset 0; fractional part at offset 1. * * @return an array of Rationals with both integer and fractional part. */ public Rational[] integerAndFractionalPart() { // note: this duplicates fractionalPart() code, for speed. final Rational[] pp = new Rational[2]; final Rational ip = integerPart(); pp[0] = ip; pp[1] = subtract(ip); return pp; } /** * Clone the current Rational. * * @return {@inheritDoc} */ @Override public Rational clone() throws CloneNotSupportedException { return (Rational) super.clone(); } public static void resetApproximationConfigurationFromAICUtilConfiguration() { resetApproximationConfiguration(AICUtilConfiguration.isRationalApproximationEnabled(), AICUtilConfiguration.getRationalApproximationPrecision(), AICUtilConfiguration.getRationalApproximationRoundingMode()); } public static void resetApproximationConfiguration(boolean enabled, int precision, RoundingMode roundingMode) { BigIntegerNumberFactory.resetApproximationConfiguration(enabled, precision, roundingMode); // Private Big Integer Numbers BIG_INTEGER_ZERO = BigIntegerNumberFactory.valueOf(0); BIG_INTEGER_ONE = BigIntegerNumberFactory.valueOf(1); BIG_INTEGER_MINUS_ONE = BigIntegerNumberFactory.valueOf(-1); BIG_INTEGER_TWO = BigIntegerNumberFactory.valueOf(2); BIG_INTEGER_MINUS_TWO = BigIntegerNumberFactory.valueOf(-2); BIG_INTEGER_TEN = BigIntegerNumberFactory.valueOf(10); BIG_INTEGER_SIXTEEN = BigIntegerNumberFactory.valueOf(16); BIG_INTEGER_TWO_POWER_64 = BigIntegerNumberFactory.valueOf(2).pow(64); // Private Rationals TWO = new Rational(2); TEN = new Rational(10); SIXTEEN = new Rational(16); TWO_POWER_64 = new Rational(BIG_INTEGER_TWO_POWER_64); LOGARITHM_TEN_GUESS = new Rational(1741647, 524288); LOGARITHM_SIXTEEN = new Rational(4); // Private log() related constants BIGINT_POS_MAX_SMALL_INT_EXPONENT_VALUE = BigIntegerNumberFactory.valueOf(POS_MAX_SMALL_INT_EXPONENT_VALUE); BIGINT_NEG_MAX_SMALL_INT_EXPONENT_VALUE = BigIntegerNumberFactory.valueOf(NEG_MAX_SMALL_INT_EXPONENT_VALUE); // RATIONAL_POS_MAX_SMALL_INT_EXPONENT_VALUE = new Rational(BIGINT_POS_MAX_SMALL_INT_EXPONENT_VALUE); RATIONAL_LOG_DOUBLE_MAX_VALUE = new Rational(""+Math.log(Double.MAX_VALUE)); RATIONAL_DOUBLE_MAX_VALUE = new Rational(""+Double.MAX_VALUE); // Public Rationals ZERO = new Rational(0); ONE = new Rational(1); MINUS_ONE = new Rational(-1); } // // PRIVATE METHODS // /** * Normalize Rational. Denominator will be positive, numerator and * denominator will have no common divisor. BigIntegers -1, 0, 1 will be set * to constants for later comparison speed. */ private void normalize(boolean isGCDComputationRequired) { // note: don't call anything that depends on a normalized this. // i.e.: don't call most (or all) of the Rational methods. if (numerator == null || denominator == null) { throw new NumberFormatException("null"); } // [these are typically cheap.] int numeratorSignum = numerator.signum(); int denominatorSignum = denominator.signum(); // note: we don't throw on denominatorSignum==0. that'll be done // elsewhere. // if (denominatorSignum == 0) { // throw new NumberFormatException("quotient zero"); // } if (numeratorSignum == 0 && denominatorSignum == 0) { // [typically not reached, due to earlier tests.] // [both for speed] numerator = BIG_INTEGER_ZERO; denominator = BIG_INTEGER_ZERO; return; } if (numeratorSignum == 0) { denominator = BIG_INTEGER_ONE; // [for speed] numerator = BIG_INTEGER_ZERO; return; } if (denominatorSignum == 0) { // [typically not reached, due to earlier tests.] numerator = BIG_INTEGER_ONE; // [for speed] denominator = BIG_INTEGER_ZERO; return; } // optimization // check the frequent case of denominator==1, for speed. // note: this only covers the normalized-for-speed 1-case. if (denominator == BIG_INTEGER_ONE) { // [for [later] speed] numerator = bigIntegerValueOf(numerator); return; } // optimization // check the symmetric case too, for speed. // note: this only covers the normalized-for-speed 1-case. if ((numerator == BIG_INTEGER_ONE || numerator == BIG_INTEGER_MINUS_ONE) && denominatorSignum > 0) { // [for [later] speed] denominator = bigIntegerValueOf(denominator); return; } // setup torn apart for speed BigIntegerNumber numeratorApart = numerator; BigIntegerNumber denominatorApart = denominator; if (denominatorSignum < 0) { numerator = numerator.negate(); denominator = denominator.negate(); numeratorSignum = -numeratorSignum; denominatorSignum = -denominatorSignum; denominatorApart = denominator; if (numeratorSignum > 0) { numeratorApart = numerator; } } else { if (numeratorSignum < 0) { numeratorApart = numerator.negate(); } } if (isGCDComputationRequired) { final BigIntegerNumber gcd = numeratorApart.gcd(denominatorApart); // test: optimization (body: not) if (!bigIntegerIsOne(gcd)) { numerator = numerator.divide(gcd); denominator = denominator.divide(gcd); } } // for [later] speed, and normalization generally numerator = bigIntegerValueOf(numerator); denominator = bigIntegerValueOf(denominator); } /** * Normalize Rational. [Convenience method to normalize(void).] */ private void normalizeFrom(BigIntegerNumber numerator, BigIntegerNumber denominator) { normalizeFrom(numerator, denominator, true); } private void normalizeFrom(BigIntegerNumber numerator, BigIntegerNumber denominator, boolean isGCDComputationRequired) { this.numerator = numerator; this.denominator = denominator; normalize(isGCDComputationRequired); } /** * Normalize Rational. [Convenience method to normalize(void).] */ private void normalizeFrom(Rational that) { if (that == null) { throw new NumberFormatException("null"); } normalizeFrom(that.numerator, that.denominator); } /** * Check constraints on radixes. Radix may not be negative or less than two. */ private static void checkRadix(int radix) { if (radix < 0) { throw new NumberFormatException("radix negative"); } if (radix < 2) { throw new NumberFormatException("radix too small"); } // note: we don't check for "radix too large"; // that's left to BigInteger.toString(radix) // [i.e.: we don't really mind whether the underlying // system supports base36, or base62, or even more] } /** * Check some of the integer format constraints. */ private static void checkNumberFormat(String strNumber) { // "x", "-x", "+x", "", "-", "+" if (strNumber == null) { throw new NumberFormatException("null"); } // note: 'embedded sign' catches both-signs cases too. final int p = strNumber.indexOf('+'); final int m = strNumber.indexOf('-'); final int pp = (p == -1 ? -1 : strNumber.indexOf('+', p + 1)); final int mm = (m == -1 ? -1 : strNumber.indexOf('-', m + 1)); if ((p != -1 && p != 0) || (m != -1 && m != 0) || pp != -1 || mm != -1) { // embedded sign. this covers the both-signs case. throw new NumberFormatException("embedded sign"); } } /** * Check number format for fraction part. */ private static void checkFractionFormat(String strFraction) { if (strFraction == null) { throw new NumberFormatException("null"); } if (strFraction.indexOf('+') != -1 || strFraction.indexOf('-') != -1) { throw new NumberFormatException("sign in fraction"); } } /** * Check number input for Java's string representations of doubles/floats * that are unsupported: "NaN" and "Infinity" (with or without sign). */ private static void checkNaNAndInfinity(String strNumber, int radix) { // the strings may actually be valid given a large enough radix // (e.g. base 36), so limit the radix/check if (radix > 16) { return; } // [null and empty string check] final int length = (strNumber == null ? 0 : strNumber.length()); if (length < 1) { return; } // optimization (String.equals and even more String.equalsIgnoreCase // are quite expensive, charAt and switch aren't) // test for last character in strings below, both cases switch (strNumber.charAt(length - 1)) { case 'N': case 'n': case 'y': case 'Y': break; default: return; } if (strNumber.equalsIgnoreCase("NaN") || strNumber.equalsIgnoreCase("Infinity") || strNumber.equalsIgnoreCase("+Infinity") || strNumber.equalsIgnoreCase("-Infinity")) { throw new NumberFormatException(strNumber); } } /** * Check constraints on radixes. [Convenience method to checkRadix(radix).] */ private static void checkRadixArgument(int radix) { try { checkRadix(radix); } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } /** * Proxy to BigInteger.valueOf(). Speeds up comparisons by using constants. */ private static BigIntegerNumber bigIntegerValueOf(long number) { // return the internal constants used for checks if possible. // optimization // check whether it's outside int range. // actually check a much narrower range, fitting the switch below. if (number >= -16 && number <= 16) { // note: test above needed to make the cast below safe // jump table, for speed switch ((int) number) { case 0: return BIG_INTEGER_ZERO; case 1: return BIG_INTEGER_ONE; case -1: return BIG_INTEGER_MINUS_ONE; case 2: return BIG_INTEGER_TWO; case -2: return BIG_INTEGER_MINUS_TWO; case 10: return BIG_INTEGER_TEN; case 16: return BIG_INTEGER_SIXTEEN; } } return BigIntegerNumberFactory.valueOf(number); } /** * Convert BigInteger to its constant if possible. Speeds up later * comparisons by using constants. */ private static BigIntegerNumber bigIntegerValueOf(BigIntegerNumber number) { // note: these tests are quite expensive, // so they should be minimized to a reasonable amount. // priority in the tests: 1, 0, -1; // two phase testing. // cheap tests first. // optimization if (number == BIG_INTEGER_ONE) { return number; } // optimization if (number == BIG_INTEGER_ZERO) { // [typically not reached, since zero is handled specially.] return number; } // optimization if (number == BIG_INTEGER_MINUS_ONE) { return number; } // more expensive tests later. // optimization if (number.equals(BIG_INTEGER_ONE)) { return BIG_INTEGER_ONE; } // optimization if (number.equals(BIG_INTEGER_ZERO)) { // [typically not reached from normalize().] return BIG_INTEGER_ZERO; } // optimization if (number.equals(BIG_INTEGER_MINUS_ONE)) { return BIG_INTEGER_MINUS_ONE; } // note: BIG_INTEGER_TWO et al. _not_ used for checks // and therefore not replaced by constants_here_. // this speeds up tests. // not a known constant return number; } /** * Proxy to (new BigInteger()). Speeds up comparisons by using constants. */ private static BigIntegerNumber bigIntegerValueOf(String strNumber, int radix) { // note: mind the radix. // however, 0/1/-1 are not a problem. // _often_ used strings (e.g. 0 for empty fraction and // 1 for empty denominator), for speed. // optimization if (strNumber.equals("1")) { return BIG_INTEGER_ONE; } // optimization if (strNumber.equals("0")) { return BIG_INTEGER_ZERO; } // optimization if (strNumber.equals("-1")) { // typically not reached, due to [private] usage pattern, // i.e. the sign is cut before return BIG_INTEGER_MINUS_ONE; } // note: BIG_INTEGER_TWO et al. _not_ used for checks // and therefore even less valuable. // there's a tradeoff between speeds of these tests // and being consistent in using all constants // (at least with the common radixes). // optimization if (radix > 2) { if (strNumber.equals("2")) { return BIG_INTEGER_TWO; } if (strNumber.equals("-2")) { // typically not reached, due to [private] usage pattern, // i.e. the sign is cut before return BIG_INTEGER_MINUS_TWO; } } // optimization if (strNumber.equals("10")) { switch (radix) { case 2: return BIG_INTEGER_TWO; case 10: return BIG_INTEGER_TEN; case 16: return BIG_INTEGER_SIXTEEN; } } // optimization if (radix == 10 && strNumber.equals("16")) { return BIG_INTEGER_SIXTEEN; } // note: not directly finding the other [radix'] representations // of 10 and 16 in the code above // use the constants if possible return bigIntegerValueOf(BigIntegerNumberFactory.valueOf(strNumber, radix)); } /** * Proxy to BigInteger.equals(). For speed. */ private static boolean bigIntegerEquals(BigIntegerNumber n, BigIntegerNumber m) { // optimization first test is for speed. if (n == m) { return true; } return n.equals(m); } /** * Zero (0) value predicate. [For convenience and speed.] */ private static boolean bigIntegerIsZero(BigIntegerNumber n) { // optimization first test is for speed. if (n == BIG_INTEGER_ZERO) { return true; } // well, this is also optimized for speed a bit. return (n.signum() == 0); } /** * One (1) value predicate. [For convenience and speed.] */ private static boolean bigIntegerIsOne(BigIntegerNumber n) { // optimization first test is for speed. if (n == BIG_INTEGER_ONE) { return true; } return bigIntegerEquals(n, BIG_INTEGER_ONE); } /** * Minus-one (-1) value predicate. [For convenience and speed.] */ private static boolean bigIntegerIsMinusOne(BigIntegerNumber n) { // optimization // first test is for speed. if (n == BIG_INTEGER_MINUS_ONE) { return true; } return bigIntegerEquals(n, BIG_INTEGER_MINUS_ONE); } /** * Negative value predicate. */ private static boolean bigIntegerIsNegative(BigIntegerNumber n) { return (n.signum() < 0); } /** * Proxy to BigInteger.multiply(). * * For speed. The more common cases of integers (denominator == 1) are * optimized. */ private static BigIntegerNumber bigIntegerMultiply(BigIntegerNumber n, BigIntegerNumber m) { // optimization: one or both operands are zero. if (bigIntegerIsZero(n) || bigIntegerIsZero(m)) { return BIG_INTEGER_ZERO; } // optimization: second operand is one (i.e. neutral element). if (bigIntegerIsOne(m)) { return n; } // optimization: first operand is one (i.e. neutral element). if (bigIntegerIsOne(n)) { return m; } // optimization if (bigIntegerIsMinusOne(m)) { // optimization if (bigIntegerIsMinusOne(n)) { // typically not reached due to earlier test(s) return BIG_INTEGER_ONE; } return n.negate(); } // optimization if (bigIntegerIsMinusOne(n)) { // [m is not -1, see test above] return m.negate(); } // default case. [this would handle all cases.] return n.multiply(m); } /** * Proxy to BigInteger.pow(). For speed. */ private static BigIntegerNumber bigIntegerPower(BigIntegerNumber n, int exponent) { // generally expecting exponent>=0 // (there's nor much use in inverting in the integer domain) // the checks for exponent<0 below are done all the same // optimization jump table, for speed. switch (exponent) { case 0: if (bigIntegerIsZero(n)) { // typically not reached, due to earlier test / [private] usage // pattern throw new ArithmeticException("zero exp zero"); } return BIG_INTEGER_ONE; case 1: return n; } // optimization if (bigIntegerIsZero(n) && exponent > 0) { // note: exponent==0 already handled above // typically not reached, due to earlier test return BIG_INTEGER_ZERO; } // optimization if (bigIntegerIsOne(n)) { return BIG_INTEGER_ONE; } // optimization if (bigIntegerIsMinusOne(n)) { return (exponent % 2 == 0 ? BIG_INTEGER_ONE : BIG_INTEGER_MINUS_ONE); } return n.pow(exponent); } /** * Binary logarithm rounded towards floor (towards negative infinity). */ // @PrecisionLoss private static int bigIntegerLogarithm2(BigIntegerNumber n) { if (bigIntegerIsZero(n)) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException("logarithm of zero"); } if (bigIntegerIsNegative(n)) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException("logarithm of negative number"); } // take this as a start // (don't wholly rely on bitLength() having the same meaning as log2) int exponent = n.bitLength() - 1; if (exponent < 0) { exponent = 0; } BigIntegerNumber p = BIG_INTEGER_TWO.pow(exponent + 1); while (n.compareTo(p) >= 0) { // typically not reached p = p.multiply(BIG_INTEGER_TWO); exponent++; } p = p.divide(BIG_INTEGER_TWO); while (n.compareTo(p) < 0) { // typically not reached p = p.divide(BIG_INTEGER_TWO); exponent--; } // [possible loss of precision step] return exponent; } /** * Proxy to BigInteger.toString(int radix). */ private static String stringValueOf(BigIntegerNumber n, int radix) { return n.toString(radix); } /** * Proxy to stringValueOf(bigIntegerValueOf(long), radix); take the same * route to format [long/bigint] integer numbers [despite the overhead]. */ private static String stringValueOf(long n, int radix) { return stringValueOf(bigIntegerValueOf(n), radix); } /** * Convert a IEEE 754 floating point number (of different sizes, as array of * longs, big endian) to a Rational. */ private static Rational fromIEEE754(long[] value0, int fractionSize, int exponentSize) { if (value0 == null) { throw new NumberFormatException("null"); } // note: the long(s) in the input array are considered unsigned, // so expansion operations (to e.g. Rational) and [right-] shift // operations // (unlike assignment, equality-test, narrowing, and/or operations) // must be appropriately chosen Rational fraction0 = ZERO; // start at the little end of the [bigendian] input int i = value0.length - 1; while (fractionSize >= 64) { if (i < 0) { throw new NumberFormatException("not enough bits"); } // mind the long (value0[i]) being unsigned fraction0 = fraction0.add(valueOfUnsigned(value0[i])).divide( TWO_POWER_64); fractionSize -= 64; i--; } // the rest must now fit into value0[0] (a long), // i.e. we don't support exponentSize > 63 at the moment; // as the power() method accepts ints (not longs), // the restriction is actually even on <= 31 bits if (i < 0) { throw new NumberFormatException("no bits"); } if (i > 0) { throw new NumberFormatException("excess bits"); } long value = value0[0]; // [fractionSize [now is] < 64 by loop above] final long fractionMask = ((long) 1 << fractionSize) - 1; final long rawFraction = value & fractionMask; value >>>= fractionSize; // [exponentSize < 32 by [private] usage pattern; rawExponent < 2**31] final int exponentMask = (1 << exponentSize) - 1; final int exponentBias = (1 << (exponentSize - 1)) - 1; final int rawExponent = (int) value & exponentMask; value >>>= exponentSize; final int signSize = 1; final int signMask = (1 << signSize) - 1; // 1 final int rawSign = (int) value & signMask; value >>>= signSize; if (value != 0) { throw new NumberFormatException("excess bits"); } // check for Infinity and NaN (IEEE 754 rawExponent at its maximum) if (rawExponent == exponentMask) { // (no fraction bits means one of the Infinities; else NaN) throw new NumberFormatException(rawFraction == 0 && fraction0.isZero() ? (rawSign == 0 ? "Infinity" : "-Infinity") : "NaN"); } // optimization -- avoids power() calculation below // (isZero and zero multiply) are cheap // check for zero (IEEE 754 rawExponent zero and no fraction bits) if (rawExponent == 0 && rawFraction == 0 && fraction0.isZero()) { return ZERO; } // handle subnormal numbers too (with rawExponent==0) // [fractionSize [still is] < 64] final long mantissa1 = rawFraction | (rawExponent == 0 ? (long) 0 : (long) 1 << fractionSize); // mind mantissa1 being unsigned final Rational mantissa = fraction0.add(valueOfUnsigned(mantissa1)); // (subnormal numbers; exponent is one off) // [rawExponent < 2**31; exponentBias < 2**30] final int exponent = rawExponent - exponentBias + (rawExponent == 0 ? 1 : 0) - fractionSize; final int sign = (rawSign == 0 ? 1 : -1); return valueOf(2).pow(exponent).multiply(mantissa).multiply(sign); } /** * Convert a Rational to a IEEE 754 floating point number (of different * sizes, as array of longs, big endian). * <p> * Possible loss of precision. */ // @PrecisionLoss private static long[] toIEEE754(Rational value, int fractionSize, int exponentSize) { if (value == null) { throw new NumberFormatException("null"); } // [needed size: fractionSize+exponentSize+1; round up bits to a // multiple of 64] final long[] out0 = new long[(fractionSize + exponentSize + 1 + (64 - 1)) / 64]; if (value.isZero()) { // 0.0 // note: as we don't keep a sign with our ZERO, // we never return IEEE 754 -0.0 here for (int j = 0; j < out0.length; j++) { out0[j] = 0; } return out0; } final boolean negate = value.isNegative(); if (negate) { value = value.negate(); } // need to scale to this to get the full mantissa int exponent = fractionSize; final Rational lower = valueOf(2).pow(fractionSize); final Rational upper = lower.multiply(2); // optimization, and a good guess (but not exact in all cases) final int scale = lower.divide(value).logarithm2(); value = value.multiply(valueOf(2).pow(scale)); exponent -= scale; while (value.compareTo(lower) < 0) { // [typically done zero or one time] value = value.multiply(2); exponent--; } while (value.compareTo(upper) >= 0) { // [typically not reached] value = value.divide(2); exponent++; } // [rounding step, possible loss of precision step] BigIntegerNumber mantissa = value.bigIntegerValue(); // adjust after [unfortunate] mantissa rounding if (upper.compareTo(mantissa) <= 0) { mantissa = mantissa.divide(BIG_INTEGER_TWO); exponent++; } // start [to fill] at the little end of the [bigendian] output int i = out0.length - 1; int fractionSize1 = fractionSize; while (fractionSize1 >= 64) { final BigIntegerNumber[] divrem = mantissa .divideAndRemainder(BIG_INTEGER_TWO_POWER_64); // [according to BigInteger javadoc] this takes the least // significant 64 bits; // i.e. in this case the long is considered unsigned, as we want it out0[i] = divrem[1].longValue(); fractionSize1 -= 64; mantissa = divrem[0]; i--; } // the rest must now fit into out0[0] if (i < 0) { // not reached throw new NumberFormatException("too many bits"); } if (i > 0) { // not reached throw new NumberFormatException("not enough bits"); } long fraction = mantissa.longValue(); final int exponentBias = (1 << (exponentSize - 1)) - 1; exponent += exponentBias; final int maximalExponent = (1 << exponentSize) - 1; if (exponent >= maximalExponent) { // overflow // throw new NumberFormatException("overflow"); // [positive or negative] infinity exponent = maximalExponent; fraction = 0; for (int j = 1; j < out0.length; j++) { out0[j] = 0; } // [keep sign] } else if (exponent <= 0) { // handle subnormal numbers too // [with know loss of precision] // drop one bit, while keeping the exponent int s = 1; // [need not shift more than fractionSize] final int n = (-exponent > fractionSize ? fractionSize : -exponent); s += n; exponent += n; // [possible loss of precision step] fraction = shiftrx(fraction, out0, 1, s); boolean zero = (fraction == 0); for (int j = 1; zero && j < out0.length; j++) { zero = (out0[j] == 0); } if (zero) { // underflow // throw new NumberFormatException("underflow"); // 0.0 or -0.0; i.e.: keep sign exponent = 0; // [nonzero == 0 implies the rest of the fraction is zero as // well] } } // cut implied most significant bit // [unless with subnormal numbers] if (exponent != 0) { fraction &= ~((long) 1 << fractionSize1); } long out = 0; out |= (negate ? 1 : 0); out <<= exponentSize; out |= exponent; out <<= fractionSize1; out |= fraction; out0[0] = out; return out0; } /** * Shift right, while propagating shifted bits (long[] is bigendian). */ private static long shiftrx(long a, long[] b, int boff, int n) { while (n > 0) { final int n2 = (n < 63 ? n : 63); final long m = ((long) 1 << n2) - 1; long c = a & m; a >>>= n2; for (int i = boff; i < b.length; i++) { final long t = b[i] & m; b[i] >>>= n2; b[i] |= (c << (64 - n2)); c = t; } n -= n2; } return a; } /** * Fixed dot-format "[-]i.f" string representation, with a precision. * <p> * Precision may be negative, in which case the rounding affects digits left * of the dot, i.e. the integer part of the number, as well. * <p> * The exponentFormat parameter allows for shorter [intermediate] string * representation, an optimization, e.g. used with toStringExponent. * <p> * Possible loss of precision. */ // @PrecisionLoss private String toStringDot(int precision, int radix, boolean exponentFormat) { checkRadixArgument(radix); Rational scaleValue = new Rational(bigIntegerPower( bigIntegerValueOf(radix), (precision < 0 ? -precision : precision))); if (precision < 0) { scaleValue = scaleValue.invert(); } // default round mode. // [rounding step, possible loss of precision step] Rational n = multiply(scaleValue).round(_toStringDotRoundingMode); final boolean negt = n.isNegative(); if (negt) { n = n.negate(); } String s = n.toString(radix); if (exponentFormat) { // note that this is _not_ the scientific notation // (one digit left of the dot exactly), // but some intermediate representation suited for post processing // [leaving away the left/right padding steps // is more performant in time and memory space] s = s + "E" + stringValueOf(-precision, radix); } else { if (precision >= 0) { // left-pad with '0' while (s.length() <= precision) { s = "0" + s; } final int dot = s.length() - precision; final String i = s.substring(0, dot); final String f = s.substring(dot); s = i; if (f.length() > 0) { s = s + "." + f; } } else { if (!s.equals("0")) { // right-pad with '0' for (int i = -precision; i > 0; i--) { s = s + "0"; } } } } // add sign if (negt) { s = "-" + s; } return s; } /** * Transform a [intermediate] dot representation to an exponent-format * representation. */ private static String toExponentRepresentation(String s, int radix) { // skip '+' if (s.length() > 0 && s.charAt(0) == '+') { // typically not reached, due to [private] usage pattern s = s.substring(1); } // handle '-' boolean negt = false; if (s.length() > 0 && s.charAt(0) == '-') { negt = true; s = s.substring(1); } // skip initial zeros while (s.length() > 0 && s.charAt(0) == '0') { s = s.substring(1); } // check for and handle exponent // handle only upper case 'E' (we know we use that in earlier steps); // this allows any base using lower case characters int exponent0 = 0; final int exp = s.indexOf('E'); if (exp != -1) { final String se = s.substring(exp + 1); s = s.substring(0, exp); exponent0 = (new Rational(se, radix)).intValueExact(); } String si, sf; int exponent; final int dot = s.indexOf('.'); if (dot != -1) { if (dot == 0) { // possibly more insignificant digits s = s.substring(1); exponent = -1; while (s.length() > 0 && s.charAt(0) == '0') { s = s.substring(1); exponent--; } if (s.equals("")) { // typically not reached, due to [private] usage pattern return "0"; } // first significant digit si = s.substring(0, 1); sf = s.substring(1); } else { // initial [significant] digit si = s.substring(0, 1); sf = s.substring(1, dot); exponent = sf.length(); sf = sf + s.substring(dot + 1); } } else { // [note that we just cut the zeros above] if (s.equals("")) { return "0"; } // initial [significant] digit si = s.substring(0, 1); // rest sf = s.substring(1); exponent = sf.length(); } exponent += exponent0; // drop trailing zeros while (sf.length() > 0 && sf.charAt(sf.length() - 1) == '0') { sf = sf.substring(0, sf.length() - 1); } s = si; if (!sf.equals("")) { s = s + "." + sf; } if (exponent != 0) { s = s + "E" + stringValueOf(exponent, radix); } if (negt) { s = "-" + s; } return s; } /** * Return binary logarithm rounded towards floor (towards negative * infinity). * <p> * Possible loss of precision. */ // @PrecisionLoss private int logarithm2() { if (isZero()) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException("logarithm of zero"); } if (isNegative()) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException("logarithm of negative number"); } final boolean inverted = (compareTo(ONE) < 0); final Rational a = (inverted ? invert() : this); // [possible loss of precision step] final int log = bigIntegerLogarithm2(a.bigIntegerValue()); return (inverted ? -(log + 1) : log); } /** * Return logarithm rounded towards floor (towards negative infinity). * <p> * Possible loss of precision. */ // @PrecisionLoss private int logarithm(int base) { // optimization if (base == 2) { return logarithm2(); } if (isZero()) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException("logarithm of zero"); } if (isNegative()) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException("logarithm of negative number"); } // if (base < 2) { // // [typically not reached, due to [private] usage pattern] // throw new ArithmeticException("bad base"); // } if (base < 0) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException("negative base"); } if (base < 2) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException("base too small"); } final boolean inverted = (compareTo(ONE) < 0); Rational a = (inverted ? invert() : this); final Rational bbase = valueOf(base); // optimization -- we could start from n=0 // initial guess // [base 2 handled earlier] // [unusual bases are handled a bit less performant] final Rational lbase = (base == 10 ? LOGARITHM_TEN_GUESS : base == 16 ? LOGARITHM_SIXTEEN : valueOf(ilog2(base))); int n = valueOf(a.logarithm2()).divide(lbase).intValue(); a = a.divide(bbase.pow(n)); // note that these steps are needed anyway: // LOGARITHM_TEN_GUESS above e.g. is (as the name suggests) // a guess only (since most logarithms usually can't be expressed // as rationals generally); odd bases or off even worse while (a.compareTo(bbase) >= 0) { a = a.divide(bbase); n++; } while (a.compareTo(ONE) < 0) { a = a.multiply(bbase); n--; } // [possible loss of precision step] return (inverted ? -(n + 1) : n); } /** * Return binary logarithm of an int. */ private static int ilog2(int n) { if (n == 0) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException("logarithm of zero"); } if (n < 0) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException("logarithm of negative number"); } int i = 0; // as this method is used in the context of [small] bases/radixes, // we expect less than 8 iterations at most, so no need to optimize while (n > 1) { n /= 2; i++; } return i; } /** * Remainder or modulus of non-negative values. Helper function to * remainder() and modulus(). */ private Rational remainderOrModulusOfPositive(Rational that) { final int thisSignum = signum(); final int thatSignum = that.signum(); if (thisSignum < 0 || thatSignum < 0) { // typically not reached, due to [private] usage pattern throw new IllegalArgumentException("negative values(s)"); } if (thatSignum == 0) { // typically not reached, due to [private] usage pattern throw new ArithmeticException("division by zero"); } // optimization if (thisSignum == 0) { return ZERO; } return new Rational(bigIntegerMultiply(numerator, that.denominator) .remainder(bigIntegerMultiply(denominator, that.numerator)), bigIntegerMultiply(denominator, that.denominator)); } /** * Round to BigInteger helper function. Internally used. * <p> * Possible loss of precision. */ // @PrecisionLoss private BigIntegerNumber roundToBigInteger(int roundMode) { // note: remainder and its duplicate are calculated for all cases. BigIntegerNumber numerator = this.numerator; final BigIntegerNumber denominator = this.denominator; final int signum = numerator.signum(); // optimization if (signum == 0) { // [typically not reached due to earlier test for integerp] return BIG_INTEGER_ZERO; } // keep info on the sign final boolean isPositive = (signum > 0); // operate on positive values if (!isPositive) { numerator = numerator.negate(); } final BigIntegerNumber[] divrem = numerator.divideAndRemainder(denominator); BigIntegerNumber dv = divrem[0]; final BigIntegerNumber r = divrem[1]; // return if we don't need to round, independent of rounding mode if (bigIntegerIsZero(r)) { // [typically not reached since remainder is not zero // with normalized that are not integerp] if (!isPositive) { dv = dv.negate(); } return dv; } boolean up = false; final int comp = r.multiply(BIG_INTEGER_TWO).compareTo(denominator); switch (roundMode) { // Rounding mode to round away from zero. case ROUND_UP: up = true; break; // Rounding mode to round towards zero. case ROUND_DOWN: up = false; break; // Rounding mode to round towards positive infinity. case ROUND_CEILING: up = isPositive; break; // Rounding mode to round towards negative infinity. case ROUND_FLOOR: up = !isPositive; break; // Rounding mode to round towards "nearest neighbor" unless both // neighbors are equidistant, in which case round up. case ROUND_HALF_UP: up = (comp >= 0); break; // Rounding mode to round towards "nearest neighbor" unless both // neighbors are equidistant, in which case round down. case ROUND_HALF_DOWN: up = (comp > 0); break; case ROUND_HALF_CEILING: up = (comp != 0 ? comp > 0 : isPositive); break; case ROUND_HALF_FLOOR: up = (comp != 0 ? comp > 0 : !isPositive); break; // Rounding mode to round towards the "nearest neighbor" unless both // neighbors are equidistant, in which case, round towards the even // neighbor. case ROUND_HALF_EVEN: up = (comp != 0 ? comp > 0 : !bigIntegerIsZero(dv .remainder(BIG_INTEGER_TWO))); break; case ROUND_HALF_ODD: up = (comp != 0 ? comp > 0 : bigIntegerIsZero(dv .remainder(BIG_INTEGER_TWO))); break; // Rounding mode to assert that the requested operation has an exact // result, hence no rounding is necessary. If this rounding mode is // specified on an operation that yields an inexact result, an // ArithmeticException is thrown. case ROUND_UNNECESSARY: if (!bigIntegerIsZero(r)) { throw new ArithmeticException("rounding necessary"); } // [typically not reached due to earlier test for integerp] up = false; break; default: throw new IllegalArgumentException("unsupported rounding mode"); } if (up) { dv = dv.add(BIG_INTEGER_ONE); } if (!isPositive) { dv = dv.negate(); } // [rounding step, possible loss of precision step] return dv; } } class BigIntegerNumberFactory { static boolean APPROXIMATION_ENABLED; static MathContext APPROXIMATION_MATH_CONTEXT; public static void resetApproximationConfiguration(boolean enabled, int precision, RoundingMode roundingMode) { APPROXIMATION_ENABLED = enabled; APPROXIMATION_MATH_CONTEXT = new MathContext(precision, roundingMode); } public static BigIntegerNumber valueOf(long l) { BigIntegerNumber result; if (APPROXIMATION_ENABLED) { result = new BigIntegerNumberApproximate(l, APPROXIMATION_MATH_CONTEXT); } else { result = new BigIntegerNumberExact(l); } return result; } public static BigIntegerNumber valueOf(String strNumber, int radix) { BigIntegerNumber result; if (APPROXIMATION_ENABLED) { result = new BigIntegerNumberApproximate(strNumber, radix, APPROXIMATION_MATH_CONTEXT); } else { result = new BigIntegerNumberExact(strNumber, radix); } return result; } public static Rational rationalValueOf(BigDecimal bigDecimal) { Rational result; if (APPROXIMATION_ENABLED) { result = new Rational(new BigIntegerNumberApproximate(bigDecimal, APPROXIMATION_MATH_CONTEXT)); } else { if (bigDecimal.scale() <= 0) { result = new Rational(new BigIntegerNumberExact(bigDecimal.toBigIntegerExact())); } else { BigIntegerNumberExact numerator = new BigIntegerNumberExact(bigDecimal.scaleByPowerOfTen(bigDecimal.scale()).toBigIntegerExact()); BigIntegerNumberExact denominator = new BigIntegerNumberExact(BigDecimal.ONE.scaleByPowerOfTen(bigDecimal.scale()).toBigIntegerExact()); result = new Rational(numerator, denominator); } } return result; } }