Package abstrasy.libraries.math.rjm

Source Code of abstrasy.libraries.math.rjm.Rational

package abstrasy.libraries.math.rjm;

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

/** Fractions (rational numbers).
* They are divisions of two BigInteger numbers, reduced to coprime
* numerator and denominator.
* @since 2006-06-25
* @author Richard J. Mathar
*/

/*
* Subpackage : abstrasy.library.math.rjm
*
* Based on org.nevec.rjm (Java library for multi-precision evaluation of basic functions)
*
* Copyright (c) Richard J. Mathar <mathar@strw.leidenuniv.nl>, licensed under the LGPL v3.0.
*
* Restrictions on combined libraries as of section 5 of the LGPL, lifted/removed by the author.
*
* This library is free software; you can redistribute it and/or modify it under the terms of
* the GNU Lesser General Public License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the
*                                      Free Software Foundation, Inc.
*                                      51 Franklin St, Fifth Floor
*                                      Boston, MA 02110-1301 USA
*
*/

public class Rational implements Cloneable, Comparable<Rational>
{
        /** 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.
        */
        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(BigInteger a, BigInteger b)
        {
                this.a = a ;
                this.b = b ;
                normalize() ;
        }

        /** ctor from a numerator.
        * @param a the BigInteger.
        */
        public Rational(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(int a, 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(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(String str) throws NumberFormatException
        {
                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(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() ;
                }
        }

        /** Create a copy.
        * @since 2008-11-07
        */
        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 */

        /** Multiply by another fraction.
        * @param val a second rational number.
        * @return the product of this with the val.
        */
        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 */

        /** Multiply by a BigInteger.
        * @param val a second number.
        * @return the product of this with the value.
        */
        public Rational multiply(final BigInteger val)
        {
                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)
        {
                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(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 */

        /** 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.
        * @since 2009-05-18
        */
        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 */

        /** 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).
        * @since 2009-05-18
        */
        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 */

        /** 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.
        * @since 2009-05-18
        */
        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 */

        /** Divide by another fraction.
        * @param val A second rational number.
        * @return The value of this/val
        */
        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 */

        /** Divide by an integer.
        * @param val a second number.
        * @return the value of this/val
        */
        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 */

        /** Divide by an integer.
        * @param val A second number.
        * @return The value of this/val
        */
        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 */

        /** Add another fraction.
        * @param val The number to be added
        * @return this+val.
        */
        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 */

        /** Add another integer.
        * @param val The number to be added
        * @return this+val.
        */
        public Rational add(BigInteger val)
        {
                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(int val)
        {
                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(Rational val)
        {
                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(BigInteger val)
        {
                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(int val)
        {
                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
        */
        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 */

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

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

        /** 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.
        */
        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
        */
        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.
                */
                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()
        {
                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(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 */

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

        /** 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(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(MAX_INT) <= 0 && a.compareTo(MIN_INT) >= 0 ) ;
        } /* Rational.isInteger */


        /** Conversion to an integer value, if this can be done exactly.
        * @since 2011-02-13
        */
        int intValue()
        {
                if ( ! isInteger() )
                        throw new NumberFormatException("cannot convert "+toString()+" to integer.") ;
                return a.intValue() ;
        }

        /** Conversion to a BigInteger value, if this can be done exactly.
        * @since 2012-03-02
        */
        BigInteger BigIntegerValue()
        {
                if ( ! isBigInteger() )
                        throw new NumberFormatException("cannot convert "+toString()+" to BigInteger.") ;
                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(MAX_INT) <= 0 && a.compareTo(MIN_INT) >= 0
                        && b.compareTo(MAX_INT) <= 0 && b.compareTo(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(int v= 0 ; v < vals.length ; v++)
                        l = BigIntegerMath.lcm(l,vals[v].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 */

 
TOP

Related Classes of abstrasy.libraries.math.rjm.Rational

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.