Coverage Report - org.apache.commons.math.fraction.Fraction

Classes in this File Line Coverage Branch Coverage Complexity
Fraction
96% 
97% 
3.182

 1  
 /*
 2  
  * Copyright 2005 The Apache Software Foundation.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.apache.commons.math.fraction;
 17  
 
 18  
 import java.math.BigInteger;
 19  
 import org.apache.commons.math.ConvergenceException;
 20  
 import org.apache.commons.math.util.MathUtils;
 21  
 
 22  
 /**
 23  
  * Representation of a rational number.
 24  
  *
 25  
  * @since 1.1
 26  
  * @version $Revision$ $Date: 2005-06-25 18:52:37 -0700 (Sat, 25 Jun 2005) $
 27  
  */
 28  
 public class Fraction extends Number implements Comparable {
 29  
 
 30  
     /** A fraction representing "1 / 1". */
 31  4
     public static final Fraction ONE = new Fraction(1, 1);
 32  
 
 33  
     /** A fraction representing "0 / 1". */
 34  4
     public static final Fraction ZERO = new Fraction(0, 1);
 35  
     
 36  
     /** Serializable version identifier */
 37  
     static final long serialVersionUID = 65382027393090L;
 38  
     
 39  
     /** The denominator. */
 40  
     private int denominator;
 41  
     
 42  
     /** The numerator. */
 43  
     private int numerator;
 44  
 
 45  
     /**
 46  
      * Create a fraction given the double value.
 47  
      * @param value the double value to convert to a fraction.
 48  
      * @throws ConvergenceException if the continued fraction failed to
 49  
      *         converge.
 50  
      */
 51  
     public Fraction(double value) throws ConvergenceException {
 52  22
         this(value, 1.0e-5, 100);
 53  22
     }
 54  
 
 55  
     /**
 56  
      * Create a fraction given the double value.
 57  
      * <p>
 58  
      * References:
 59  
      * <ul>
 60  
      * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
 61  
      * Continued Fraction</a> equations (11) and (22)-(26)</li>
 62  
      * </ul>
 63  
      * </p>
 64  
      * @param value the double value to convert to a fraction.
 65  
      * @param epsilon maximum error allowed.  The resulting fraction is within
 66  
      *        <code>epsilon</code> of <code>value</code>, in absolute terms.
 67  
      * @param maxIterations maximum number of convergents
 68  
      * @throws ConvergenceException if the continued fraction failed to
 69  
      *         converge.
 70  
      */
 71  
     public Fraction(double value, double epsilon, int maxIterations)
 72  
         throws ConvergenceException
 73  22
     {
 74  22
         double r0 = value;
 75  22
         int a0 = (int)Math.floor(r0);
 76  
 
 77  
         // check for (almost) integer arguments, which should not go
 78  
         // to iterations.
 79  22
         if (Math.abs(a0 - value) < epsilon) {
 80  4
             this.numerator = a0;
 81  4
             this.denominator = 1;
 82  4
             return;
 83  
         }
 84  
         
 85  18
         int p0 = 1;
 86  18
         int q0 = 0;
 87  18
         int p1 = a0;
 88  18
         int q1 = 1;
 89  
 
 90  18
         int p2 = 0;
 91  18
         int q2 = 1;
 92  
 
 93  18
         int n = 0;
 94  18
         boolean stop = false;
 95  
         do {
 96  54
             ++n;
 97  54
             double r1 = 1.0 / (r0 - a0);
 98  54
             int a1 = (int)Math.floor(r1);
 99  54
             p2 = (a1 * p1) + p0;
 100  54
             q2 = (a1 * q1) + q0;
 101  
             
 102  54
             double convergent = (double)p2 / (double)q2;
 103  54
             if (n < maxIterations && Math.abs(convergent - value) > epsilon) {
 104  36
                 p0 = p1;
 105  36
                 p1 = p2;
 106  36
                 q0 = q1;
 107  36
                 q1 = q2;
 108  36
                 a0 = a1;
 109  36
                 r0 = r1;
 110  
             } else {
 111  18
                 stop = true;
 112  
             }
 113  54
         } while (!stop);
 114  
 
 115  18
         if (n >= maxIterations) {
 116  0
             throw new ConvergenceException(
 117  
                     "Unable to convert double to fraction");
 118  
         }
 119  
         
 120  18
         this.numerator = p2;
 121  18
         this.denominator = q2;
 122  18
         reduce();
 123  18
     }
 124  
     
 125  
     /**
 126  
      * Create a fraction given the numerator and denominator.  The fraction is
 127  
      * reduced to lowest terms.
 128  
      * @param num the numerator.
 129  
      * @param den the denominator.
 130  
      * @throws ArithmeticException if the denomiator is <code>zero</code>
 131  
      */
 132  
     public Fraction(int num, int den) {
 133  318
         super();
 134  318
         if (den == 0) {
 135  2
             throw new ArithmeticException("The denominator must not be zero");
 136  
         }
 137  316
         if (den < 0) {
 138  24
             if (num == Integer.MIN_VALUE ||
 139  
                     den == Integer.MIN_VALUE) {
 140  4
                 throw new ArithmeticException("overflow: can't negate");
 141  
             }
 142  20
             num = -num;
 143  20
             den = -den;
 144  
         }
 145  312
         this.numerator = num;
 146  312
         this.denominator = den;
 147  312
         reduce();
 148  312
     }
 149  
     
 150  
     /**
 151  
      * Returns the absolute value of this fraction.
 152  
      * @return the absolute value.
 153  
      */
 154  
     public Fraction abs() {
 155  
         Fraction ret;
 156  6
         if (numerator >= 0) {
 157  2
             ret = this;
 158  
         } else {
 159  4
             ret = negate();
 160  
         }
 161  6
         return ret;        
 162  
     }
 163  
     
 164  
     /**
 165  
      * Compares this object to another based on size.
 166  
      * @param object the object to compare to
 167  
      * @return -1 if this is less than <tt>object</tt>, +1 if this is greater
 168  
      *         than <tt>object</tt>, 0 if they are equal.
 169  
      */
 170  
     public int compareTo(Object object) {
 171  8
         int ret = 0;
 172  
         
 173  8
         if (this != object) { 
 174  6
             Fraction other = (Fraction)object;
 175  6
             double first = doubleValue();
 176  6
             double second = other.doubleValue();
 177  
             
 178  6
             if (first < second) {
 179  2
                 ret = -1;
 180  4
             } else if (first > second) {
 181  2
                 ret = 1;
 182  
             }
 183  
         }
 184  
         
 185  8
         return ret;
 186  
     }
 187  
     
 188  
     /**
 189  
      * Gets the fraction as a <tt>double</tt>. This calculates the fraction as
 190  
      * the numerator divided by denominator.
 191  
      * @return the fraction as a <tt>double</tt>
 192  
      */
 193  
     public double doubleValue() {
 194  28
         return (double)numerator / (double)denominator;
 195  
     }
 196  
     
 197  
     /**
 198  
      * Test for the equality of two fractions.  If the lowest term
 199  
      * numerator and denominators are the same for both fractions, the two
 200  
      * fractions are considered to be equal.
 201  
      * @param other fraction to test for equality to this fraction
 202  
      * @return true if two fractions are equal, false if object is
 203  
      *         <tt>null</tt>, not an instance of {@link Fraction}, or not equal
 204  
      *         to this fraction instance.
 205  
      */
 206  
     public boolean equals(Object other) {
 207  
         boolean ret;
 208  
         
 209  16
         if (this == other) { 
 210  4
             ret = true;
 211  12
         } else if (other == null) {
 212  2
             ret = false;
 213  
         } else {
 214  
             try {
 215  
                 // since fractions are always in lowest terms, numerators and
 216  
                 // denominators can be compared directly for equality.
 217  10
                 Fraction rhs = (Fraction)other;
 218  8
                 ret = (numerator == rhs.numerator) &&
 219  
                     (denominator == rhs.denominator);
 220  2
             } catch (ClassCastException ex) {
 221  
                 // ignore exception
 222  2
                 ret = false;
 223  8
             }
 224  
         }
 225  
         
 226  16
         return ret;
 227  
     }
 228  
     
 229  
     /**
 230  
      * Gets the fraction as a <tt>float</tt>. This calculates the fraction as
 231  
      * the numerator divided by denominator.
 232  
      * @return the fraction as a <tt>float</tt>
 233  
      */
 234  
     public float floatValue() {
 235  4
         return (float)doubleValue();
 236  
     }
 237  
     
 238  
     /**
 239  
      * Access the denominator.
 240  
      * @return the denominator.
 241  
      */
 242  
     public int getDenominator() {
 243  162
         return denominator;
 244  
     }
 245  
     
 246  
     /**
 247  
      * Access the numerator.
 248  
      * @return the numerator.
 249  
      */
 250  
     public int getNumerator() {
 251  166
         return numerator;
 252  
     }
 253  
     
 254  
     /**
 255  
      * Gets a hashCode for the fraction.
 256  
      * @return a hash code value for this object
 257  
      */
 258  
     public int hashCode() {
 259  6
         return 37 * (37 * 17 + getNumerator()) + getDenominator();
 260  
     }
 261  
     
 262  
     /**
 263  
      * Gets the fraction as an <tt>int</tt>. This returns the whole number part
 264  
      * of the fraction.
 265  
      * @return the whole number fraction part
 266  
      */
 267  
     public int intValue() {
 268  4
         return (int)doubleValue();
 269  
     }
 270  
     
 271  
     /**
 272  
      * Gets the fraction as a <tt>long</tt>. This returns the whole number part
 273  
      * of the fraction.
 274  
      * @return the whole number fraction part
 275  
      */
 276  
     public long longValue() {
 277  4
         return (long)doubleValue();
 278  
     }
 279  
     
 280  
     /**
 281  
      * Return the additive inverse of this fraction.
 282  
      * @return the negation of this fraction.
 283  
      */
 284  
     public Fraction negate() {
 285  16
         if (numerator==Integer.MIN_VALUE) {
 286  2
             throw new ArithmeticException("overflow: too large to negate");
 287  
         }
 288  14
         return new Fraction(-numerator, denominator);
 289  
     }
 290  
 
 291  
     /**
 292  
      * Return the multiplicative inverse of this fraction.
 293  
      * @return the reciprocal fraction
 294  
      */
 295  
     public Fraction reciprocal() {
 296  34
         return new Fraction(denominator, numerator);
 297  
     }
 298  
     
 299  
     /**
 300  
      * <p>Adds the value of this fraction to another, returning the result in reduced form.
 301  
      * The algorithm follows Knuth, 4.5.1.</p>
 302  
      *
 303  
      * @param fraction  the fraction to add, must not be <code>null</code>
 304  
      * @return a <code>Fraction</code> instance with the resulting values
 305  
      * @throws IllegalArgumentException if the fraction is <code>null</code>
 306  
      * @throws ArithmeticException if the resulting numerator or denominator exceeds
 307  
      *  <code>Integer.MAX_VALUE</code>
 308  
      */
 309  
     public Fraction add(Fraction fraction) {
 310  30
         return addSub(fraction, true /* add */);
 311  
     }
 312  
 
 313  
     /**
 314  
      * <p>Subtracts the value of another fraction from the value of this one, 
 315  
      * returning the result in reduced form.</p>
 316  
      *
 317  
      * @param fraction  the fraction to subtract, must not be <code>null</code>
 318  
      * @return a <code>Fraction</code> instance with the resulting values
 319  
      * @throws IllegalArgumentException if the fraction is <code>null</code>
 320  
      * @throws ArithmeticException if the resulting numerator or denominator
 321  
      *   cannot be represented in an <code>int</code>.
 322  
      */
 323  
     public Fraction subtract(Fraction fraction) {
 324  26
         return addSub(fraction, false /* subtract */);
 325  
     }
 326  
 
 327  
     /** 
 328  
      * Implement add and subtract using algorithm described in Knuth 4.5.1.
 329  
      * 
 330  
      * @param fraction the fraction to subtract, must not be <code>null</code>
 331  
      * @param isAdd true to add, false to subtract
 332  
      * @return a <code>Fraction</code> instance with the resulting values
 333  
      * @throws IllegalArgumentException if the fraction is <code>null</code>
 334  
      * @throws ArithmeticException if the resulting numerator or denominator
 335  
      *   cannot be represented in an <code>int</code>.
 336  
      */
 337  
     private Fraction addSub(Fraction fraction, boolean isAdd) {
 338  56
         if (fraction == null) {
 339  4
             throw new IllegalArgumentException("The fraction must not be null");
 340  
         }
 341  
         // zero is identity for addition.
 342  52
         if (numerator == 0) {
 343  0
             return isAdd ? fraction : fraction.negate();
 344  
         }
 345  52
         if (fraction.numerator == 0) {
 346  0
             return this;
 347  
         }     
 348  
         // if denominators are randomly distributed, d1 will be 1 about 61%
 349  
         // of the time.
 350  52
         int d1 = MathUtils.gcd(denominator, fraction.denominator);
 351  52
         if (d1==1) {
 352  
             // result is ( (u*v' +/- u'v) / u'v')
 353  30
             int uvp = MathUtils.mulAndCheck(numerator, fraction.denominator);
 354  30
             int upv = MathUtils.mulAndCheck(fraction.numerator, denominator);
 355  30
             return new Fraction
 356  
                 (isAdd ? MathUtils.addAndCheck(uvp, upv) : 
 357  
                  MathUtils.subAndCheck(uvp, upv),
 358  
                  MathUtils.mulAndCheck(denominator, fraction.denominator));
 359  
         }
 360  
         // the quantity 't' requires 65 bits of precision; see knuth 4.5.1
 361  
         // exercise 7.  we're going to use a BigInteger.
 362  
         // t = u(v'/d1) +/- v(u'/d1)
 363  22
         BigInteger uvp = BigInteger.valueOf(numerator)
 364  
         .multiply(BigInteger.valueOf(fraction.denominator/d1));
 365  22
         BigInteger upv = BigInteger.valueOf(fraction.numerator)
 366  
         .multiply(BigInteger.valueOf(denominator/d1));
 367  22
         BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv);
 368  
         // but d2 doesn't need extra precision because
 369  
         // d2 = gcd(t,d1) = gcd(t mod d1, d1)
 370  22
         int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue();
 371  22
         int d2 = (tmodd1==0)?d1:MathUtils.gcd(tmodd1, d1);
 372  
 
 373  
         // result is (t/d2) / (u'/d1)(v'/d2)
 374  22
         BigInteger w = t.divide(BigInteger.valueOf(d2));
 375  22
         if (w.bitLength() > 31) {
 376  4
             throw new ArithmeticException
 377  
             ("overflow: numerator too large after multiply");
 378  
         }
 379  18
         return new Fraction (w.intValue(), 
 380  
                 MathUtils.mulAndCheck(denominator/d1, 
 381  
                         fraction.denominator/d2));
 382  
     }
 383  
 
 384  
     /**
 385  
      * <p>Multiplies the value of this fraction by another, returning the 
 386  
      * result in reduced form.</p>
 387  
      *
 388  
      * @param fraction  the fraction to multiply by, must not be <code>null</code>
 389  
      * @return a <code>Fraction</code> instance with the resulting values
 390  
      * @throws IllegalArgumentException if the fraction is <code>null</code>
 391  
      * @throws ArithmeticException if the resulting numerator or denominator exceeds
 392  
      *  <code>Integer.MAX_VALUE</code>
 393  
      */
 394  
     public Fraction multiply(Fraction fraction) {
 395  32
         if (fraction == null) {
 396  2
             throw new IllegalArgumentException("The fraction must not be null");
 397  
         }
 398  30
         if (numerator == 0 || fraction.numerator == 0) {
 399  2
             return ZERO;
 400  
         }
 401  
         // knuth 4.5.1
 402  
         // make sure we don't overflow unless the result *must* overflow.
 403  28
         int d1 = MathUtils.gcd(numerator, fraction.denominator);
 404  28
         int d2 = MathUtils.gcd(fraction.numerator, denominator);
 405  28
         return getReducedFraction
 406  
         (MathUtils.mulAndCheck(numerator/d1, fraction.numerator/d2),
 407  
                 MathUtils.mulAndCheck(denominator/d2, fraction.denominator/d1));
 408  
     }
 409  
 
 410  
     /**
 411  
      * <p>Divide the value of this fraction by another.</p>
 412  
      *
 413  
      * @param fraction  the fraction to divide by, must not be <code>null</code>
 414  
      * @return a <code>Fraction</code> instance with the resulting values
 415  
      * @throws IllegalArgumentException if the fraction is <code>null</code>
 416  
      * @throws ArithmeticException if the fraction to divide by is zero
 417  
      * @throws ArithmeticException if the resulting numerator or denominator exceeds
 418  
      *  <code>Integer.MAX_VALUE</code>
 419  
      */
 420  
     public Fraction divide(Fraction fraction) {
 421  24
         if (fraction == null) {
 422  2
             throw new IllegalArgumentException("The fraction must not be null");
 423  
         }
 424  22
         if (fraction.numerator == 0) {
 425  2
             throw new ArithmeticException("The fraction to divide by must not be zero");
 426  
         }
 427  20
         return multiply(fraction.reciprocal());
 428  
     }
 429  
     
 430  
     /**
 431  
      * <p>Creates a <code>Fraction</code> instance with the 2 parts
 432  
      * of a fraction Y/Z.</p>
 433  
      *
 434  
      * <p>Any negative signs are resolved to be on the numerator.</p>
 435  
      *
 436  
      * @param numerator  the numerator, for example the three in 'three sevenths'
 437  
      * @param denominator  the denominator, for example the seven in 'three sevenths'
 438  
      * @return a new fraction instance, with the numerator and denominator reduced
 439  
      * @throws ArithmeticException if the denominator is <code>zero</code>
 440  
      */
 441  
     public static Fraction getReducedFraction(int numerator, int denominator) {
 442  34
         if (denominator == 0) {
 443  2
             throw new ArithmeticException("The denominator must not be zero");
 444  
         }
 445  32
         if (numerator==0) {
 446  2
             return ZERO; // normalize zero.
 447  
         }
 448  
         // allow 2^k/-2^31 as a valid fraction (where k>0)
 449  30
         if (denominator==Integer.MIN_VALUE && (numerator&1)==0) {
 450  2
             numerator/=2; denominator/=2;
 451  
         }
 452  30
         if (denominator < 0) {
 453  4
             if (numerator==Integer.MIN_VALUE ||
 454  
                     denominator==Integer.MIN_VALUE) {
 455  0
                 throw new ArithmeticException("overflow: can't negate");
 456  
             }
 457  4
             numerator = -numerator;
 458  4
             denominator = -denominator;
 459  
         }
 460  
         // simplify fraction.
 461  30
         int gcd = MathUtils.gcd(numerator, denominator);
 462  30
         numerator /= gcd;
 463  30
         denominator /= gcd;
 464  30
         return new Fraction(numerator, denominator);
 465  
     }
 466  
     
 467  
     /**
 468  
      * Reduce this fraction to lowest terms.  This is accomplished by dividing
 469  
      * both numerator and denominator by their greatest common divisor.
 470  
      */
 471  
     private void reduce() {
 472  
         // reduce numerator and denominator by greatest common denominator.
 473  330
         int d = MathUtils.gcd(numerator, denominator);
 474  330
         if (d > 1) {
 475  22
             numerator /= d;
 476  22
             denominator /= d;
 477  
         }
 478  
 
 479  
         // move sign to numerator.
 480  330
         if (denominator < 0) {
 481  0
             numerator *= -1;
 482  0
             denominator *= -1;
 483  
         }
 484  330
     }
 485  
 }