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