Skip to content

Commit

Permalink
CLDR-15954 Add test of unit preferences test (#3571)
Browse files Browse the repository at this point in the history
  • Loading branch information
macchiati authored Mar 19, 2024
1 parent 2c1c4a6 commit 0d85978
Show file tree
Hide file tree
Showing 10 changed files with 907 additions and 190 deletions.
42 changes: 42 additions & 0 deletions common/testData/units/unitLocalePreferencesTest.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Test data for unit locale preferences
# Copyright © 1991-2024 Unicode, Inc.
# For terms of use, see http://www.unicode.org/copyright.html
# SPDX-License-Identifier: Unicode-3.0
# CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
#
# Format:
# input-unit; amount; usage; languageTag; expected-unit; expected-amount # comment
#
# • The amounts are both rationals
# • The comment is optional (if it isn't present the # can be omitted)
#
# Use: Convert the Input amount & unit according to the Usage and Locale.
# The result should match the Expected amount and unit.
#
# The input and expected output units are unit identifers; in particular, the output does not have further processing:
# • no localization
#
fahrenheit; 1; default; en-u-rg-uszzzz-ms-ussystem-mu-celsius; celsius; -155/9 # mu > ms > rg > (likely) region
fahrenheit; 1; default; en-u-rg-uszzzz-ms-ussystem-mu-celsius; celsius; -155/9
fahrenheit; 1; default; en-u-rg-uszzzz-ms-metric; celsius; -155/9
fahrenheit; 1; default; en-u-rg-dezzzz; celsius; -155/9
fahrenheit; 1; default; en-DE; celsius; -155/9 # explicit region > likely region
fahrenheit; 1; default; en-US; fahrenheit; 1
fahrenheit; 1; default; en; fahrenheit; 1 # likely region = US
gallon-imperial; 2.5; fluid; en-u-rg-uszzzz-ms-metric; liter; 11.365225
gallon-imperial; 2.5; fluid; en-u-rg-dezzzz; liter; 11.365225
gallon-imperial; 2.5; fluid; en-DE; liter; 11.365225
gallon-imperial; 2.5; fluid; en-US-u-rg-uszzzz-ms-uksystem; gallon-imperial; 2.5 # ms-uksystem should behave like GB
gallon-imperial; 2.5; fluid; en-u-rg-gbzzzz; gallon-imperial; 2.5
gallon-imperial; 2.5; fluid; en-GB; gallon-imperial; 2.5
gallon-imperial; 2.5; fluid; en-u-rg-uszzzz-ms-ussystem; gallon; 1,420,653,125/473176473
gallon-imperial; 2.5; fluid; en-u-rg-uszzzz; gallon; 1,420,653,125/473176473
gallon-imperial; 2.5; fluid; en-US; gallon; 1,420,653,125/473176473
gallon-imperial; 2.5; fluid; en; gallon; 1,420,653,125/473176473 # likely region = US
ampere; 2.5; default; en; ampere; 2.5 # an input unit whose quantity has no preference data should get base units
pound-force-foot; 12,345; default; en; kilowatt-hour; 0.004649325714486427205
kilocandela; 1; default; en; candela; 1,000 # an input unit whose quantity has no preference data should get base units
candela-per-byte; 1; default; en; candela-per-bit; 0.125 # an input unit that has no quantity should get base units
candela-per-cubic-foot; 1; default; en; candela-per-cubic-meter; 1,953,125,000/55306341 # an input unit that has no quantity should get base units
foot; 1; default; de-u-mu-celsius; centimeter; 30.48 # a -mu unit that is not convertible from the input unit should get ignored
#pound; 28; default; en-u-mu-stone; stone; 2 # only temperature units are supported
2 changes: 1 addition & 1 deletion common/testData/units/unitPreferencesTest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# • no formatted with the skeleton
# • no suppression of zero values (for secondary -and- units such as pound in stone-and-pound)
#
# Generation: Set GENERATE_TESTS in TestUnits.java to regenerate unitPreferencesTest.txt.
# Generation: Use GenerateUnitTestData.java to regenerate unitPreferencesTest.txt.

area; default; 001; 1100000; 1100000.0; square-meter; 11/10; 1.1; square-kilometer
area; default; 001; 1000000; 1000000.0; square-meter; 1; 1.0; square-kilometer
Expand Down
2 changes: 1 addition & 1 deletion common/testData/units/unitsTest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# round to 4 decimal digits before comparing.
# Note that certain conversions are approximate, such as degrees to radians
#
# Generation: Set GENERATE_TESTS in TestUnits.java to regenerate unitsTest.txt.
# Generation: Use GenerateUnitTestData.java to regenerate unitsTest.txt.

acceleration ; meter-per-square-second ; meter-per-square-second ; 1 * x ; 1,000.00
acceleration ; g-force ; meter-per-square-second ; 9.80665 * x ; 9806.65
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,11 @@ public static Set<String> getGrammarLocales() {
"knot",
"dalton",
"kilocalorie",
"electronvolt");
"electronvolt",
// The following may be reinstated after 45.
"dot-per-centimeter",
"millimeter-ofhg",
"milligram-ofglucose-per-deciliter");

