summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/util/Range.java30
-rw-r--r--core/java/android/util/Rational.java457
2 files changed, 422 insertions, 65 deletions
diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java
index d7e8cf0..3907e77 100644
--- a/core/java/android/util/Range.java
+++ b/core/java/android/util/Range.java
@@ -97,6 +97,27 @@ public final class Range<T extends Comparable<? super T>> {
}
/**
+ * Checks if the {@code value} is within the bounds of this range.
+ *
+ * <p>A value is considered to be within this range if it's {@code >=} then
+ * the lower endpoint <i>and</i> {@code <=} to the upper endpoint (using the {@link Comparable}
+ * interface.</p>
+ *
+ * @param value a non-{@code null} {@code T} reference
+ * @return {@code true} if the value is within this inclusive range, {@code false} otherwise
+ *
+ * @throws NullPointerException if {@code value} was {@code null}
+ */
+ public boolean inRange(T value) {
+ checkNotNull(value, "value must not be null");
+
+ boolean gteLower = value.compareTo(mLower) >= 0;
+ boolean lteUpper = value.compareTo(mUpper) <= 0;
+
+ return gteLower && lteUpper;
+ }
+
+ /**
* Compare two ranges for equality.
*
* <p>A range is considered equal if and only if both the lower and upper endpoints
@@ -105,16 +126,13 @@ public final class Range<T extends Comparable<? super T>> {
* @return {@code true} if the ranges are equal, {@code false} otherwise
*/
@Override
- public boolean equals(final Object obj) {
+ public boolean equals(Object obj) {
if (obj == null) {
return false;
- }
- if (this == obj) {
+ } else if (this == obj) {
return true;
- }
- if (obj instanceof Range) {
+ } else if (obj instanceof Range) {
@SuppressWarnings("rawtypes")
- final
Range other = (Range) obj;
return mLower.equals(other.mLower) && mUpper.equals(other.mUpper);
}
diff --git a/core/java/android/util/Rational.java b/core/java/android/util/Rational.java
index 8d4c67f..9952859 100644
--- a/core/java/android/util/Rational.java
+++ b/core/java/android/util/Rational.java
@@ -15,29 +15,88 @@
*/
package android.util;
+import static com.android.internal.util.Preconditions.*;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+
/**
* <p>An immutable data type representation a rational number.</p>
*
* <p>Contains a pair of {@code int}s representing the numerator and denominator of a
* Rational number. </p>
*/
-public final class Rational {
+public final class Rational extends Number implements Comparable<Rational> {
+ /**
+ * Constant for the <em>Not-a-Number (NaN)</em> value of the {@code Rational} type.
+ *
+ * <p>A {@code NaN} value is considered to be equal to itself (that is {@code NaN.equals(NaN)}
+ * will return {@code true}; it is always greater than any non-{@code NaN} value (that is
+ * {@code NaN.compareTo(notNaN)} will return a number greater than {@code 0}).</p>
+ *
+ * <p>Equivalent to constructing a new rational with both the numerator and denominator
+ * equal to {@code 0}.</p>
+ */
+ public static final Rational NaN = new Rational(0, 0);
+
+ /**
+ * Constant for the positive infinity value of the {@code Rational} type.
+ *
+ * <p>Equivalent to constructing a new rational with a positive numerator and a denominator
+ * equal to {@code 0}.</p>
+ */
+ public static final Rational POSITIVE_INFINITY = new Rational(1, 0);
+
+ /**
+ * Constant for the negative infinity value of the {@code Rational} type.
+ *
+ * <p>Equivalent to constructing a new rational with a negative numerator and a denominator
+ * equal to {@code 0}.</p>
+ */
+ public static final Rational NEGATIVE_INFINITY = new Rational(-1, 0);
+
+ /**
+ * Constant for the zero value of the {@code Rational} type.
+ *
+ * <p>Equivalent to constructing a new rational with a numerator equal to {@code 0} and
+ * any non-zero denominator.</p>
+ */
+ public static final Rational ZERO = new Rational(0, 1);
+
+ /**
+ * Unique version number per class to be compliant with {@link java.io.Serializable}.
+ *
+ * <p>Increment each time the fields change in any way.</p>
+ */
+ private static final long serialVersionUID = 1L;
+
+ /*
+ * Do not change the order of these fields or add new instance fields to maintain the
+ * Serializable compatibility across API revisions.
+ */
private final int mNumerator;
private final int mDenominator;
/**
- * <p>Create a Rational with a given numerator and denominator.</p>
+ * <p>Create a {@code Rational} with a given numerator and denominator.</p>
*
* <p>The signs of the numerator and the denominator may be flipped such that the denominator
- * is always positive.</p>
+ * is always positive. Both the numerator and denominator will be converted to their reduced
+ * forms (see {@link #equals} for more details).</p>
*
- * <p>A rational value with a 0-denominator may be constructed, but will have similar semantics
- * as float {@code NaN} and {@code INF} values. For {@code NaN},
- * both {@link #getNumerator} and {@link #getDenominator} functions will return 0. For
- * positive or negative {@code INF}, only the {@link #getDenominator} will return 0.</p>
+ * <p>For example,
+ * <ul>
+ * <li>a rational of {@code 2/4} will be reduced to {@code 1/2}.
+ * <li>a rational of {@code 1/-1} will be flipped to {@code -1/1}
+ * <li>a rational of {@code 5/0} will be reduced to {@code 1/0}
+ * <li>a rational of {@code 0/5} will be reduced to {@code 0/1}
+ * </ul>
+ * </p>
*
* @param numerator the numerator of the rational
* @param denominator the denominator of the rational
+ *
+ * @see #equals
*/
public Rational(int numerator, int denominator) {
@@ -46,32 +105,100 @@ public final class Rational {
denominator = -denominator;
}
- mNumerator = numerator;
- mDenominator = denominator;
+ // Convert to reduced form
+ if (denominator == 0 && numerator > 0) {
+ mNumerator = 1; // +Inf
+ mDenominator = 0;
+ } else if (denominator == 0 && numerator < 0) {
+ mNumerator = -1; // -Inf
+ mDenominator = 0;
+ } else if (denominator == 0 && numerator == 0) {
+ mNumerator = 0; // NaN
+ mDenominator = 0;
+ } else if (numerator == 0) {
+ mNumerator = 0;
+ mDenominator = 1;
+ } else {
+ int gcd = gcd(numerator, denominator);
+
+ mNumerator = numerator / gcd;
+ mDenominator = denominator / gcd;
+ }
}
/**
* Gets the numerator of the rational.
+ *
+ * <p>The numerator will always return {@code 1} if this rational represents
+ * infinity (that is, the denominator is {@code 0}).</p>
*/
public int getNumerator() {
- if (mDenominator == 0) {
- return 0;
- }
return mNumerator;
}
/**
* Gets the denominator of the rational
+ *
+ * <p>The denominator may return {@code 0}, in which case the rational may represent
+ * positive infinity (if the numerator was positive), negative infinity (if the numerator
+ * was negative), or {@code NaN} (if the numerator was {@code 0}).</p>
+ *
+ * <p>The denominator will always return {@code 1} if the numerator is {@code 0}.
*/
public int getDenominator() {
return mDenominator;
}
- private boolean isNaN() {
+ /**
+ * Indicates whether this rational is a <em>Not-a-Number (NaN)</em> value.
+ *
+ * <p>A {@code NaN} value occurs when both the numerator and the denominator are {@code 0}.</p>
+ *
+ * @return {@code true} if this rational is a <em>Not-a-Number (NaN)</em> value;
+ * {@code false} if this is a (potentially infinite) number value
+ */
+ public boolean isNaN() {
return mDenominator == 0 && mNumerator == 0;
}
- private boolean isInf() {
+ /**
+ * Indicates whether this rational represents an infinite value.
+ *
+ * <p>An infinite value occurs when the denominator is {@code 0} (but the numerator is not).</p>
+ *
+ * @return {@code true} if this rational is a (positive or negative) infinite value;
+ * {@code false} if this is a finite number value (or {@code NaN})
+ */
+ public boolean isInfinite() {
+ return mNumerator != 0 && mDenominator == 0;
+ }
+
+ /**
+ * Indicates whether this rational represents a finite value.
+ *
+ * <p>A finite value occurs when the denominator is not {@code 0}; in other words
+ * the rational is neither infinity or {@code NaN}.</p>
+ *
+ * @return {@code true} if this rational is a (positive or negative) infinite value;
+ * {@code false} if this is a finite number value (or {@code NaN})
+ */
+ public boolean isFinite() {
+ return mDenominator != 0;
+ }
+
+ /**
+ * Indicates whether this rational represents a zero value.
+ *
+ * <p>A zero value is a {@link #isFinite finite} rational with a numerator of {@code 0}.</p>
+ *
+ * @return {@code true} if this rational is finite zero value;
+ * {@code false} otherwise
+ */
+ public boolean isZero() {
+ return isFinite() && mNumerator == 0;
+ }
+
+ private boolean isPosInf() {
return mDenominator == 0 && mNumerator > 0;
}
@@ -82,12 +209,12 @@ public final class Rational {
/**
* <p>Compare this Rational to another object and see if they are equal.</p>
*
- * <p>A Rational object can only be equal to another Rational object (comparing against any other
- * type will return false).</p>
+ * <p>A Rational object can only be equal to another Rational object (comparing against any
+ * other type will return {@code false}).</p>
*
* <p>A Rational object is considered equal to another Rational object if and only if one of
- * the following holds</p>:
- * <ul><li>Both are NaN</li>
+ * the following holds:</p>
+ * <ul><li>Both are {@code NaN}</li>
* <li>Both are infinities of the same sign</li>
* <li>Both have the same numerator and denominator in their reduced form</li>
* </ul>
@@ -96,12 +223,12 @@ public final class Rational {
* denominator by their greatest common divisor.</p>
*
* <pre>{@code
- * (new Rational(1, 2)).equals(new Rational(1, 2)) == true // trivially true
- * (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false
- * (new Rational(1, 2)).equals(new Rational(2, 4)) == true // true after reduction
- * (new Rational(0, 0)).equals(new Rational(0, 0)) == true // NaN.equals(NaN)
- * (new Rational(1, 0)).equals(new Rational(5, 0)) == true // both are +infinity
- * (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity
+ * (new Rational(1, 2)).equals(new Rational(1, 2)) == true // trivially true
+ * (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false
+ * (new Rational(1, 2)).equals(new Rational(2, 4)) == true // true after reduction
+ * (new Rational(0, 0)).equals(new Rational(0, 0)) == true // NaN.equals(NaN)
+ * (new Rational(1, 0)).equals(new Rational(5, 0)) == true // both are +infinity
+ * (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity
* }</pre>
*
* @param obj a reference to another object
@@ -110,41 +237,31 @@ public final class Rational {
*/
@Override
public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- } else if (obj instanceof Rational) {
- Rational other = (Rational) obj;
- if (mDenominator == 0 || other.mDenominator == 0) {
- if (isNaN() && other.isNaN()) {
- return true;
- } else if (isInf() && other.isInf() || isNegInf() && other.isNegInf()) {
- return true;
- } else {
- return false;
- }
- } else if (mNumerator == other.mNumerator && mDenominator == other.mDenominator) {
- return true;
- } else {
- int thisGcd = gcd();
- int otherGcd = other.gcd();
-
- int thisNumerator = mNumerator / thisGcd;
- int thisDenominator = mDenominator / thisGcd;
-
- int otherNumerator = other.mNumerator / otherGcd;
- int otherDenominator = other.mDenominator / otherGcd;
-
- return (thisNumerator == otherNumerator && thisDenominator == otherDenominator);
- }
- }
- return false;
+ return obj instanceof Rational && equals((Rational) obj);
+ }
+
+ private boolean equals(Rational other) {
+ return (mNumerator == other.mNumerator && mDenominator == other.mDenominator);
}
+ /**
+ * Return a string representation of this rational, e.g. {@code "1/2"}.
+ *
+ * <p>The following rules of conversion apply:
+ * <ul>
+ * <li>{@code NaN} values will return {@code "NaN"}
+ * <li>Positive infinity values will return {@code "Infinity"}
+ * <li>Negative infinity values will return {@code "-Infinity"}
+ * <li>All other values will return {@code "numerator/denominator"} where {@code numerator}
+ * and {@code denominator} are substituted with the appropriate numerator and denominator
+ * values.
+ * </ul></p>
+ */
@Override
public String toString() {
if (isNaN()) {
return "NaN";
- } else if (isInf()) {
+ } else if (isPosInf()) {
return "Infinity";
} else if (isNegInf()) {
return "-Infinity";
@@ -160,7 +277,8 @@ public final class Rational {
* @hide
*/
public float toFloat() {
- return (float) mNumerator / (float) mDenominator;
+ // TODO: remove this duplicate function (used in CTS and the shim)
+ return floatValue();
}
/**
@@ -177,20 +295,24 @@ public final class Rational {
/**
* Calculates the greatest common divisor using Euclid's algorithm.
*
+ * <p><em>Visible for testing only.</em></p>
+ *
+ * @param numerator the numerator in a fraction
+ * @param denominator the denominator in a fraction
+ *
* @return An int value representing the gcd. Always positive.
* @hide
*/
- public int gcd() {
- /**
+ public static int gcd(int numerator, int denominator) {
+ /*
* Non-recursive implementation of Euclid's algorithm:
*
* gcd(a, 0) := a
* gcd(a, b) := gcd(b, a mod b)
*
*/
-
- int a = mNumerator;
- int b = mDenominator;
+ int a = numerator;
+ int b = denominator;
while (b != 0) {
int oldB = b;
@@ -201,4 +323,221 @@ public final class Rational {
return Math.abs(a);
}
+
+ /**
+ * Returns the value of the specified number as a {@code double}.
+ *
+ * <p>The {@code double} is calculated by converting both the numerator and denominator
+ * to a {@code double}; then returning the result of dividing the numerator by the
+ * denominator.</p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code double}.
+ */
+ @Override
+ public double doubleValue() {
+ double num = mNumerator;
+ double den = mDenominator;
+
+ return num / den;
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code float}.
+ *
+ * <p>The {@code float} is calculated by converting both the numerator and denominator
+ * to a {@code float}; then returning the result of dividing the numerator by the
+ * denominator.</p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code float}.
+ */
+ @Override
+ public float floatValue() {
+ float num = mNumerator;
+ float den = mDenominator;
+
+ return num / den;
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code int}.
+ *
+ * <p>{@link #isInfinite Finite} rationals are converted to an {@code int} value
+ * by dividing the numerator by the denominator; conversion for non-finite values happens
+ * identically to casting a floating point value to an {@code int}, in particular:
+ *
+ * <p>
+ * <ul>
+ * <li>Positive infinity saturates to the largest maximum integer
+ * {@link Integer#MAX_VALUE}</li>
+ * <li>Negative infinity saturates to the smallest maximum integer
+ * {@link Integer#MIN_VALUE}</li>
+ * <li><em>Not-A-Number (NaN)</em> returns {@code 0}.</li>
+ * </ul>
+ * </p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code int}.
+ */
+ @Override
+ public int intValue() {
+ // Mimic float to int conversion rules from JLS 5.1.3
+
+ if (isPosInf()) {
+ return Integer.MAX_VALUE;
+ } else if (isNegInf()) {
+ return Integer.MIN_VALUE;
+ } else if (isNaN()) {
+ return 0;
+ } else { // finite
+ return mNumerator / mDenominator;
+ }
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code long}.
+ *
+ * <p>{@link #isInfinite Finite} rationals are converted to an {@code long} value
+ * by dividing the numerator by the denominator; conversion for non-finite values happens
+ * identically to casting a floating point value to a {@code long}, in particular:
+ *
+ * <p>
+ * <ul>
+ * <li>Positive infinity saturates to the largest maximum long
+ * {@link Long#MAX_VALUE}</li>
+ * <li>Negative infinity saturates to the smallest maximum long
+ * {@link Long#MIN_VALUE}</li>
+ * <li><em>Not-A-Number (NaN)</em> returns {@code 0}.</li>
+ * </ul>
+ * </p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code long}.
+ */
+ @Override
+ public long longValue() {
+ // Mimic float to long conversion rules from JLS 5.1.3
+
+ if (isPosInf()) {
+ return Long.MAX_VALUE;
+ } else if (isNegInf()) {
+ return Long.MIN_VALUE;
+ } else if (isNaN()) {
+ return 0;
+ } else { // finite
+ return mNumerator / mDenominator;
+ }
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code short}.
+ *
+ * <p>{@link #isInfinite Finite} rationals are converted to a {@code short} value
+ * identically to {@link #intValue}; the {@code int} result is then truncated to a
+ * {@code short} before returning the value.</p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code short}.
+ */
+ @Override
+ public short shortValue() {
+ return (short) intValue();
+ }
+
+ /**
+ * Compare this rational to the specified rational to determine their natural order.
+ *
+ * <p>{@link #NaN} is considered to be equal to itself and greater than all other
+ * {@code Rational} values. Otherwise, if the objects are not {@link #equals equal}, then
+ * the following rules apply:</p>
+ *
+ * <ul>
+ * <li>Positive infinity is greater than any other finite number (or negative infinity)
+ * <li>Negative infinity is less than any other finite number (or positive infinity)
+ * <li>The finite number represented by this rational is checked numerically
+ * against the other finite number by converting both rationals to a common denominator multiple
+ * and comparing their numerators.
+ * </ul>
+ *
+ * @param another the rational to be compared
+ *
+ * @return a negative integer, zero, or a positive integer as this object is less than,
+ * equal to, or greater than the specified rational.
+ *
+ * @throws NullPointerException if {@code another} was {@code null}
+ */
+ @Override
+ public int compareTo(Rational another) {
+ checkNotNull(another, "another must not be null");
+
+ if (equals(another)) {
+ return 0;
+ } else if (isNaN()) { // NaN is greater than the other non-NaN value
+ return 1;
+ } else if (another.isNaN()) { // the other NaN is greater than this non-NaN value
+ return -1;
+ } else if (isPosInf() || another.isNegInf()) {
+ return 1; // positive infinity is greater than any non-NaN/non-posInf value
+ } else if (isNegInf() || another.isPosInf()) {
+ return -1; // negative infinity is less than any non-NaN/non-negInf value
+ }
+
+ // else both this and another are finite numbers
+
+ // make the denominators the same, then compare numerators
+ long thisNumerator = ((long)mNumerator) * another.mDenominator; // long to avoid overflow
+ long otherNumerator = ((long)another.mNumerator) * mDenominator; // long to avoid overflow
+
+ // avoid underflow from subtraction by doing comparisons
+ if (thisNumerator < otherNumerator) {
+ return -1;
+ } else if (thisNumerator > otherNumerator) {
+ return 1;
+ } else {
+ // This should be covered by #equals, but have this code path just in case
+ return 0;
+ }
+ }
+
+ /*
+ * Serializable implementation.
+ *
+ * The following methods are omitted:
+ * >> writeObject - the default is sufficient (field by field serialization)
+ * >> readObjectNoData - the default is sufficient (0s for both fields is a NaN)
+ */
+
+ /**
+ * writeObject with default serialized form - guards against
+ * deserializing non-reduced forms of the rational.
+ *
+ * @throws InvalidObjectException if the invariants were violated
+ */
+ private void readObject(java.io.ObjectInputStream in)
+ throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+
+ /*
+ * Guard against trying to deserialize illegal values (in this case, ones
+ * that don't have a standard reduced form).
+ *
+ * - Non-finite values must be one of [0, 1], [0, 0], [0, 1], [0, -1]
+ * - Finite values must always have their greatest common divisor as 1
+ */
+
+ if (mNumerator == 0) { // either zero or NaN
+ if (mDenominator == 1 || mDenominator == 0) {
+ return;
+ }
+ throw new InvalidObjectException(
+ "Rational must be deserialized from a reduced form for zero values");
+ } else if (mDenominator == 0) { // either positive or negative infinity
+ if (mNumerator == 1 || mNumerator == -1) {
+ return;
+ }
+ throw new InvalidObjectException(
+ "Rational must be deserialized from a reduced form for infinity values");
+ } else { // finite value
+ if (gcd(mNumerator, mDenominator) > 1) {
+ throw new InvalidObjectException(
+ "Rational must be deserialized from a reduced form for finite values");
+ }
+ }
+ }
}