Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      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 android.util;
     17 
     18 import static com.android.internal.util.Preconditions.*;
     19 
     20 import android.annotation.UnsupportedAppUsage;
     21 import java.io.IOException;
     22 import java.io.InvalidObjectException;
     23 
     24 /**
     25  * <p>An immutable data type representation a rational number.</p>
     26  *
     27  * <p>Contains a pair of {@code int}s representing the numerator and denominator of a
     28  * Rational number. </p>
     29  */
     30 public final class Rational extends Number implements Comparable<Rational> {
     31     /**
     32      * Constant for the <em>Not-a-Number (NaN)</em> value of the {@code Rational} type.
     33      *
     34      * <p>A {@code NaN} value is considered to be equal to itself (that is {@code NaN.equals(NaN)}
     35      * will return {@code true}; it is always greater than any non-{@code NaN} value (that is
     36      * {@code NaN.compareTo(notNaN)} will return a number greater than {@code 0}).</p>
     37      *
     38      * <p>Equivalent to constructing a new rational with both the numerator and denominator
     39      * equal to {@code 0}.</p>
     40      */
     41     public static final Rational NaN = new Rational(0, 0);
     42 
     43     /**
     44      * Constant for the positive infinity value of the {@code Rational} type.
     45      *
     46      * <p>Equivalent to constructing a new rational with a positive numerator and a denominator
     47      * equal to {@code 0}.</p>
     48      */
     49     public static final Rational POSITIVE_INFINITY = new Rational(1, 0);
     50 
     51     /**
     52      * Constant for the negative infinity value of the {@code Rational} type.
     53      *
     54      * <p>Equivalent to constructing a new rational with a negative numerator and a denominator
     55      * equal to {@code 0}.</p>
     56      */
     57     public static final Rational NEGATIVE_INFINITY = new Rational(-1, 0);
     58 
     59     /**
     60      * Constant for the zero value of the {@code Rational} type.
     61      *
     62      * <p>Equivalent to constructing a new rational with a numerator equal to {@code 0} and
     63      * any non-zero denominator.</p>
     64      */
     65     public static final Rational ZERO = new Rational(0, 1);
     66 
     67     /**
     68      * Unique version number per class to be compliant with {@link java.io.Serializable}.
     69      *
     70      * <p>Increment each time the fields change in any way.</p>
     71      */
     72     private static final long serialVersionUID = 1L;
     73 
     74     /*
     75      * Do not change the order of these fields or add new instance fields to maintain the
     76      * Serializable compatibility across API revisions.
     77      */
     78     @UnsupportedAppUsage
     79     private final int mNumerator;
     80     @UnsupportedAppUsage
     81     private final int mDenominator;
     82 
     83     /**
     84      * <p>Create a {@code Rational} with a given numerator and denominator.</p>
     85      *
     86      * <p>The signs of the numerator and the denominator may be flipped such that the denominator
     87      * is always positive. Both the numerator and denominator will be converted to their reduced
     88      * forms (see {@link #equals} for more details).</p>
     89      *
     90      * <p>For example,
     91      * <ul>
     92      * <li>a rational of {@code 2/4} will be reduced to {@code 1/2}.
     93      * <li>a rational of {@code 1/-1} will be flipped to {@code -1/1}
     94      * <li>a rational of {@code 5/0} will be reduced to {@code 1/0}
     95      * <li>a rational of {@code 0/5} will be reduced to {@code 0/1}
     96      * </ul>
     97      * </p>
     98      *
     99      * @param numerator the numerator of the rational
    100      * @param denominator the denominator of the rational
    101      *
    102      * @see #equals
    103      */
    104     public Rational(int numerator, int denominator) {
    105 
    106         if (denominator < 0) {
    107             numerator = -numerator;
    108             denominator = -denominator;
    109         }
    110 
    111         // Convert to reduced form
    112         if (denominator == 0 && numerator > 0) {
    113             mNumerator = 1; // +Inf
    114             mDenominator = 0;
    115         } else if (denominator == 0 && numerator < 0) {
    116             mNumerator = -1; // -Inf
    117             mDenominator = 0;
    118         } else if (denominator == 0 && numerator == 0) {
    119             mNumerator = 0; // NaN
    120             mDenominator = 0;
    121         } else if (numerator == 0) {
    122             mNumerator = 0;
    123             mDenominator = 1;
    124         } else {
    125             int gcd = gcd(numerator, denominator);
    126 
    127             mNumerator = numerator / gcd;
    128             mDenominator = denominator / gcd;
    129         }
    130     }
    131 
    132     /**
    133      * Gets the numerator of the rational.
    134      *
    135      * <p>The numerator will always return {@code 1} if this rational represents
    136      * infinity (that is, the denominator is {@code 0}).</p>
    137      */
    138     public int getNumerator() {
    139         return mNumerator;
    140     }
    141 
    142     /**
    143      * Gets the denominator of the rational
    144      *
    145      * <p>The denominator may return {@code 0}, in which case the rational may represent
    146      * positive infinity (if the numerator was positive), negative infinity (if the numerator
    147      * was negative), or {@code NaN} (if the numerator was {@code 0}).</p>
    148      *
    149      * <p>The denominator will always return {@code 1} if the numerator is {@code 0}.
    150      */
    151     public int getDenominator() {
    152         return mDenominator;
    153     }
    154 
    155     /**
    156      * Indicates whether this rational is a <em>Not-a-Number (NaN)</em> value.
    157      *
    158      * <p>A {@code NaN} value occurs when both the numerator and the denominator are {@code 0}.</p>
    159      *
    160      * @return {@code true} if this rational is a <em>Not-a-Number (NaN)</em> value;
    161      *         {@code false} if this is a (potentially infinite) number value
    162      */
    163     public boolean isNaN() {
    164         return mDenominator == 0 && mNumerator == 0;
    165     }
    166 
    167     /**
    168      * Indicates whether this rational represents an infinite value.
    169      *
    170      * <p>An infinite value occurs when the denominator is {@code 0} (but the numerator is not).</p>
    171      *
    172      * @return {@code true} if this rational is a (positive or negative) infinite value;
    173      *         {@code false} if this is a finite number value (or {@code NaN})
    174      */
    175     public boolean isInfinite() {
    176         return mNumerator != 0 && mDenominator == 0;
    177     }
    178 
    179     /**
    180      * Indicates whether this rational represents a finite value.
    181      *
    182      * <p>A finite value occurs when the denominator is not {@code 0}; in other words
    183      * the rational is neither infinity or {@code NaN}.</p>
    184      *
    185      * @return {@code true} if this rational is a (positive or negative) infinite value;
    186      *         {@code false} if this is a finite number value (or {@code NaN})
    187      */
    188     public boolean isFinite() {
    189         return mDenominator != 0;
    190     }
    191 
    192     /**
    193      * Indicates whether this rational represents a zero value.
    194      *
    195      * <p>A zero value is a {@link #isFinite finite} rational with a numerator of {@code 0}.</p>
    196      *
    197      * @return {@code true} if this rational is finite zero value;
    198      *         {@code false} otherwise
    199      */
    200     public boolean isZero() {
    201         return isFinite() && mNumerator == 0;
    202     }
    203 
    204     private boolean isPosInf() {
    205         return mDenominator == 0 && mNumerator > 0;
    206     }
    207 
    208     private boolean isNegInf() {
    209         return mDenominator == 0 && mNumerator < 0;
    210     }
    211 
    212     /**
    213      * <p>Compare this Rational to another object and see if they are equal.</p>
    214      *
    215      * <p>A Rational object can only be equal to another Rational object (comparing against any
    216      * other type will return {@code false}).</p>
    217      *
    218      * <p>A Rational object is considered equal to another Rational object if and only if one of
    219      * the following holds:</p>
    220      * <ul><li>Both are {@code NaN}</li>
    221      *     <li>Both are infinities of the same sign</li>
    222      *     <li>Both have the same numerator and denominator in their reduced form</li>
    223      * </ul>
    224      *
    225      * <p>A reduced form of a Rational is calculated by dividing both the numerator and the
    226      * denominator by their greatest common divisor.</p>
    227      *
    228      * <pre>{@code
    229      * (new Rational(1, 2)).equals(new Rational(1, 2)) == true   // trivially true
    230      * (new Rational(2, 3)).equals(new Rational(1, 2)) == false  // trivially false
    231      * (new Rational(1, 2)).equals(new Rational(2, 4)) == true   // true after reduction
    232      * (new Rational(0, 0)).equals(new Rational(0, 0)) == true   // NaN.equals(NaN)
    233      * (new Rational(1, 0)).equals(new Rational(5, 0)) == true   // both are +infinity
    234      * (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity
    235      * }</pre>
    236      *
    237      * @param obj a reference to another object
    238      *
    239      * @return A boolean that determines whether or not the two Rational objects are equal.
    240      */
    241     @Override
    242     public boolean equals(Object obj) {
    243         return obj instanceof Rational && equals((Rational) obj);
    244     }
    245 
    246     private boolean equals(Rational other) {
    247         return (mNumerator == other.mNumerator && mDenominator == other.mDenominator);
    248     }
    249 
    250     /**
    251      * Return a string representation of this rational, e.g. {@code "1/2"}.
    252      *
    253      * <p>The following rules of conversion apply:
    254      * <ul>
    255      * <li>{@code NaN} values will return {@code "NaN"}
    256      * <li>Positive infinity values will return {@code "Infinity"}
    257      * <li>Negative infinity values will return {@code "-Infinity"}
    258      * <li>All other values will return {@code "numerator/denominator"} where {@code numerator}
    259      * and {@code denominator} are substituted with the appropriate numerator and denominator
    260      * values.
    261      * </ul></p>
    262      */
    263     @Override
    264     public String toString() {
    265         if (isNaN()) {
    266             return "NaN";
    267         } else if (isPosInf()) {
    268             return "Infinity";
    269         } else if (isNegInf()) {
    270             return "-Infinity";
    271         } else {
    272             return mNumerator + "/" + mDenominator;
    273         }
    274     }
    275 
    276     /**
    277      * <p>Convert to a floating point representation.</p>
    278      *
    279      * @return The floating point representation of this rational number.
    280      * @hide
    281      */
    282     public float toFloat() {
    283         // TODO: remove this duplicate function (used in CTS and the shim)
    284         return floatValue();
    285     }
    286 
    287     /**
    288      * {@inheritDoc}
    289      */
    290     @Override
    291     public int hashCode() {
    292         // Bias the hash code for the first (2^16) values for both numerator and denominator
    293         int numeratorFlipped = mNumerator << 16 | mNumerator >>> 16;
    294 
    295         return mDenominator ^ numeratorFlipped;
    296     }
    297 
    298     /**
    299      * Calculates the greatest common divisor using Euclid's algorithm.
    300      *
    301      * <p><em>Visible for testing only.</em></p>
    302      *
    303      * @param numerator the numerator in a fraction
    304      * @param denominator the denominator in a fraction
    305      *
    306      * @return An int value representing the gcd. Always positive.
    307      * @hide
    308      */
    309     public static int gcd(int numerator, int denominator) {
    310         /*
    311          * Non-recursive implementation of Euclid's algorithm:
    312          *
    313          *  gcd(a, 0) := a
    314          *  gcd(a, b) := gcd(b, a mod b)
    315          *
    316          */
    317         int a = numerator;
    318         int b = denominator;
    319 
    320         while (b != 0) {
    321             int oldB = b;
    322 
    323             b = a % b;
    324             a = oldB;
    325         }
    326 
    327         return Math.abs(a);
    328     }
    329 
    330     /**
    331      * Returns the value of the specified number as a {@code double}.
    332      *
    333      * <p>The {@code double} is calculated by converting both the numerator and denominator
    334      * to a {@code double}; then returning the result of dividing the numerator by the
    335      * denominator.</p>
    336      *
    337      * @return the divided value of the numerator and denominator as a {@code double}.
    338      */
    339     @Override
    340     public double doubleValue() {
    341         double num = mNumerator;
    342         double den = mDenominator;
    343 
    344         return num / den;
    345     }
    346 
    347     /**
    348      * Returns the value of the specified number as a {@code float}.
    349      *
    350      * <p>The {@code float} is calculated by converting both the numerator and denominator
    351      * to a {@code float}; then returning the result of dividing the numerator by the
    352      * denominator.</p>
    353      *
    354      * @return the divided value of the numerator and denominator as a {@code float}.
    355      */
    356     @Override
    357     public float floatValue() {
    358         float num = mNumerator;
    359         float den = mDenominator;
    360 
    361         return num / den;
    362     }
    363 
    364     /**
    365      * Returns the value of the specified number as a {@code int}.
    366      *
    367      * <p>{@link #isInfinite Finite} rationals are converted to an {@code int} value
    368      * by dividing the numerator by the denominator; conversion for non-finite values happens
    369      * identically to casting a floating point value to an {@code int}, in particular:
    370      *
    371      * <p>
    372      * <ul>
    373      * <li>Positive infinity saturates to the largest maximum integer
    374      * {@link Integer#MAX_VALUE}</li>
    375      * <li>Negative infinity saturates to the smallest maximum integer
    376      * {@link Integer#MIN_VALUE}</li>
    377      * <li><em>Not-A-Number (NaN)</em> returns {@code 0}.</li>
    378      * </ul>
    379      * </p>
    380      *
    381      * @return the divided value of the numerator and denominator as a {@code int}.
    382      */
    383     @Override
    384     public int intValue() {
    385         // Mimic float to int conversion rules from JLS 5.1.3
    386 
    387         if (isPosInf()) {
    388             return Integer.MAX_VALUE;
    389         } else if (isNegInf()) {
    390             return Integer.MIN_VALUE;
    391         } else if (isNaN()) {
    392             return 0;
    393         } else { // finite
    394             return mNumerator / mDenominator;
    395         }
    396     }
    397 
    398     /**
    399      * Returns the value of the specified number as a {@code long}.
    400      *
    401      * <p>{@link #isInfinite Finite} rationals are converted to an {@code long} value
    402      * by dividing the numerator by the denominator; conversion for non-finite values happens
    403      * identically to casting a floating point value to a {@code long}, in particular:
    404      *
    405      * <p>
    406      * <ul>
    407      * <li>Positive infinity saturates to the largest maximum long
    408      * {@link Long#MAX_VALUE}</li>
    409      * <li>Negative infinity saturates to the smallest maximum long
    410      * {@link Long#MIN_VALUE}</li>
    411      * <li><em>Not-A-Number (NaN)</em> returns {@code 0}.</li>
    412      * </ul>
    413      * </p>
    414      *
    415      * @return the divided value of the numerator and denominator as a {@code long}.
    416      */
    417     @Override
    418     public long longValue() {
    419         // Mimic float to long conversion rules from JLS 5.1.3
    420 
    421         if (isPosInf()) {
    422             return Long.MAX_VALUE;
    423         } else if (isNegInf()) {
    424             return Long.MIN_VALUE;
    425         } else if (isNaN()) {
    426             return 0;
    427         } else { // finite
    428             return mNumerator / mDenominator;
    429         }
    430     }
    431 
    432     /**
    433      * Returns the value of the specified number as a {@code short}.
    434      *
    435      * <p>{@link #isInfinite Finite} rationals are converted to a {@code short} value
    436      * identically to {@link #intValue}; the {@code int} result is then truncated to a
    437      * {@code short} before returning the value.</p>
    438      *
    439      * @return the divided value of the numerator and denominator as a {@code short}.
    440      */
    441     @Override
    442     public short shortValue() {
    443         return (short) intValue();
    444     }
    445 
    446     /**
    447      * Compare this rational to the specified rational to determine their natural order.
    448      *
    449      * <p>{@link #NaN} is considered to be equal to itself and greater than all other
    450      * {@code Rational} values. Otherwise, if the objects are not {@link #equals equal}, then
    451      * the following rules apply:</p>
    452      *
    453      * <ul>
    454      * <li>Positive infinity is greater than any other finite number (or negative infinity)
    455      * <li>Negative infinity is less than any other finite number (or positive infinity)
    456      * <li>The finite number represented by this rational is checked numerically
    457      * against the other finite number by converting both rationals to a common denominator multiple
    458      * and comparing their numerators.
    459      * </ul>
    460      *
    461      * @param another the rational to be compared
    462      *
    463      * @return a negative integer, zero, or a positive integer as this object is less than,
    464      *         equal to, or greater than the specified rational.
    465      *
    466      * @throws NullPointerException if {@code another} was {@code null}
    467      */
    468     @Override
    469     public int compareTo(Rational another) {
    470         checkNotNull(another, "another must not be null");
    471 
    472         if (equals(another)) {
    473             return 0;
    474         } else if (isNaN()) { // NaN is greater than the other non-NaN value
    475             return 1;
    476         } else if (another.isNaN()) { // the other NaN is greater than this non-NaN value
    477             return -1;
    478         } else if (isPosInf() || another.isNegInf()) {
    479             return 1; // positive infinity is greater than any non-NaN/non-posInf value
    480         } else if (isNegInf() || another.isPosInf()) {
    481             return -1; // negative infinity is less than any non-NaN/non-negInf value
    482         }
    483 
    484         // else both this and another are finite numbers
    485 
    486         // make the denominators the same, then compare numerators
    487         long thisNumerator = ((long)mNumerator) * another.mDenominator; // long to avoid overflow
    488         long otherNumerator = ((long)another.mNumerator) * mDenominator; // long to avoid overflow
    489 
    490         // avoid underflow from subtraction by doing comparisons
    491         if (thisNumerator < otherNumerator) {
    492             return -1;
    493         } else if (thisNumerator > otherNumerator) {
    494             return 1;
    495         } else {
    496             // This should be covered by #equals, but have this code path just in case
    497             return 0;
    498         }
    499     }
    500 
    501     /*
    502      * Serializable implementation.
    503      *
    504      * The following methods are omitted:
    505      * >> writeObject - the default is sufficient (field by field serialization)
    506      * >> readObjectNoData - the default is sufficient (0s for both fields is a NaN)
    507      */
    508 
    509     /**
    510      * writeObject with default serialized form - guards against
    511      * deserializing non-reduced forms of the rational.
    512      *
    513      * @throws InvalidObjectException if the invariants were violated
    514      */
    515     private void readObject(java.io.ObjectInputStream in)
    516             throws IOException, ClassNotFoundException {
    517         in.defaultReadObject();
    518 
    519         /*
    520          * Guard against trying to deserialize illegal values (in this case, ones
    521          * that don't have a standard reduced form).
    522          *
    523          * - Non-finite values must be one of [0, 1], [0, 0], [0, 1], [0, -1]
    524          * - Finite values must always have their greatest common divisor as 1
    525          */
    526 
    527         if (mNumerator == 0) { // either zero or NaN
    528             if (mDenominator == 1 || mDenominator == 0) {
    529                 return;
    530             }
    531             throw new InvalidObjectException(
    532                     "Rational must be deserialized from a reduced form for zero values");
    533         } else if (mDenominator == 0) { // either positive or negative infinity
    534             if (mNumerator == 1 || mNumerator == -1) {
    535                 return;
    536             }
    537             throw new InvalidObjectException(
    538                     "Rational must be deserialized from a reduced form for infinity values");
    539         } else { // finite value
    540             if (gcd(mNumerator, mDenominator) > 1) {
    541                 throw new InvalidObjectException(
    542                         "Rational must be deserialized from a reduced form for finite values");
    543             }
    544         }
    545     }
    546 
    547     private static NumberFormatException invalidRational(String s) {
    548         throw new NumberFormatException("Invalid Rational: \"" + s + "\"");
    549     }
    550 
    551     /**
    552      * Parses the specified string as a rational value.
    553      * <p>The ASCII characters {@code \}{@code u003a} (':') and
    554      * {@code \}{@code u002f} ('/') are recognized as separators between
    555      * the numerator and denumerator.</p>
    556      * <p>
    557      * For any {@code Rational r}: {@code Rational.parseRational(r.toString()).equals(r)}.
    558      * However, the method also handles rational numbers expressed in the
    559      * following forms:</p>
    560      * <p>
    561      * "<i>num</i>{@code /}<i>den</i>" or
    562      * "<i>num</i>{@code :}<i>den</i>" {@code => new Rational(num, den);},
    563      * where <i>num</i> and <i>den</i> are string integers potentially
    564      * containing a sign, such as "-10", "+7" or "5".</p>
    565      *
    566      * <pre>{@code
    567      * Rational.parseRational("3:+6").equals(new Rational(1, 2)) == true
    568      * Rational.parseRational("-3/-6").equals(new Rational(1, 2)) == true
    569      * Rational.parseRational("4.56") => throws NumberFormatException
    570      * }</pre>
    571      *
    572      * @param string the string representation of a rational value.
    573      * @return the rational value represented by {@code string}.
    574      *
    575      * @throws NumberFormatException if {@code string} cannot be parsed
    576      * as a rational value.
    577      * @throws NullPointerException if {@code string} was {@code null}
    578      */
    579     public static Rational parseRational(String string)
    580             throws NumberFormatException {
    581         checkNotNull(string, "string must not be null");
    582 
    583         if (string.equals("NaN")) {
    584             return NaN;
    585         } else if (string.equals("Infinity")) {
    586             return POSITIVE_INFINITY;
    587         } else if (string.equals("-Infinity")) {
    588             return NEGATIVE_INFINITY;
    589         }
    590 
    591         int sep_ix = string.indexOf(':');
    592         if (sep_ix < 0) {
    593             sep_ix = string.indexOf('/');
    594         }
    595         if (sep_ix < 0) {
    596             throw invalidRational(string);
    597         }
    598         try {
    599             return new Rational(Integer.parseInt(string.substring(0, sep_ix)),
    600                     Integer.parseInt(string.substring(sep_ix + 1)));
    601         } catch (NumberFormatException e) {
    602             throw invalidRational(string);
    603         }
    604     }
    605 }
    606