util: Make Rational a Number/Comparable; add Range#inRange
* Also changes Rational to reduce the numerator/denominator by its greatest common divisor at construction time (e.g. (2/4 -> 1/2)). Bug: 15432042 Change-Id: Ib827abccf44a040667e5931cf9442afc86b57e2d
This commit is contained in:
@@ -30533,12 +30533,26 @@ package android.util {
|
||||
method public static android.util.Range<T> create(T, T);
|
||||
method public T getLower();
|
||||
method public T getUpper();
|
||||
method public boolean inRange(T);
|
||||
}
|
||||
|
||||
public final class Rational {
|
||||
public final class Rational extends java.lang.Number implements java.lang.Comparable {
|
||||
ctor public Rational(int, int);
|
||||
method public int compareTo(android.util.Rational);
|
||||
method public double doubleValue();
|
||||
method public float floatValue();
|
||||
method public int getDenominator();
|
||||
method public int getNumerator();
|
||||
method public int intValue();
|
||||
method public boolean isFinite();
|
||||
method public boolean isInfinite();
|
||||
method public boolean isNaN();
|
||||
method public boolean isZero();
|
||||
method public long longValue();
|
||||
field public static final android.util.Rational NEGATIVE_INFINITY;
|
||||
field public static final android.util.Rational NaN;
|
||||
field public static final android.util.Rational POSITIVE_INFINITY;
|
||||
field public static final android.util.Rational ZERO;
|
||||
}
|
||||
|
||||
public final class Size {
|
||||
|
||||
@@ -96,6 +96,27 @@ public final class Range<T extends Comparable<? super T>> {
|
||||
return mUpper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.mediaframeworktest.unit;
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Range;
|
||||
import android.util.Rational;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* adb shell am instrument \
|
||||
* -e class 'com.android.mediaframeworktest.unit.RangeTest' \
|
||||
* -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
|
||||
* </pre>
|
||||
*/
|
||||
public class RangeTest extends junit.framework.TestCase {
|
||||
|
||||
@SmallTest
|
||||
public void testConstructor() {
|
||||
// Trivial, same range
|
||||
Range<Integer> intRange = new Range<Integer>(1, 1);
|
||||
|
||||
assertLower(intRange, 1);
|
||||
assertUpper(intRange, 1);
|
||||
|
||||
// Different values in range
|
||||
Range<Integer> intRange2 = new Range<Integer>(100, 200);
|
||||
assertLower(intRange2, 100);
|
||||
assertUpper(intRange2, 200);
|
||||
|
||||
Range<Float> floatRange = new Range<Float>(Float.NEGATIVE_INFINITY,
|
||||
Float.POSITIVE_INFINITY);
|
||||
assertLower(floatRange, Float.NEGATIVE_INFINITY);
|
||||
assertUpper(floatRange, Float.POSITIVE_INFINITY);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testIllegalValues() {
|
||||
// Test NPEs
|
||||
try {
|
||||
new Range<Integer>(null, null);
|
||||
fail("Expected exception to be thrown for (null, null)");
|
||||
} catch (NullPointerException e) {
|
||||
// OK: both args are null
|
||||
}
|
||||
|
||||
try {
|
||||
new Range<Integer>(null, 0);
|
||||
fail("Expected exception to be thrown for (null, 0)");
|
||||
} catch (NullPointerException e) {
|
||||
// OK: left arg is null
|
||||
}
|
||||
|
||||
try {
|
||||
new Range<Integer>(0, null);
|
||||
fail("Expected exception to be thrown for (0, null)");
|
||||
} catch (NullPointerException e) {
|
||||
// OK: right arg is null
|
||||
}
|
||||
|
||||
// Test IAEs
|
||||
|
||||
try {
|
||||
new Range<Integer>(50, -50);
|
||||
fail("Expected exception to be thrown for (50, -50)");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// OK: 50 > -50 so it fails
|
||||
}
|
||||
|
||||
try {
|
||||
new Range<Float>(0.0f, Float.NEGATIVE_INFINITY);
|
||||
fail("Expected exception to be thrown for (0.0f, -Infinity)");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// OK: 0.0f is > NEGATIVE_INFINITY, so it fails
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testEquals() {
|
||||
Range<Float> oneHalf = Range.create(1.0f, 2.0f);
|
||||
Range<Float> oneHalf2 = new Range<Float>(1.0f, 2.0f);
|
||||
assertEquals(oneHalf, oneHalf2);
|
||||
assertHashCodeEquals(oneHalf, oneHalf2);
|
||||
|
||||
Range<Float> twoThirds = new Range<Float>(2.0f, 3.0f);
|
||||
Range<Float> twoThirds2 = Range.create(2.0f, 3.0f);
|
||||
assertEquals(twoThirds, twoThirds2);
|
||||
assertHashCodeEquals(twoThirds, twoThirds2);
|
||||
|
||||
Range<Rational> negativeOneTenthPositiveOneTenth =
|
||||
new Range<Rational>(new Rational(-1, 10), new Rational(1, 10));
|
||||
Range<Rational> negativeOneTenthPositiveOneTenth2 =
|
||||
Range.create(new Rational(-1, 10), new Rational(1, 10));
|
||||
assertEquals(negativeOneTenthPositiveOneTenth, negativeOneTenthPositiveOneTenth2);
|
||||
assertHashCodeEquals(negativeOneTenthPositiveOneTenth, negativeOneTenthPositiveOneTenth2);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testInRange() {
|
||||
Range<Integer> hundredOneTwo = Range.create(100, 200);
|
||||
|
||||
assertInRange(hundredOneTwo, 100);
|
||||
assertInRange(hundredOneTwo, 200);
|
||||
assertInRange(hundredOneTwo, 150);
|
||||
assertOutOfRange(hundredOneTwo, 99);
|
||||
assertOutOfRange(hundredOneTwo, 201);
|
||||
assertOutOfRange(hundredOneTwo, 100000);
|
||||
|
||||
Range<Float> infinities = Range.create(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY);
|
||||
|
||||
assertInRange(infinities, Float.NEGATIVE_INFINITY);
|
||||
assertInRange(infinities, Float.POSITIVE_INFINITY);
|
||||
assertInRange(infinities, 0.0f);
|
||||
assertOutOfRange(infinities, Float.NaN);
|
||||
|
||||
Range<Rational> negativeOneTenthPositiveOneTenth =
|
||||
new Range<Rational>(new Rational(-1, 10), new Rational(1, 10));
|
||||
assertInRange(negativeOneTenthPositiveOneTenth, new Rational(-1, 10));
|
||||
assertInRange(negativeOneTenthPositiveOneTenth, new Rational(1, 10));
|
||||
assertInRange(negativeOneTenthPositiveOneTenth, Rational.ZERO);
|
||||
assertOutOfRange(negativeOneTenthPositiveOneTenth, new Rational(-100, 1));
|
||||
assertOutOfRange(negativeOneTenthPositiveOneTenth, new Rational(100, 1));
|
||||
}
|
||||
|
||||
private static <T extends Comparable<? super T>> void assertInRange(Range<T> object, T needle) {
|
||||
assertAction("in-range", object, needle, true, object.inRange(needle));
|
||||
}
|
||||
|
||||
private static <T extends Comparable<? super T>> void assertOutOfRange(Range<T> object,
|
||||
T needle) {
|
||||
assertAction("out-of-range", object, needle, false, object.inRange(needle));
|
||||
}
|
||||
|
||||
private static <T extends Comparable<? super T>> void assertUpper(Range<T> object, T expected) {
|
||||
assertAction("upper", object, expected, object.getUpper());
|
||||
}
|
||||
|
||||
private static <T extends Comparable<? super T>> void assertLower(Range<T> object, T expected) {
|
||||
assertAction("lower", object, expected, object.getLower());
|
||||
}
|
||||
|
||||
private static <T, T2> void assertAction(String action, T object, T2 expected,
|
||||
T2 actual) {
|
||||
assertEquals("Expected " + object + " " + action + " to be ",
|
||||
expected, actual);
|
||||
}
|
||||
|
||||
private static <T, T2> void assertAction(String action, T object, T2 needle, boolean expected,
|
||||
boolean actual) {
|
||||
String expectedMessage = expected ? action : ("not " + action);
|
||||
assertEquals("Expected " + needle + " to be " + expectedMessage + " of " + object,
|
||||
expected, actual);
|
||||
}
|
||||
|
||||
private static <T extends Comparable<? super T>> void assertHashCodeEquals(
|
||||
Range<T> left, Range<T> right) {
|
||||
assertEquals("Left hash code for " + left +
|
||||
" expected to be equal to right hash code for " + right,
|
||||
left.hashCode(), right.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,17 @@ package com.android.mediaframeworktest.unit;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Rational;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static android.util.Rational.*;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* adb shell am instrument \
|
||||
@@ -27,6 +38,22 @@ import android.util.Rational;
|
||||
* </pre>
|
||||
*/
|
||||
public class RationalTest extends junit.framework.TestCase {
|
||||
|
||||
/** (1,1) */
|
||||
private static final Rational UNIT = new Rational(1, 1);
|
||||
|
||||
/**
|
||||
* Test @hide greatest common divisior functionality that cannot be tested in CTS.
|
||||
*/
|
||||
@SmallTest
|
||||
public void testGcd() {
|
||||
assertEquals(1, Rational.gcd(1, 2));
|
||||
assertEquals(1, Rational.gcd(2, 3));
|
||||
assertEquals(78, Rational.gcd(5*78, 7*78));
|
||||
assertEquals(1, Rational.gcd(-1, 2));
|
||||
assertEquals(1, Rational.gcd(-2, 3));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testConstructor() {
|
||||
|
||||
@@ -52,12 +79,12 @@ public class RationalTest extends junit.framework.TestCase {
|
||||
|
||||
// Infinity.
|
||||
r = new Rational(1, 0);
|
||||
assertEquals(0, r.getNumerator());
|
||||
assertEquals(1, r.getNumerator());
|
||||
assertEquals(0, r.getDenominator());
|
||||
|
||||
// Negative infinity.
|
||||
r = new Rational(-1, 0);
|
||||
assertEquals(0, r.getNumerator());
|
||||
assertEquals(-1, r.getNumerator());
|
||||
assertEquals(0, r.getDenominator());
|
||||
|
||||
// NaN.
|
||||
@@ -66,24 +93,6 @@ public class RationalTest extends junit.framework.TestCase {
|
||||
assertEquals(0, r.getDenominator());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testGcd() {
|
||||
Rational r = new Rational(1, 2);
|
||||
assertEquals(1, r.gcd());
|
||||
|
||||
Rational twoThirds = new Rational(2, 3);
|
||||
assertEquals(1, twoThirds.gcd());
|
||||
|
||||
Rational moreComplicated2 = new Rational(5*78, 7*78);
|
||||
assertEquals(78, moreComplicated2.gcd());
|
||||
|
||||
Rational oneHalf = new Rational(-1, 2);
|
||||
assertEquals(1, oneHalf.gcd());
|
||||
|
||||
twoThirds = new Rational(-2, 3);
|
||||
assertEquals(1, twoThirds.gcd());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testEquals() {
|
||||
Rational r = new Rational(1, 2);
|
||||
@@ -118,7 +127,13 @@ public class RationalTest extends junit.framework.TestCase {
|
||||
assertEquals(moreComplicated, moreComplicated2);
|
||||
assertEquals(moreComplicated2, moreComplicated);
|
||||
|
||||
Rational nan = new Rational(0, 0);
|
||||
// Zero is always equal to itself
|
||||
Rational zero2 = new Rational(0, 100);
|
||||
assertEquals(ZERO, zero2);
|
||||
assertEquals(zero2, ZERO);
|
||||
|
||||
// NaN is always equal to itself
|
||||
Rational nan = NaN;
|
||||
Rational nan2 = new Rational(0, 0);
|
||||
assertTrue(nan.equals(nan));
|
||||
assertTrue(nan.equals(nan2));
|
||||
@@ -127,9 +142,9 @@ public class RationalTest extends junit.framework.TestCase {
|
||||
assertFalse(r.equals(nan));
|
||||
|
||||
// Infinities of the same sign are equal.
|
||||
Rational posInf = new Rational(1, 0);
|
||||
Rational posInf = POSITIVE_INFINITY;
|
||||
Rational posInf2 = new Rational(2, 0);
|
||||
Rational negInf = new Rational(-1, 0);
|
||||
Rational negInf = NEGATIVE_INFINITY;
|
||||
Rational negInf2 = new Rational(-2, 0);
|
||||
assertEquals(posInf, posInf);
|
||||
assertEquals(negInf, negInf);
|
||||
@@ -148,4 +163,349 @@ public class RationalTest extends junit.framework.TestCase {
|
||||
assertFalse(nan.equals(posInf));
|
||||
assertFalse(nan.equals(negInf));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testReduction() {
|
||||
Rational moreComplicated = new Rational(5 * 78, 7 * 78);
|
||||
assertEquals(new Rational(5, 7), moreComplicated);
|
||||
assertEquals(5, moreComplicated.getNumerator());
|
||||
assertEquals(7, moreComplicated.getDenominator());
|
||||
|
||||
Rational posInf = new Rational(5, 0);
|
||||
assertEquals(1, posInf.getNumerator());
|
||||
assertEquals(0, posInf.getDenominator());
|
||||
assertEquals(POSITIVE_INFINITY, posInf);
|
||||
|
||||
Rational negInf = new Rational(-100, 0);
|
||||
assertEquals(-1, negInf.getNumerator());
|
||||
assertEquals(0, negInf.getDenominator());
|
||||
assertEquals(NEGATIVE_INFINITY, negInf);
|
||||
|
||||
Rational zero = new Rational(0, -100);
|
||||
assertEquals(0, zero.getNumerator());
|
||||
assertEquals(1, zero.getDenominator());
|
||||
assertEquals(ZERO, zero);
|
||||
|
||||
Rational flipSigns = new Rational(1, -1);
|
||||
assertEquals(-1, flipSigns.getNumerator());
|
||||
assertEquals(1, flipSigns.getDenominator());
|
||||
|
||||
Rational flipAndReduce = new Rational(100, -200);
|
||||
assertEquals(-1, flipAndReduce.getNumerator());
|
||||
assertEquals(2, flipAndReduce.getDenominator());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testCompareTo() {
|
||||
// unit is equal to itself
|
||||
assertCompareEquals(UNIT, new Rational(1, 1));
|
||||
|
||||
// NaN is greater than anything but NaN
|
||||
assertCompareEquals(NaN, new Rational(0, 0));
|
||||
assertGreaterThan(NaN, UNIT);
|
||||
assertGreaterThan(NaN, POSITIVE_INFINITY);
|
||||
assertGreaterThan(NaN, NEGATIVE_INFINITY);
|
||||
assertGreaterThan(NaN, ZERO);
|
||||
|
||||
// Positive infinity is greater than any other non-NaN
|
||||
assertCompareEquals(POSITIVE_INFINITY, new Rational(1, 0));
|
||||
assertGreaterThan(POSITIVE_INFINITY, UNIT);
|
||||
assertGreaterThan(POSITIVE_INFINITY, NEGATIVE_INFINITY);
|
||||
assertGreaterThan(POSITIVE_INFINITY, ZERO);
|
||||
|
||||
// Negative infinity is smaller than any other non-NaN
|
||||
assertCompareEquals(NEGATIVE_INFINITY, new Rational(-1, 0));
|
||||
assertLessThan(NEGATIVE_INFINITY, UNIT);
|
||||
assertLessThan(NEGATIVE_INFINITY, POSITIVE_INFINITY);
|
||||
assertLessThan(NEGATIVE_INFINITY, ZERO);
|
||||
|
||||
// A finite number with the same denominator is trivially comparable
|
||||
assertGreaterThan(new Rational(3, 100), new Rational(1, 100));
|
||||
assertGreaterThan(new Rational(3, 100), ZERO);
|
||||
|
||||
// Compare finite numbers with different divisors
|
||||
assertGreaterThan(new Rational(5, 25), new Rational(1, 10));
|
||||
assertGreaterThan(new Rational(5, 25), ZERO);
|
||||
|
||||
// Compare finite numbers with different signs
|
||||
assertGreaterThan(new Rational(5, 25), new Rational(-1, 10));
|
||||
assertLessThan(new Rational(-5, 25), ZERO);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testConvenienceMethods() {
|
||||
// isFinite
|
||||
assertFinite(ZERO, true);
|
||||
assertFinite(NaN, false);
|
||||
assertFinite(NEGATIVE_INFINITY, false);
|
||||
assertFinite(POSITIVE_INFINITY, false);
|
||||
assertFinite(UNIT, true);
|
||||
|
||||
// isInfinite
|
||||
assertInfinite(ZERO, false);
|
||||
assertInfinite(NaN, false);
|
||||
assertInfinite(NEGATIVE_INFINITY, true);
|
||||
assertInfinite(POSITIVE_INFINITY, true);
|
||||
assertInfinite(UNIT, false);
|
||||
|
||||
// isNaN
|
||||
assertNaN(ZERO, false);
|
||||
assertNaN(NaN, true);
|
||||
assertNaN(NEGATIVE_INFINITY, false);
|
||||
assertNaN(POSITIVE_INFINITY, false);
|
||||
assertNaN(UNIT, false);
|
||||
|
||||
// isZero
|
||||
assertZero(ZERO, true);
|
||||
assertZero(NaN, false);
|
||||
assertZero(NEGATIVE_INFINITY, false);
|
||||
assertZero(POSITIVE_INFINITY, false);
|
||||
assertZero(UNIT, false);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testValueConversions() {
|
||||
// Unit, simple case
|
||||
assertValueEquals(UNIT, 1.0f);
|
||||
assertValueEquals(UNIT, 1.0);
|
||||
assertValueEquals(UNIT, 1L);
|
||||
assertValueEquals(UNIT, 1);
|
||||
assertValueEquals(UNIT, (short)1);
|
||||
|
||||
// Zero, simple case
|
||||
assertValueEquals(ZERO, 0.0f);
|
||||
assertValueEquals(ZERO, 0.0);
|
||||
assertValueEquals(ZERO, 0L);
|
||||
assertValueEquals(ZERO, 0);
|
||||
assertValueEquals(ZERO, (short)0);
|
||||
|
||||
// NaN is 0 for integers, not-a-number for floating point
|
||||
assertValueEquals(NaN, Float.NaN);
|
||||
assertValueEquals(NaN, Double.NaN);
|
||||
assertValueEquals(NaN, 0L);
|
||||
assertValueEquals(NaN, 0);
|
||||
assertValueEquals(NaN, (short)0);
|
||||
|
||||
// Positive infinity, saturates upwards for integers
|
||||
assertValueEquals(POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
|
||||
assertValueEquals(POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
|
||||
assertValueEquals(POSITIVE_INFINITY, Long.MAX_VALUE);
|
||||
assertValueEquals(POSITIVE_INFINITY, Integer.MAX_VALUE);
|
||||
assertValueEquals(POSITIVE_INFINITY, (short)-1);
|
||||
|
||||
// Negative infinity, saturates downwards for integers
|
||||
assertValueEquals(NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
|
||||
assertValueEquals(NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
|
||||
assertValueEquals(NEGATIVE_INFINITY, Long.MIN_VALUE);
|
||||
assertValueEquals(NEGATIVE_INFINITY, Integer.MIN_VALUE);
|
||||
assertValueEquals(NEGATIVE_INFINITY, (short)0);
|
||||
|
||||
// Normal finite values, round down for integers
|
||||
final Rational oneQuarter = new Rational(1, 4);
|
||||
assertValueEquals(oneQuarter, 1.0f / 4.0f);
|
||||
assertValueEquals(oneQuarter, 1.0 / 4.0);
|
||||
assertValueEquals(oneQuarter, 0L);
|
||||
assertValueEquals(oneQuarter, 0);
|
||||
assertValueEquals(oneQuarter, (short)0);
|
||||
|
||||
final Rational nineFifths = new Rational(9, 5);
|
||||
assertValueEquals(nineFifths, 9.0f / 5.0f);
|
||||
assertValueEquals(nineFifths, 9.0 / 5.0);
|
||||
assertValueEquals(nineFifths, 1L);
|
||||
assertValueEquals(nineFifths, 1);
|
||||
assertValueEquals(nineFifths, (short)1);
|
||||
|
||||
final Rational negativeHundred = new Rational(-1000, 10);
|
||||
assertValueEquals(negativeHundred, -100.f / 1.f);
|
||||
assertValueEquals(negativeHundred, -100.0 / 1.0);
|
||||
assertValueEquals(negativeHundred, -100L);
|
||||
assertValueEquals(negativeHundred, -100);
|
||||
assertValueEquals(negativeHundred, (short)-100);
|
||||
|
||||
// Short truncates if the result is too large
|
||||
assertValueEquals(new Rational(Integer.MAX_VALUE, 1), (short)Integer.MAX_VALUE);
|
||||
assertValueEquals(new Rational(0x00FFFFFF, 1), (short)0x00FFFFFF);
|
||||
assertValueEquals(new Rational(0x00FF00FF, 1), (short)0x00FF00FF);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testSerialize() throws ClassNotFoundException, IOException {
|
||||
/*
|
||||
* Check correct [de]serialization
|
||||
*/
|
||||
assertEqualsAfterSerializing(ZERO);
|
||||
assertEqualsAfterSerializing(NaN);
|
||||
assertEqualsAfterSerializing(NEGATIVE_INFINITY);
|
||||
assertEqualsAfterSerializing(POSITIVE_INFINITY);
|
||||
assertEqualsAfterSerializing(UNIT);
|
||||
assertEqualsAfterSerializing(new Rational(100, 200));
|
||||
assertEqualsAfterSerializing(new Rational(-100, 200));
|
||||
assertEqualsAfterSerializing(new Rational(5, 1));
|
||||
assertEqualsAfterSerializing(new Rational(Integer.MAX_VALUE, Integer.MIN_VALUE));
|
||||
|
||||
/*
|
||||
* Check bad deserialization fails
|
||||
*/
|
||||
try {
|
||||
Rational badZero = createIllegalRational(0, 100); // [0, 100] , should be [0, 1]
|
||||
Rational results = serializeRoundTrip(badZero);
|
||||
fail("Deserializing " + results + " should not have succeeded");
|
||||
} catch (InvalidObjectException e) {
|
||||
// OK
|
||||
}
|
||||
|
||||
try {
|
||||
Rational badPosInfinity = createIllegalRational(100, 0); // [100, 0] , should be [1, 0]
|
||||
Rational results = serializeRoundTrip(badPosInfinity);
|
||||
fail("Deserializing " + results + " should not have succeeded");
|
||||
} catch (InvalidObjectException e) {
|
||||
// OK
|
||||
}
|
||||
|
||||
try {
|
||||
Rational badNegInfinity =
|
||||
createIllegalRational(-100, 0); // [-100, 0] , should be [-1, 0]
|
||||
Rational results = serializeRoundTrip(badNegInfinity);
|
||||
fail("Deserializing " + results + " should not have succeeded");
|
||||
} catch (InvalidObjectException e) {
|
||||
// OK
|
||||
}
|
||||
|
||||
try {
|
||||
Rational badReduced = createIllegalRational(2, 4); // [2,4] , should be [1, 2]
|
||||
Rational results = serializeRoundTrip(badReduced);
|
||||
fail("Deserializing " + results + " should not have succeeded");
|
||||
} catch (InvalidObjectException e) {
|
||||
// OK
|
||||
}
|
||||
|
||||
try {
|
||||
Rational badReducedNeg = createIllegalRational(-2, 4); // [-2, 4] should be [-1, 2]
|
||||
Rational results = serializeRoundTrip(badReducedNeg);
|
||||
fail("Deserializing " + results + " should not have succeeded");
|
||||
} catch (InvalidObjectException e) {
|
||||
// OK
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertValueEquals(Rational object, float expected) {
|
||||
assertEquals("Checking floatValue() for " + object + ";",
|
||||
expected, object.floatValue());
|
||||
}
|
||||
|
||||
private static void assertValueEquals(Rational object, double expected) {
|
||||
assertEquals("Checking doubleValue() for " + object + ";",
|
||||
expected, object.doubleValue());
|
||||
}
|
||||
|
||||
private static void assertValueEquals(Rational object, long expected) {
|
||||
assertEquals("Checking longValue() for " + object + ";",
|
||||
expected, object.longValue());
|
||||
}
|
||||
|
||||
private static void assertValueEquals(Rational object, int expected) {
|
||||
assertEquals("Checking intValue() for " + object + ";",
|
||||
expected, object.intValue());
|
||||
}
|
||||
|
||||
private static void assertValueEquals(Rational object, short expected) {
|
||||
assertEquals("Checking shortValue() for " + object + ";",
|
||||
expected, object.shortValue());
|
||||
}
|
||||
|
||||
private static void assertFinite(Rational object, boolean expected) {
|
||||
assertAction("finite", object, expected, object.isFinite());
|
||||
}
|
||||
|
||||
private static void assertInfinite(Rational object, boolean expected) {
|
||||
assertAction("infinite", object, expected, object.isInfinite());
|
||||
}
|
||||
|
||||
private static void assertNaN(Rational object, boolean expected) {
|
||||
assertAction("NaN", object, expected, object.isNaN());
|
||||
}
|
||||
|
||||
private static void assertZero(Rational object, boolean expected) {
|
||||
assertAction("zero", object, expected, object.isZero());
|
||||
}
|
||||
|
||||
private static <T> void assertAction(String action, T object, boolean expected,
|
||||
boolean actual) {
|
||||
String expectedMessage = expected ? action : ("not " + action);
|
||||
assertEquals("Expected " + object + " to be " + expectedMessage,
|
||||
expected, actual);
|
||||
}
|
||||
|
||||
private static <T extends Comparable<? super T>> void assertLessThan(T left, T right) {
|
||||
assertTrue("Expected (LR) left " + left + " to be less than right " + right,
|
||||
left.compareTo(right) < 0);
|
||||
assertTrue("Expected (RL) left " + left + " to be less than right " + right,
|
||||
right.compareTo(left) > 0);
|
||||
}
|
||||
|
||||
private static <T extends Comparable<? super T>> void assertGreaterThan(T left, T right) {
|
||||
assertTrue("Expected (LR) left " + left + " to be greater than right " + right,
|
||||
left.compareTo(right) > 0);
|
||||
assertTrue("Expected (RL) left " + left + " to be greater than right " + right,
|
||||
right.compareTo(left) < 0);
|
||||
}
|
||||
|
||||
private static <T extends Comparable<? super T>> void assertCompareEquals(T left, T right) {
|
||||
assertTrue("Expected (LR) left " + left + " to be compareEquals to right " + right,
|
||||
left.compareTo(right) == 0);
|
||||
assertTrue("Expected (RL) left " + left + " to be compareEquals to right " + right,
|
||||
right.compareTo(left) == 0);
|
||||
}
|
||||
|
||||
private static <T extends Serializable> byte[] serialize(T obj) throws IOException {
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
try (ObjectOutputStream objectStream = new ObjectOutputStream(byteStream)) {
|
||||
objectStream.writeObject(obj);
|
||||
}
|
||||
return byteStream.toByteArray();
|
||||
}
|
||||
|
||||
private static <T extends Serializable> T deserialize(byte[] array, Class<T> klass)
|
||||
throws IOException, ClassNotFoundException {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(array);
|
||||
ObjectInputStream ois = new ObjectInputStream(bais);
|
||||
Object obj = ois.readObject();
|
||||
return klass.cast(obj);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Serializable> T serializeRoundTrip(T obj)
|
||||
throws IOException, ClassNotFoundException {
|
||||
Class<T> klass = (Class<T>) obj.getClass();
|
||||
byte[] arr = serialize(obj);
|
||||
T serialized = deserialize(arr, klass);
|
||||
return serialized;
|
||||
}
|
||||
|
||||
private static <T extends Serializable> void assertEqualsAfterSerializing(T obj)
|
||||
throws ClassNotFoundException, IOException {
|
||||
T serialized = serializeRoundTrip(obj);
|
||||
assertEquals("Expected values to be equal after serialization round-trip", obj, serialized);
|
||||
}
|
||||
|
||||
private static Rational createIllegalRational(int numerator, int denominator) {
|
||||
Rational r = new Rational(numerator, denominator);
|
||||
mutateField(r, "mNumerator", numerator);
|
||||
mutateField(r, "mDenominator", denominator);
|
||||
return r;
|
||||
}
|
||||
|
||||
private static <T> void mutateField(T object, String name, int value) {
|
||||
try {
|
||||
Field f = object.getClass().getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
f.set(object, value);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user