All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.accelad.math.nilgiri.bigdecimal.Rational Maven / Gradle / Ivy

There is a newer version: 1.16
Show newest version
package com.accelad.math.nilgiri.bigdecimal;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;


public class Rational implements Cloneable, Comparable {

    BigInteger a;


    BigInteger b;


    static public BigInteger MAX_INT = new BigInteger("2147483647");
    static public BigInteger MIN_INT = new BigInteger("-2147483648");


    static Rational ONE = new Rational(1, 1);

    static public Rational ZERO = new Rational();


    static public Rational HALF = new Rational(1, 2);


    public Rational() {
        a = BigInteger.ZERO;
        b = BigInteger.ONE;
    }


    public Rational(BigInteger a, BigInteger b) {
        this.a = a;
        this.b = b;
        normalize();
    }


    public Rational(BigInteger a) {
        this.a = a;
        b = new BigInteger("1");
    }


    public Rational(int a, int b) {
        this(new BigInteger("" + a), new BigInteger("" + b));
    }


    public Rational(int n) {
        this(n, 1);
    }


    public Rational(String str) throws NumberFormatException {
        this(str, 10);
    }


    public Rational(String str, int radix) throws NumberFormatException {
        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();
        }
    }


    public Rational clone() {
        /*
         * protected access means this does not work return new
         * Rational(a.clone(), b.clone()) ;
         */
        BigInteger aclon = new BigInteger("" + a);
        BigInteger bclon = new BigInteger("" + b);
        return new Rational(aclon, bclon);
    } /* Rational.clone */


    public Rational multiply(final Rational val) {
        BigInteger num = a.multiply(val.a);
        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 */


    public Rational multiply(final BigInteger val) {
        Rational val2 = new Rational(val, BigInteger.ONE);
        return (multiply(val2));
    } /* Rational.multiply */


    public Rational multiply(final int val) {
        BigInteger tmp = new BigInteger("" + val);
        return multiply(tmp);
    } /* Rational.multiply */


    public Rational pow(int exponent) {
        if (exponent == 0)
            return new Rational(1, 1);

        BigInteger num = a.pow(Math.abs(exponent));
        BigInteger deno = b.pow(Math.abs(exponent));
        if (exponent > 0)
            return (new Rational(num, deno));
        else
            return (new Rational(deno, num));
    } /* Rational.pow */


    public Rational pow(BigInteger exponent) throws NumberFormatException {
        /* test for overflow */
        if (exponent.compareTo(MAX_INT) == 1)
            throw new NumberFormatException("Exponent " + exponent.toString() + " too large.");
        if (exponent.compareTo(MIN_INT) == -1)
            throw new NumberFormatException("Exponent " + exponent.toString() + " too small.");

        /* promote to the simpler interface above */
        return pow(exponent.intValue());
    } /* Rational.pow */


    public Rational root(BigInteger r) throws NumberFormatException {
        /* test for overflow */
        if (r.compareTo(MAX_INT) == 1)
            throw new NumberFormatException("Root " + r.toString() + " too large.");
        if (r.compareTo(MIN_INT) == -1)
            throw new NumberFormatException("Root " + r.toString() + " too small.");

        int rthroot = r.intValue();
        /* cannot pull root of a negative value with even-valued root */
        if (compareTo(ZERO) == -1 && (rthroot % 2) == 0)
            throw new NumberFormatException(
                    "Negative basis " + toString() + " with odd root " + r.toString());

        /*
         * extract a sign such that we calculate |n|^(1/r), still r carrying any
         * sign
         */
        final boolean flipsign = (compareTo(ZERO) == -1 && (rthroot % 2) != 0) ? true : false;

        /*
         * delegate the main work to ifactor#root()
         */
        Ifactor num = new Ifactor(a.abs());
        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 */


    public Rational pow(Rational exponent) throws NumberFormatException {
        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)
         */
        Rational tmp = pow(exponent.a);
        return tmp.root(exponent.b);
    } /* Rational.pow */


    public Rational divide(final Rational val) {
        if (val.compareTo(Rational.ZERO) == 0)
            throw new ArithmeticException("Dividing " + toString() + " through zero.");
        BigInteger num = a.multiply(val.b);
        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 */


    public Rational divide(BigInteger val) {
        if (val.compareTo(BigInteger.ZERO) == 0)
            throw new ArithmeticException("Dividing " + toString() + " through zero.");
        Rational val2 = new Rational(val, BigInteger.ONE);
        return (divide(val2));
    } /* Rational.divide */


    public Rational divide(int val) {
        if (val == 0)
            throw new ArithmeticException("Dividing " + toString() + " through zero.");
        Rational val2 = new Rational(val, 1);
        return (divide(val2));
    } /* Rational.divide */


    public Rational add(Rational val) {
        BigInteger num = a.multiply(val.b).add(b.multiply(val.a));
        BigInteger deno = b.multiply(val.b);
        return (new Rational(num, deno));
    } /* Rational.add */


    public Rational add(BigInteger val) {
        Rational val2 = new Rational(val, BigInteger.ONE);
        return (add(val2));
    } /* Rational.add */


    public Rational add(int val) {
        BigInteger val2 = a.add(b.multiply(new BigInteger("" + val)));
        return new Rational(val2, b);
    } /* Rational.add */


    public Rational negate() {
        return (new Rational(a.negate(), b));
    } /* Rational.negate */


    public Rational subtract(Rational val) {
        Rational val2 = val.negate();
        return (add(val2));
    } /* Rational.subtract */


    public Rational subtract(BigInteger val) {
        Rational val2 = new Rational(val, BigInteger.ONE);
        return (subtract(val2));
    } /* Rational.subtract */


    public Rational subtract(int val) {
        Rational val2 = new Rational(val, 1);
        return (subtract(val2));
    } /* Rational.subtract */


    public static Rational binomial(Rational n, BigInteger m) {
        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 */


    public static Rational binomial(Rational n, int m) {
        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 */


    public static Rational hankelSymb(Rational n, int k) {
        if (k == 0)
            return Rational.ONE;
        else if (k < 0)
            throw new ArithmeticException("Negative parameter " + k);
        Rational nkhalf = n.subtract(k).add(Rational.HALF);
        nkhalf = nkhalf.Pochhammer(2 * k);
        Factorial f = new Factorial();
        return nkhalf.divide(f.at(k));
    } /* Rational.binomial */


    public BigInteger numer() {
        return a;
    }


    public BigInteger denom() {
        return b;
    }


    public Rational abs() {
        return (new Rational(a.abs(), b.abs()));
    }


    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 */


    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 */


    public BigInteger trunc() {
        /*
         * is already integer: return the numerator
         */
        if (b.compareTo(BigInteger.ONE) == 0)
            return a;
        else
            return a.divide(b);
    } /* Rational.trunc */


    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 */


    public int compareTo(final BigInteger val) {
        final Rational val2 = new Rational(val, BigInteger.ONE);
        return (compareTo(val2));
    } /* Rational.compareTo */


    public String toString() {
        if (b.compareTo(BigInteger.ONE) != 0)
            return (a.toString() + "/" + b.toString());
        else
            return a.toString();
    } /* Rational.toString */


    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.
         */
        BigDecimal adivb = (new BigDecimal(a)).divide(new BigDecimal(b), MathContext.DECIMAL128);
        return adivb.doubleValue();
    } /* Rational.doubleValue */


    public float floatValue() {
        BigDecimal adivb = (new BigDecimal(a)).divide(new BigDecimal(b), MathContext.DECIMAL128);
        return adivb.floatValue();
    } /* Rational.floatValue */


    public BigDecimal BigDecimalValue(MathContext mc) {
        /*
         * numerator and denominator individually rephrased
         */
        BigDecimal n = new BigDecimal(a);
        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 */


    public String toFString(int digits) {
        if (b.compareTo(BigInteger.ONE) != 0) {
            MathContext mc = new MathContext(digits, RoundingMode.DOWN);
            BigDecimal f = (new BigDecimal(a)).divide(new BigDecimal(b), mc);
            return (f.toString());
        } else
            return a.toString();
    } /* Rational.toFString */


    public Rational max(final Rational val) {
        if (compareTo(val) > 0)
            return this;
        else
            return val;
    } /* Rational.max */


    public Rational min(final Rational val) {
        if (compareTo(val) < 0)
            return this;
        else
            return val;
    } /* Rational.min */


    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 */


    public Rational Pochhammer(int n) {
        return Pochhammer(new BigInteger("" + n));
    } /* Rational.pochhammer */


    public boolean isBigInteger() {
        return (b.abs().compareTo(BigInteger.ONE) == 0);
    } /* Rational.isBigInteger */


    public boolean isInteger() {
        if (!isBigInteger())
            return false;
        return (a.compareTo(MAX_INT) <= 0 && a.compareTo(MIN_INT) >= 0);
    } /* Rational.isInteger */


    int intValue() {
        if (!isInteger())
            throw new NumberFormatException("cannot convert " + toString() + " to integer.");
        return a.intValue();
    }


    BigInteger BigIntegerValue() {
        if (!isBigInteger())
            throw new NumberFormatException("cannot convert " + toString() + " to BigInteger.");
        return a;
    }


    public boolean isIntegerFrac() {
        return (a.compareTo(MAX_INT) <= 0 && a.compareTo(MIN_INT) >= 0 && b.compareTo(MAX_INT) <= 0
                && b.compareTo(MIN_INT) >= 0);
    } /* Rational.isIntegerFrac */


    public int signum() {
        return (b.signum() * a.signum());
    } /* Rational.signum */


    static public BigInteger lcmDenom(final Rational[] vals) {
        BigInteger l = BigInteger.ONE;
        for (int v = 0; v < vals.length; v++)
            l = BigIntegerMath.lcm(l, vals[v].b);
        return l;
    } /* Rational.lcmDenom */


    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 */




© 2015 - 2025 Weber Informatics LLC | Privacy Policy