package org.nevec.rjm; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; import java.math.RoundingMode; import it.cavallium.warppi.util.Error; import it.cavallium.warppi.util.Errors; /** * Fractions (rational numbers). They are divisions of two BigInteger numbers, * reduced to coprime numerator and denominator. * * @since 2006-06-25 * @author Richard J. Mathar */ public class Rational implements Cloneable, Comparable { /** * numerator */ BigInteger a; /** * denominator, always larger than zero. */ BigInteger b; /** * The maximum and minimum value of a standard Java integer, 2^31. * * @since 2009-05-18 */ static public BigInteger MAX_INT = new BigInteger("2147483647"); static public BigInteger MIN_INT = new BigInteger("-2147483648"); /** * The constant 1. */ public static Rational ONE = new Rational(1, 1); /** * The constant 0. */ static public Rational ZERO = new Rational(); /** * The constant 1/2 * * @since 2010-05-25 */ static public Rational HALF = new Rational(1, 2); /** * Default ctor, which represents the zero. * * @since 2007-11-17 */ public Rational() { a = BigInteger.ZERO; b = BigInteger.ONE; } /** * ctor from a numerator and denominator. * * @param a * the numerator. * @param b * the denominator. */ public Rational(final BigInteger a, final BigInteger b) { this.a = a; this.b = b; normalize(); } /** * ctor from a numerator. * * @param a * the BigInteger. */ public Rational(final BigInteger a) { this.a = a; b = new BigInteger("1"); } /** * ctor from a numerator and denominator. * * @param a * the numerator. * @param b * the denominator. */ public Rational(final int a, final int b) { this(new BigInteger("" + a), new BigInteger("" + b)); } /** * ctor from an integer. * * @param n * the integer to be represented by the new instance. * @since 2010-07-18 */ public Rational(final int n) { this(n, 1); } /** * ctor from a string representation. * * @param str * the string. This either has a slash in it, separating two * integers, or, if there is no slash, is representing the * numerator with implicit denominator equal to 1. Warning: this * does not yet test for a denominator equal to zero */ public Rational(final String str) { this(str, 10); } /** * ctor from a string representation in a specified base. * * @param str * the string. This either has a slash in it, separating two * integers, or, if there is no slash, is just representing the * numerator. * @param radix * the number base for numerator and denominator Warning: this * does not yet test for a denominator equal to zero */ public Rational(final String str, final int radix) { final int hasslah = str.indexOf("/"); if (hasslah == -1) { a = new BigInteger(str, radix); b = new BigInteger("1", radix); /* no normalization necessary here */ } else { /* * create numerator and denominator separately */ a = new BigInteger(str.substring(0, hasslah), radix); b = new BigInteger(str.substring(hasslah + 1), radix); normalize(); } } /** * Create a copy. * * @since 2008-11-07 */ @Override public Rational clone() { /* * protected access means this does not work return new * Rational(a.clone(), b.clone()) ; */ final BigInteger aclon = new BigInteger("" + a); final BigInteger bclon = new BigInteger("" + b); return new Rational(aclon, bclon); } /* Rational.clone */ /** * Multiply by another fraction. * * @param val * a second rational number. * @return the product of this with the val. */ public Rational multiply(final Rational val) { final BigInteger num = a.multiply(val.a); final BigInteger deno = b.multiply(val.b); /* * Normalization to an coprime format will be done inside the ctor() and * is not duplicated here. */ return new Rational(num, deno); } /* Rational.multiply */ /** * Multiply by a BigInteger. * * @param val * a second number. * @return the product of this with the value. */ public Rational multiply(final BigInteger val) { final Rational val2 = new Rational(val, BigInteger.ONE); return multiply(val2); } /* Rational.multiply */ /** * Multiply by an integer. * * @param val * a second number. * @return the product of this with the value. */ public Rational multiply(final int val) { final BigInteger tmp = new BigInteger("" + val); return multiply(tmp); } /* Rational.multiply */ /** * Power to an integer. * * @param exponent * the exponent. * @return this value raised to the power given by the exponent. If the * exponent is 0, the value 1 is returned. */ public Rational pow(final int exponent) { if (exponent == 0) { return new Rational(1, 1); } final BigInteger num = a.pow(Math.abs(exponent)); final BigInteger deno = b.pow(Math.abs(exponent)); if (exponent > 0) { return new Rational(num, deno); } else { return new Rational(deno, num); } } /* Rational.pow */ /** * Power to an integer. * * @param exponent * the exponent. * @return this value raised to the power given by the exponent. If the * exponent is 0, the value 1 is returned. * @throws Error * @since 2009-05-18 */ public Rational pow(final BigInteger exponent) throws Error { /* test for overflow */ if (exponent.compareTo(Rational.MAX_INT) == 1) { throw new Error(Errors.NUMBER_TOO_LARGE); } if (exponent.compareTo(Rational.MIN_INT) == -1) { throw new Error(Errors.NUMBER_TOO_SMALL); } /* promote to the simpler interface above */ return pow(exponent.intValue()); } /* Rational.pow */ /** * r-th root. * * @param r * the inverse of the exponent. 2 for the square root, 3 for the * third root etc * @return this value raised to the inverse power given by the root * argument, this^(1/r). * @throws Error * @since 2009-05-18 */ public Rational root(final BigInteger r) throws Error { /* test for overflow */ if (r.compareTo(Rational.MAX_INT) == 1) { throw new Error(Errors.NUMBER_TOO_LARGE); } if (r.compareTo(Rational.MIN_INT) == -1) { throw new Error(Errors.NUMBER_TOO_SMALL); } final int rthroot = r.intValue(); /* cannot pull root of a negative value with even-valued root */ if (compareTo(Rational.ZERO) == -1 && rthroot % 2 == 0) { throw new Error(Errors.NEGATIVE_PARAMETER); } /* * extract a sign such that we calculate |n|^(1/r), still r carrying any * sign */ final boolean flipsign = compareTo(Rational.ZERO) == -1 && rthroot % 2 != 0 ? true : false; /* * delegate the main work to ifactor#root() */ final Ifactor num = new Ifactor(a.abs()); final Ifactor deno = new Ifactor(b); final Rational resul = num.root(rthroot).divide(deno.root(rthroot)); if (flipsign) { return resul.negate(); } else { return resul; } } /* Rational.root */ /** * Raise to a rational power. * * @param exponent * The exponent. * @return This value raised to the power given by the exponent. If the * exponent is 0, the value 1 is returned. * @throws Error * @since 2009-05-18 */ public Rational pow(final Rational exponent) throws Error { if (exponent.a.compareTo(BigInteger.ZERO) == 0) { return new Rational(1, 1); } /* * calculate (a/b)^(exponent.a/exponent.b) as * ((a/b)^exponent.a)^(1/exponent.b) = tmp^(1/exponent.b) */ final Rational tmp = pow(exponent.a); return tmp.root(exponent.b); } /* Rational.pow */ /** * Divide by another fraction. * * @param val * A second rational number. * @return The value of this/val * @throws Error */ public Rational divide(final Rational val) throws Error { if (val.compareTo(Rational.ZERO) == 0) { throw new Error(Errors.DIVISION_BY_ZERO); } final BigInteger num = a.multiply(val.b); final BigInteger deno = b.multiply(val.a); /* * Reduction to a coprime format is done inside the ctor, and not * repeated here. */ return new Rational(num, deno); } /* Rational.divide */ /** * Divide by an integer. * * @param val * a second number. * @return the value of this/val * @throws Error */ public Rational divide(final BigInteger val) throws Error { if (val.compareTo(BigInteger.ZERO) == 0) { throw new Error(Errors.DIVISION_BY_ZERO); } final Rational val2 = new Rational(val, BigInteger.ONE); return divide(val2); } /* Rational.divide */ /** * Divide by an integer. * * @param val * A second number. * @return The value of this/val * @throws Error */ public Rational divide(final int val) throws Error { if (val == 0) { throw new Error(Errors.DIVISION_BY_ZERO); } final Rational val2 = new Rational(val, 1); return divide(val2); } /* Rational.divide */ /** * Add another fraction. * * @param val * The number to be added * @return this+val. */ public Rational add(final Rational val) { final BigInteger num = a.multiply(val.b).add(b.multiply(val.a)); final BigInteger deno = b.multiply(val.b); return new Rational(num, deno); } /* Rational.add */ /** * Add another integer. * * @param val * The number to be added * @return this+val. */ public Rational add(final BigInteger val) { final Rational val2 = new Rational(val, BigInteger.ONE); return add(val2); } /* Rational.add */ /** * Add another integer. * * @param val * The number to be added * @return this+val. * @since May 26 2010 */ public Rational add(final int val) { final BigInteger val2 = a.add(b.multiply(new BigInteger("" + val))); return new Rational(val2, b); } /* Rational.add */ /** * Compute the negative. * * @return -this. */ public Rational negate() { return new Rational(a.negate(), b); } /* Rational.negate */ /** * Subtract another fraction. * * @param val * the number to be subtracted from this * @return this - val. */ public Rational subtract(final Rational val) { final Rational val2 = val.negate(); return add(val2); } /* Rational.subtract */ /** * Subtract an integer. * * @param val * the number to be subtracted from this * @return this - val. */ public Rational subtract(final BigInteger val) { final Rational val2 = new Rational(val, BigInteger.ONE); return subtract(val2); } /* Rational.subtract */ /** * Subtract an integer. * * @param val * the number to be subtracted from this * @return this - val. */ public Rational subtract(final int val) { final Rational val2 = new Rational(val, 1); return subtract(val2); } /* Rational.subtract */ /** * binomial (n choose m). * * @param n * the numerator. Equals the size of the set to choose from. * @param m * the denominator. Equals the number of elements to select. * @return the binomial coefficient. * @since 2006-06-27 * @author Richard J. Mathar * @throws Error */ public static Rational binomial(final Rational n, final BigInteger m) throws Error { if (m.compareTo(BigInteger.ZERO) == 0) { return Rational.ONE; } Rational bin = n; for (BigInteger i = new BigInteger("2"); i.compareTo(m) != 1; i = i.add(BigInteger.ONE)) { bin = bin.multiply(n.subtract(i.subtract(BigInteger.ONE))).divide(i); } return bin; } /* Rational.binomial */ /** * binomial (n choose m). * * @param n * the numerator. Equals the size of the set to choose from. * @param m * the denominator. Equals the number of elements to select. * @return the binomial coefficient. * @since 2009-05-19 * @author Richard J. Mathar * @throws Error */ public static Rational binomial(final Rational n, final int m) throws Error { if (m == 0) { return Rational.ONE; } Rational bin = n; for (int i = 2; i <= m; i++) { bin = bin.multiply(n.subtract(i - 1)).divide(i); } return bin; } /* Rational.binomial */ /** * Hankel's symbol (n,k) * * @param n * the first parameter. * @param k * the second parameter, greater or equal to 0. * @return Gamma(n+k+1/2)/k!/GAMMA(n-k+1/2) * @since 2010-07-18 * @author Richard J. Mathar * @throws Error */ public static Rational hankelSymb(final Rational n, final int k) throws Error { if (k == 0) { return Rational.ONE; } else if (k < 0) { throw new Error(Errors.NEGATIVE_PARAMETER); } Rational nkhalf = n.subtract(k).add(Rational.HALF); nkhalf = nkhalf.Pochhammer(2 * k); final Factorial f = new Factorial(); return nkhalf.divide(f.at(k)); } /* Rational.binomial */ /** * Get the numerator. * * @return The numerator of the reduced fraction. */ public BigInteger numer() { return a; } /** * Get the denominator. * * @return The denominator of the reduced fraction. */ public BigInteger denom() { return b; } /** * Absolute value. * * @return The absolute (non-negative) value of this. */ public Rational abs() { return new Rational(a.abs(), b.abs()); } /** * floor(): the nearest integer not greater than this. * * @return The integer rounded towards negative infinity. */ public BigInteger floor() { /* * is already integer: return the numerator */ if (b.compareTo(BigInteger.ONE) == 0) { return a; } else if (a.compareTo(BigInteger.ZERO) > 0) { return a.divide(b); } else { return a.divide(b).subtract(BigInteger.ONE); } } /* Rational.floor */ /** * ceil(): the nearest integer not smaller than this. * * @return The integer rounded towards positive infinity. * @since 2010-05-26 */ public BigInteger ceil() { /* * is already integer: return the numerator */ if (b.compareTo(BigInteger.ONE) == 0) { return a; } else if (a.compareTo(BigInteger.ZERO) > 0) { return a.divide(b).add(BigInteger.ONE); } else { return a.divide(b); } } /* Rational.ceil */ /** * Remove the fractional part. * * @return The integer rounded towards zero. */ public BigInteger trunc() { /* * is already integer: return the numerator */ if (b.compareTo(BigInteger.ONE) == 0) { return a; } else { return a.divide(b); } } /* Rational.trunc */ /** * Compares the value of this with another constant. * * @param val * the other constant to compare with * @return -1, 0 or 1 if this number is numerically less than, equal to, or * greater than val. */ @Override public int compareTo(final Rational val) { /* * Since we have always kept the denominators positive, simple * cross-multiplying works without changing the sign. */ final BigInteger left = a.multiply(val.b); final BigInteger right = val.a.multiply(b); return left.compareTo(right); } /* Rational.compareTo */ /** * Compares the value of this with another constant. * * @param val * the other constant to compare with * @return -1, 0 or 1 if this number is numerically less than, equal to, or * greater than val. */ public int compareTo(final BigInteger val) { final Rational val2 = new Rational(val, BigInteger.ONE); return compareTo(val2); } /* Rational.compareTo */ /** * Return a string in the format number/denom. If the denominator equals 1, * print just the numerator without a slash. * * @return the human-readable version in base 10 */ @Override public String toString() { if (b.compareTo(BigInteger.ONE) != 0) { return a.toString() + "/" + b.toString(); } else { return a.toString(); } } /* Rational.toString */ /** * Return a double value representation. * * @return The value with double precision. * @since 2008-10-26 */ public double doubleValue() { /* * To meet the risk of individual overflows of the exponents of a * separate invocation a.doubleValue() or b.doubleValue(), we divide * first in a BigDecimal environment and convert the result. */ final BigDecimal adivb = new BigDecimal(a).divide(new BigDecimal(b), MathContext.DECIMAL128); return adivb.doubleValue(); } /* Rational.doubleValue */ /** * Return a float value representation. * * @return The value with single precision. * @since 2009-08-06 */ public float floatValue() { final BigDecimal adivb = new BigDecimal(a).divide(new BigDecimal(b), MathContext.DECIMAL128); return adivb.floatValue(); } /* Rational.floatValue */ /** * Return a representation as BigDecimal. * * @param mc * the mathematical context which determines precision, rounding * mode etc * @return A representation as a BigDecimal floating point number. * @since 2008-10-26 */ public BigDecimal BigDecimalValue(final MathContext mc) { /* * numerator and denominator individually rephrased */ final BigDecimal n = new BigDecimal(a); final BigDecimal d = new BigDecimal(b); /* * the problem with n.divide(d,mc) is that the apparent precision might * be smaller than what is set by mc if the value has a precise * truncated representation. 1/4 will appear as 0.25, independent of mc */ return BigDecimalMath.scalePrec(n.divide(d, mc), mc); } /* Rational.BigDecimalValue */ /** * Return a string in floating point format. * * @param digits * The precision (number of digits) * @return The human-readable version in base 10. * @since 2008-10-25 */ public String toFString(final int digits) { if (b.compareTo(BigInteger.ONE) != 0) { final MathContext mc = new MathContext(digits, RoundingMode.DOWN); final BigDecimal f = new BigDecimal(a).divide(new BigDecimal(b), mc); return f.toString(); } else { return a.toString(); } } /* Rational.toFString */ /** * Compares the value of this with another constant. * * @param val * The other constant to compare with * @return The arithmetic maximum of this and val. * @since 2008-10-19 */ public Rational max(final Rational val) { if (compareTo(val) > 0) { return this; } else { return val; } } /* Rational.max */ /** * Compares the value of this with another constant. * * @param val * The other constant to compare with * @return The arithmetic minimum of this and val. * @since 2008-10-19 */ public Rational min(final Rational val) { if (compareTo(val) < 0) { return this; } else { return val; } } /* Rational.min */ /** * Compute Pochhammer's symbol (this)_n. * * @param n * The number of product terms in the evaluation. * @return Gamma(this+n)/Gamma(this) = this*(this+1)*...*(this+n-1). * @since 2008-10-25 */ public Rational Pochhammer(final BigInteger n) { if (n.compareTo(BigInteger.ZERO) < 0) { return null; } else if (n.compareTo(BigInteger.ZERO) == 0) { return Rational.ONE; } else { /* * initialize results with the current value */ Rational res = new Rational(a, b); BigInteger i = BigInteger.ONE; for (; i.compareTo(n) < 0; i = i.add(BigInteger.ONE)) { res = res.multiply(add(i)); } return res; } } /* Rational.pochhammer */ /** * Compute pochhammer's symbol (this)_n. * * @param n * The number of product terms in the evaluation. * @return Gamma(this+n)/GAMMA(this). * @since 2008-11-13 */ public Rational Pochhammer(final int n) { return Pochhammer(new BigInteger("" + n)); } /* Rational.pochhammer */ /** * True if the value is integer. Equivalent to the indication whether a * conversion to an integer can be exact. * * @since 2010-05-26 */ public boolean isBigInteger() { return b.abs().compareTo(BigInteger.ONE) == 0; } /* Rational.isBigInteger */ /** * True if the value is integer and in the range of the standard integer. * Equivalent to the indication whether a conversion to an integer can be * exact. * * @since 2010-05-26 */ public boolean isInteger() { if (!isBigInteger()) { return false; } return a.compareTo(Rational.MAX_INT) <= 0 && a.compareTo(Rational.MIN_INT) >= 0; } /* Rational.isInteger */ /** * Conversion to an integer value, if this can be done exactly. * * @throws Error * * @since 2011-02-13 */ int intValue() throws Error { if (!isInteger()) { throw new Error(Errors.CONVERSION_ERROR); } return a.intValue(); } /** * Conversion to a BigInteger value, if this can be done exactly. * * @throws Error * * @since 2012-03-02 */ BigInteger BigIntegerValue() throws Error { if (!isBigInteger()) { throw new Error(Errors.CONVERSION_ERROR); } return a; } /** * True if the value is a fraction of two integers in the range of the * standard integer. * * @since 2010-05-26 */ public boolean isIntegerFrac() { return a.compareTo(Rational.MAX_INT) <= 0 && a.compareTo(Rational.MIN_INT) >= 0 && b.compareTo(Rational.MAX_INT) <= 0 && b.compareTo(Rational.MIN_INT) >= 0; } /* Rational.isIntegerFrac */ /** * The sign: 1 if the number is >0, 0 if ==0, -1 if <0 * * @return the signum of the value. * @since 2010-05-26 */ public int signum() { return b.signum() * a.signum(); } /* Rational.signum */ /** * Common lcm of the denominators of a set of rational values. * * @param vals * The list/set of the rational values. * @return LCM(denom of first, denom of second, ..,denom of last) * @since 2012-03-02 */ static public BigInteger lcmDenom(final Rational[] vals) { BigInteger l = BigInteger.ONE; for (final Rational val : vals) { l = BigIntegerMath.lcm(l, val.b); } return l; } /* Rational.lcmDenom */ /** * Normalize to coprime numerator and denominator. Also copy a negative sign * of the denominator to the numerator. * * @since 2008-10-19 */ protected void normalize() { /* * compute greatest common divisor of numerator and denominator */ final BigInteger g = a.gcd(b); if (g.compareTo(BigInteger.ONE) > 0) { a = a.divide(g); b = b.divide(g); } if (b.compareTo(BigInteger.ZERO) == -1) { a = a.negate(); b = b.negate(); } } /* Rational.normalize */ } /* Rational */