public static Set<String> getSpecialsToTranslate() {
return INCLUDE_OTHER;
Expand Down
149 changes: 97 additions & 52 deletions tools/cldr-code/src/main/java/org/unicode/cldr/util/Rational.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,39 @@
*
* @author markdavis
*/
public final class Rational implements Comparable<Rational> {
public final class Rational extends Number implements Comparable<Rational> {
private static final long serialVersionUID = 1L;
private static final Pattern INT_POWER_10 = Pattern.compile("10*");
public final BigInteger numerator;
public final BigInteger denominator;

static final BigInteger BI_TWO = BigInteger.valueOf(2);
static final BigInteger BI_FIVE = BigInteger.valueOf(5);
static final BigInteger BI_MINUS_ONE = BigInteger.valueOf(-1);
static final BigInteger BI_TEN = BigInteger.valueOf(10);

static final BigDecimal BD_TWO = BigDecimal.valueOf(2);
static final BigDecimal BD_FIVE = BigDecimal.valueOf(5);

// Constraints:
// always stored in normalized form.
// no common factor > 1 (reduced)
// denominator never negative
// if numerator is zero, denominator is 1 or 0
// if denominator is zero, numerator is 1, -1, or 0

public static final Rational ZERO = Rational.of(0);
public static final Rational ONE = Rational.of(1);
public static final Rational TWO = Rational.of(2);
public static final Rational NEGATIVE_ONE = ONE.negate();
// NOTE, the constructor doesn't do any checking, so everything other than these goes
// through Rational.of(...)
public static final Rational ZERO = new Rational(BigInteger.ZERO, BigInteger.ONE);
public static final Rational ONE = new Rational(BigInteger.ONE, BigInteger.ONE);
public static final Rational NaN = new Rational(BigInteger.ZERO, BigInteger.ZERO);
public static final Rational INFINITY = new Rational(BigInteger.ONE, BigInteger.ZERO);

public static final Rational INFINITY = Rational.of(1, 0);
public static final Rational NEGATIVE_ONE = ONE.negate();
public static final Rational NEGATIVE_INFINITY = INFINITY.negate();
public static final Rational NaN = Rational.of(0, 0);

public static final Rational TEN = Rational.of(10, 1);
public static final Rational TWO = new Rational(BI_TWO, BigInteger.ONE);
public static final Rational TEN = new Rational(BI_TEN, BigInteger.ONE);
public static final Rational TENTH = TEN.reciprocal();

public static final char REPTEND_MARKER = '˙';
Expand Down Expand Up @@ -202,18 +213,41 @@ public Map<String, Rational> getConstants() {
}

public static Rational of(long numerator, long denominator) {
return new Rational(BigInteger.valueOf(numerator), BigInteger.valueOf(denominator));
return Rational.of(BigInteger.valueOf(numerator), BigInteger.valueOf(denominator));
}

public static Rational of(long numerator) {
return new Rational(BigInteger.valueOf(numerator), BigInteger.ONE);
return Rational.of(BigInteger.valueOf(numerator), BigInteger.ONE);
}

public static Rational of(BigInteger numerator, BigInteger denominator) {
return new Rational(numerator, denominator);
int dComparison = denominator.compareTo(BigInteger.ZERO);
if (dComparison == 0) {
// catch equivalents to NaN, -INF, +INF
// 0/0 => NaN
// +/0 => INF
// -/0 => -INF
int nComparison = numerator.compareTo(BigInteger.ZERO);
return nComparison < 0 ? NEGATIVE_INFINITY : nComparison > 0 ? INFINITY : NaN;
} else {
// reduce to lowest form
BigInteger gcd = numerator.gcd(denominator);
if (gcd.compareTo(BigInteger.ONE) > 0) {
numerator = numerator.divide(gcd);
denominator = denominator.divide(gcd);
}
if (dComparison < 0) {
// ** NOTE: is already reduced, so safe to use constructor
return new Rational(numerator, denominator);
} else {
// ** NOTE: is already reduced, so safe to use constructor
return new Rational(numerator.negate(), denominator.negate());
}
}
}

public static Rational of(BigInteger numerator) {
// ** NOTE: is already reduced, so safe to use constructor
return new Rational(numerator, BigInteger.ONE);
}

Expand All @@ -236,56 +270,47 @@ private Rational(BigInteger numerator, BigInteger denominator) {
}

public Rational add(Rational other) {
BigInteger gcd_den = denominator.gcd(other.denominator);
return new Rational(
numerator
.multiply(other.denominator)
.divide(gcd_den)
.add(other.numerator.multiply(denominator).divide(gcd_den)),
denominator.multiply(other.denominator).divide(gcd_den));
BigInteger newNumerator =
numerator.multiply(other.denominator).add(other.numerator.multiply(denominator));
BigInteger newDenominator = denominator.multiply(other.denominator);
return Rational.of(newNumerator, newDenominator);
}

public Rational subtract(Rational other) {
BigInteger gcd_den = denominator.gcd(other.denominator);
return new Rational(
BigInteger newNumerator =
numerator
.multiply(other.denominator)
.divide(gcd_den)
.subtract(other.numerator.multiply(denominator).divide(gcd_den)),
denominator.multiply(other.denominator).divide(gcd_den));
.subtract(other.numerator.multiply(denominator));
BigInteger newDenominator = denominator.multiply(other.denominator);
return Rational.of(newNumerator, newDenominator);
}

public Rational multiply(Rational other) {
BigInteger gcd_num_oden = numerator.gcd(other.denominator);
boolean isZero = gcd_num_oden.equals(BigInteger.ZERO);
BigInteger smallNum = isZero ? numerator : numerator.divide(gcd_num_oden);
BigInteger smallODen = isZero ? other.denominator : other.denominator.divide(gcd_num_oden);

BigInteger gcd_den_onum = denominator.gcd(other.numerator);
isZero = gcd_den_onum.equals(BigInteger.ZERO);
BigInteger smallONum = isZero ? other.numerator : other.numerator.divide(gcd_den_onum);
BigInteger smallDen = isZero ? denominator : denominator.divide(gcd_den_onum);
BigInteger newNumerator = numerator.multiply(other.numerator);
BigInteger newDenominator = denominator.multiply(other.denominator);
return Rational.of(newNumerator, newDenominator);
}

return new Rational(smallNum.multiply(smallONum), smallDen.multiply(smallODen));
public Rational divide(Rational other) {
BigInteger newNumerator = numerator.multiply(other.denominator);
BigInteger newDenominator = denominator.multiply(other.numerator);
return Rational.of(newNumerator, newDenominator);
}

public Rational pow(int i) {
return new Rational(numerator.pow(i), denominator.pow(i));
return Rational.of(numerator.pow(i), denominator.pow(i));
}

public static Rational pow10(int i) {
return i > 0 ? TEN.pow(i) : TENTH.pow(-i);
}

public Rational divide(Rational other) {
return multiply(other.reciprocal());
}

public Rational reciprocal() {
return new Rational(denominator, numerator);
return Rational.of(denominator, numerator);
}

public Rational negate() {
// ** NOTE: is already reduced, so safe to use constructor
return new Rational(numerator.negate(), denominator);
}

Expand All @@ -298,17 +323,24 @@ public BigDecimal toBigDecimal(MathContext mathContext) {
}
}

public BigDecimal toBigDecimal() {
return toBigDecimal(MathContext.DECIMAL128); // prevent failures due to repeating fractions
}

@Override
public double doubleValue() {
if (denominator.equals(BigInteger.ZERO) && numerator.equals(BigInteger.ZERO)) {
return Double.NaN;
}
return new BigDecimal(numerator)
.divide(new BigDecimal(denominator), MathContext.DECIMAL64)
.doubleValue();
return toBigDecimal(MathContext.DECIMAL64).doubleValue();
}

public BigDecimal toBigDecimal() {
return toBigDecimal(MathContext.UNLIMITED);
@Override
public float floatValue() {
if (denominator.equals(BigInteger.ZERO) && numerator.equals(BigInteger.ZERO)) {
return Float.NaN;
}
return toBigDecimal(MathContext.DECIMAL32).floatValue();
}

public static Rational of(BigDecimal bigDecimal) {
Expand All @@ -320,16 +352,22 @@ public static Rational of(BigDecimal bigDecimal) {
final int scale = bigDecimal.scale();
final BigInteger unscaled = bigDecimal.unscaledValue();
if (scale == 0) {
// ** NOTE: is already reduced, so safe to use constructor
return new Rational(unscaled, BigInteger.ONE);
} else if (scale >= 0) {
return new Rational(unscaled, BigDecimal.ONE.movePointRight(scale).toBigInteger());
return Rational.of(unscaled, BigDecimal.ONE.movePointRight(scale).toBigInteger());
} else {
// ** NOTE: is already reduced, so safe to use constructor
return new Rational(
unscaled.multiply(BigDecimal.ONE.movePointLeft(scale).toBigInteger()),
BigInteger.ONE);
}
}

public static Rational of(double doubleValue) {
return of(new BigDecimal(doubleValue));
}

public enum FormatStyle {
/**
* Simple numerator / denominator, plain BigInteger.toString(), dropping " / 1". <br>
Expand Down Expand Up @@ -496,13 +534,6 @@ public Rational abs() {
return numerator.signum() >= 0 ? this : this.negate();
}

static final BigInteger BI_TWO = BigInteger.valueOf(2);
static final BigInteger BI_FIVE = BigInteger.valueOf(5);
static final BigInteger BI_MINUS_ONE = BigInteger.valueOf(-1);

static final BigDecimal BD_TWO = BigDecimal.valueOf(2);
static final BigDecimal BD_FIVE = BigDecimal.valueOf(5);

/**
* Goal is to be able to display rationals in a short but exact form, like 1,234,567/3 or
* 1.234567E21/3. To do this, find the smallest denominator (excluding powers of 2 and 5), and
Expand Down Expand Up @@ -728,4 +759,18 @@ public boolean approximatelyEquals(Rational b, Rational epsilon) {
public boolean approximatelyEquals(Rational b) {
return approximatelyEquals(b, EPSILON);
}

public boolean approximatelyEquals(Number b) {
return approximatelyEquals(Rational.of(b.doubleValue()), EPSILON);
}

@Override
public int intValue() {
return toBigDecimal().intValue();
}

@Override
public long longValue() {
return toBigDecimal().longValue();
}
}
Loading

0 comments on commit 0d85978

Please sign in to comment.