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