blob: 39e8b14689da1fb496f0396b4b347e1627014331 [file] [log] [blame]
Igor Murashkinb519cc52013-07-02 11:23:44 -07001/*
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 */
Igor Murashkin72f9f0a2014-05-14 15:46:10 -070016package android.util;
Igor Murashkinb519cc52013-07-02 11:23:44 -070017
Igor Murashkin007bfb12014-06-05 18:02:22 -070018import static com.android.internal.util.Preconditions.*;
19
Mathew Inwood4eb56ab2018-08-14 17:24:32 +010020import android.annotation.UnsupportedAppUsage;
Igor Murashkin007bfb12014-06-05 18:02:22 -070021import java.io.IOException;
22import java.io.InvalidObjectException;
23
Igor Murashkinb519cc52013-07-02 11:23:44 -070024/**
Igor Murashkin72f9f0a2014-05-14 15:46:10 -070025 * <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>
Igor Murashkinb519cc52013-07-02 11:23:44 -070029 */
Igor Murashkin007bfb12014-06-05 18:02:22 -070030public 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 */
Mathew Inwood4eb56ab2018-08-14 17:24:32 +010078 @UnsupportedAppUsage
Igor Murashkinb519cc52013-07-02 11:23:44 -070079 private final int mNumerator;
Mathew Inwood4eb56ab2018-08-14 17:24:32 +010080 @UnsupportedAppUsage
Igor Murashkinb519cc52013-07-02 11:23:44 -070081 private final int mDenominator;
82
83 /**
Igor Murashkin007bfb12014-06-05 18:02:22 -070084 * <p>Create a {@code Rational} with a given numerator and denominator.</p>
Igor Murashkinb519cc52013-07-02 11:23:44 -070085 *
Timothy Knight23c88092013-08-21 14:33:40 -070086 * <p>The signs of the numerator and the denominator may be flipped such that the denominator
Igor Murashkin007bfb12014-06-05 18:02:22 -070087 * is always positive. Both the numerator and denominator will be converted to their reduced
88 * forms (see {@link #equals} for more details).</p>
Timothy Knight23c88092013-08-21 14:33:40 -070089 *
Igor Murashkin007bfb12014-06-05 18:02:22 -070090 * <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>
Igor Murashkinb519cc52013-07-02 11:23:44 -070098 *
99 * @param numerator the numerator of the rational
100 * @param denominator the denominator of the rational
Igor Murashkin007bfb12014-06-05 18:02:22 -0700101 *
102 * @see #equals
Igor Murashkinb519cc52013-07-02 11:23:44 -0700103 */
104 public Rational(int numerator, int denominator) {
105
Igor Murashkinb519cc52013-07-02 11:23:44 -0700106 if (denominator < 0) {
107 numerator = -numerator;
108 denominator = -denominator;
109 }
110
Igor Murashkin007bfb12014-06-05 18:02:22 -0700111 // 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 }
Igor Murashkinb519cc52013-07-02 11:23:44 -0700130 }
131
132 /**
133 * Gets the numerator of the rational.
Igor Murashkin007bfb12014-06-05 18:02:22 -0700134 *
135 * <p>The numerator will always return {@code 1} if this rational represents
136 * infinity (that is, the denominator is {@code 0}).</p>
Igor Murashkinb519cc52013-07-02 11:23:44 -0700137 */
138 public int getNumerator() {
139 return mNumerator;
140 }
141
142 /**
143 * Gets the denominator of the rational
Igor Murashkin007bfb12014-06-05 18:02:22 -0700144 *
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}.
Igor Murashkinb519cc52013-07-02 11:23:44 -0700150 */
151 public int getDenominator() {
152 return mDenominator;
153 }
154
Igor Murashkin007bfb12014-06-05 18:02:22 -0700155 /**
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() {
Timothy Knight23c88092013-08-21 14:33:40 -0700164 return mDenominator == 0 && mNumerator == 0;
165 }
166
Igor Murashkin007bfb12014-06-05 18:02:22 -0700167 /**
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() {
Timothy Knight23c88092013-08-21 14:33:40 -0700205 return mDenominator == 0 && mNumerator > 0;
206 }
207
208 private boolean isNegInf() {
209 return mDenominator == 0 && mNumerator < 0;
210 }
211
Igor Murashkinb519cc52013-07-02 11:23:44 -0700212 /**
213 * <p>Compare this Rational to another object and see if they are equal.</p>
214 *
Igor Murashkin007bfb12014-06-05 18:02:22 -0700215 * <p>A Rational object can only be equal to another Rational object (comparing against any
216 * other type will return {@code false}).</p>
Igor Murashkinb519cc52013-07-02 11:23:44 -0700217 *
Timothy Knight23c88092013-08-21 14:33:40 -0700218 * <p>A Rational object is considered equal to another Rational object if and only if one of
Igor Murashkin007bfb12014-06-05 18:02:22 -0700219 * the following holds:</p>
220 * <ul><li>Both are {@code NaN}</li>
Timothy Knight23c88092013-08-21 14:33:40 -0700221 * <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>
Igor Murashkinb519cc52013-07-02 11:23:44 -0700224 *
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 *
Igor Murashkin3c40a042014-04-22 15:05:50 -0700228 * <pre>{@code
Igor Murashkin007bfb12014-06-05 18:02:22 -0700229 * (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
Igor Murashkin3c40a042014-04-22 15:05:50 -0700235 * }</pre>
Igor Murashkinb519cc52013-07-02 11:23:44 -0700236 *
237 * @param obj a reference to another object
238 *
Benjamin Hendricks24eb8a32013-08-15 12:46:22 -0700239 * @return A boolean that determines whether or not the two Rational objects are equal.
Igor Murashkinb519cc52013-07-02 11:23:44 -0700240 */
241 @Override
242 public boolean equals(Object obj) {
Igor Murashkin007bfb12014-06-05 18:02:22 -0700243 return obj instanceof Rational && equals((Rational) obj);
Igor Murashkinb519cc52013-07-02 11:23:44 -0700244 }
245
Igor Murashkin007bfb12014-06-05 18:02:22 -0700246 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 */
Igor Murashkinb519cc52013-07-02 11:23:44 -0700263 @Override
264 public String toString() {
Timothy Knight23c88092013-08-21 14:33:40 -0700265 if (isNaN()) {
266 return "NaN";
Igor Murashkin007bfb12014-06-05 18:02:22 -0700267 } else if (isPosInf()) {
Timothy Knight23c88092013-08-21 14:33:40 -0700268 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() {
Igor Murashkin007bfb12014-06-05 18:02:22 -0700283 // TODO: remove this duplicate function (used in CTS and the shim)
284 return floatValue();
Igor Murashkinb519cc52013-07-02 11:23:44 -0700285 }
286
Igor Murashkin3c40a042014-04-22 15:05:50 -0700287 /**
288 * {@inheritDoc}
289 */
Igor Murashkinb519cc52013-07-02 11:23:44 -0700290 @Override
291 public int hashCode() {
Igor Murashkin3c40a042014-04-22 15:05:50 -0700292 // Bias the hash code for the first (2^16) values for both numerator and denominator
293 int numeratorFlipped = mNumerator << 16 | mNumerator >>> 16;
Igor Murashkinb519cc52013-07-02 11:23:44 -0700294
Igor Murashkin3c40a042014-04-22 15:05:50 -0700295 return mDenominator ^ numeratorFlipped;
Igor Murashkinb519cc52013-07-02 11:23:44 -0700296 }
297
298 /**
299 * Calculates the greatest common divisor using Euclid's algorithm.
300 *
Igor Murashkin007bfb12014-06-05 18:02:22 -0700301 * <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 *
Benjamin Hendricks24eb8a32013-08-15 12:46:22 -0700306 * @return An int value representing the gcd. Always positive.
Igor Murashkinb519cc52013-07-02 11:23:44 -0700307 * @hide
308 */
Igor Murashkin007bfb12014-06-05 18:02:22 -0700309 public static int gcd(int numerator, int denominator) {
310 /*
Igor Murashkinb519cc52013-07-02 11:23:44 -0700311 * Non-recursive implementation of Euclid's algorithm:
312 *
313 * gcd(a, 0) := a
314 * gcd(a, b) := gcd(b, a mod b)
315 *
316 */
Igor Murashkin007bfb12014-06-05 18:02:22 -0700317 int a = numerator;
318 int b = denominator;
Igor Murashkinb519cc52013-07-02 11:23:44 -0700319
320 while (b != 0) {
321 int oldB = b;
322
323 b = a % b;
324 a = oldB;
325 }
326
327 return Math.abs(a);
328 }
Igor Murashkin007bfb12014-06-05 18:02:22 -0700329
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(
Lajos Molnarb1a236b2014-07-11 00:54:50 -0700542 "Rational must be deserialized from a reduced form for finite values");
Igor Murashkin007bfb12014-06-05 18:02:22 -0700543 }
544 }
545 }
Lajos Molnarb1a236b2014-07-11 00:54:50 -0700546
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 }
Igor Murashkinb519cc52013-07-02 11:23:44 -0700605}