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