mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-06 22:15:31 +00:00
ICU-21270 Support exponent in FixedDecimal and samples in C++
This commit is contained in:
parent
c4fa504fec
commit
6700602974
5 changed files with 394 additions and 118 deletions
|
@ -378,9 +378,23 @@ static double scaleForInt(double d) {
|
|||
return scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for the overrides of getSamples() for double and FixedDecimal
|
||||
* return value types. Provide only one of an allocated array of doubles or
|
||||
* FixedDecimals, and a nullptr for the other.
|
||||
*/
|
||||
static int32_t
|
||||
getSamplesFromString(const UnicodeString &samples, double *dest,
|
||||
int32_t destCapacity, UErrorCode& status) {
|
||||
getSamplesFromString(const UnicodeString &samples, double *destDbl,
|
||||
FixedDecimal* destFd, int32_t destCapacity,
|
||||
UErrorCode& status) {
|
||||
|
||||
if ((destDbl == nullptr && destFd == nullptr)
|
||||
|| (destDbl != nullptr && destFd != nullptr)) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isDouble = destDbl != nullptr;
|
||||
int32_t sampleCount = 0;
|
||||
int32_t sampleStartIdx = 0;
|
||||
int32_t sampleEndIdx = 0;
|
||||
|
@ -398,9 +412,13 @@ getSamplesFromString(const UnicodeString &samples, double *dest,
|
|||
int32_t tildeIndex = sampleRange.indexOf(TILDE);
|
||||
if (tildeIndex < 0) {
|
||||
FixedDecimal fixed(sampleRange, status);
|
||||
double sampleValue = fixed.source;
|
||||
if (fixed.visibleDecimalDigitCount == 0 || sampleValue != floor(sampleValue)) {
|
||||
dest[sampleCount++] = sampleValue;
|
||||
if (isDouble) {
|
||||
double sampleValue = fixed.source;
|
||||
if (fixed.visibleDecimalDigitCount == 0 || sampleValue != floor(sampleValue)) {
|
||||
destDbl[sampleCount++] = sampleValue;
|
||||
}
|
||||
} else {
|
||||
destFd[sampleCount++] = fixed;
|
||||
}
|
||||
} else {
|
||||
|
||||
|
@ -427,14 +445,21 @@ getSamplesFromString(const UnicodeString &samples, double *dest,
|
|||
rangeLo *= scale;
|
||||
rangeHi *= scale;
|
||||
for (double n=rangeLo; n<=rangeHi; n+=1) {
|
||||
// Hack Alert: don't return any decimal samples with integer values that
|
||||
// originated from a format with trailing decimals.
|
||||
// This API is returning doubles, which can't distinguish having displayed
|
||||
// zeros to the right of the decimal.
|
||||
// This results in test failures with values mapping back to a different keyword.
|
||||
double sampleValue = n/scale;
|
||||
if (!(sampleValue == floor(sampleValue) && fixedLo.visibleDecimalDigitCount > 0)) {
|
||||
dest[sampleCount++] = sampleValue;
|
||||
if (isDouble) {
|
||||
// Hack Alert: don't return any decimal samples with integer values that
|
||||
// originated from a format with trailing decimals.
|
||||
// This API is returning doubles, which can't distinguish having displayed
|
||||
// zeros to the right of the decimal.
|
||||
// This results in test failures with values mapping back to a different keyword.
|
||||
if (!(sampleValue == floor(sampleValue) && fixedLo.visibleDecimalDigitCount > 0)) {
|
||||
destDbl[sampleCount++] = sampleValue;
|
||||
}
|
||||
} else {
|
||||
int32_t v = (int32_t) fixedLo.getPluralOperand(PluralOperand::PLURAL_OPERAND_V);
|
||||
int32_t e = (int32_t) fixedLo.getPluralOperand(PluralOperand::PLURAL_OPERAND_E);
|
||||
FixedDecimal newSample = FixedDecimal::createWithExponent(sampleValue, v, e);
|
||||
destFd[sampleCount++] = newSample;
|
||||
}
|
||||
if (sampleCount >= destCapacity) {
|
||||
break;
|
||||
|
@ -446,24 +471,52 @@ getSamplesFromString(const UnicodeString &samples, double *dest,
|
|||
return sampleCount;
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
PluralRules::getSamples(const UnicodeString &keyword, double *dest,
|
||||
int32_t destCapacity, UErrorCode& status) {
|
||||
if (destCapacity == 0 || U_FAILURE(status)) {
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
if (U_FAILURE(mInternalStatus)) {
|
||||
status = mInternalStatus;
|
||||
return 0;
|
||||
}
|
||||
if (dest != nullptr ? destCapacity < 0 : destCapacity != 0) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
RuleChain *rc = rulesForKeyword(keyword);
|
||||
if (rc == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, dest, destCapacity, status);
|
||||
int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, dest, nullptr, destCapacity, status);
|
||||
if (numSamples == 0) {
|
||||
numSamples = getSamplesFromString(rc->fDecimalSamples, dest, destCapacity, status);
|
||||
numSamples = getSamplesFromString(rc->fDecimalSamples, dest, nullptr, destCapacity, status);
|
||||
}
|
||||
return numSamples;
|
||||
}
|
||||
|
||||
int32_t
|
||||
PluralRules::getSamples(const UnicodeString &keyword, FixedDecimal *dest,
|
||||
int32_t destCapacity, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
if (U_FAILURE(mInternalStatus)) {
|
||||
status = mInternalStatus;
|
||||
return 0;
|
||||
}
|
||||
if (dest != nullptr ? destCapacity < 0 : destCapacity != 0) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
RuleChain *rc = rulesForKeyword(keyword);
|
||||
if (rc == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, nullptr, dest, destCapacity, status);
|
||||
if (numSamples == 0) {
|
||||
numSamples = getSamplesFromString(rc->fDecimalSamples, nullptr, dest, destCapacity, status);
|
||||
}
|
||||
return numSamples;
|
||||
}
|
||||
|
@ -1548,8 +1601,8 @@ PluralOperand tokenTypeToPluralOperand(tokenType tt) {
|
|||
}
|
||||
}
|
||||
|
||||
FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) {
|
||||
init(n, v, f);
|
||||
FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f, int32_t e) {
|
||||
init(n, v, f, e);
|
||||
// check values. TODO make into unit test.
|
||||
//
|
||||
// long visiblePower = (int) Math.pow(10, v);
|
||||
|
@ -1565,6 +1618,10 @@ FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) {
|
|||
// }
|
||||
}
|
||||
|
||||
FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) {
|
||||
init(n, v, f);
|
||||
}
|
||||
|
||||
FixedDecimal::FixedDecimal(double n, int32_t v) {
|
||||
// Ugly, but for samples we don't care.
|
||||
init(n, v, getFractionalDigits(n, v));
|
||||
|
@ -1584,20 +1641,36 @@ FixedDecimal::FixedDecimal() {
|
|||
|
||||
FixedDecimal::FixedDecimal(const UnicodeString &num, UErrorCode &status) {
|
||||
CharString cs;
|
||||
cs.appendInvariantChars(num, status);
|
||||
int32_t parsedExponent = 0;
|
||||
|
||||
int32_t exponentIdx = num.indexOf(u'e');
|
||||
if (exponentIdx < 0) {
|
||||
exponentIdx = num.indexOf(u'E');
|
||||
}
|
||||
if (exponentIdx >= 0) {
|
||||
cs.appendInvariantChars(num.tempSubString(0, exponentIdx), status);
|
||||
int32_t expSubstrStart = exponentIdx + 1;
|
||||
parsedExponent = ICU_Utility::parseAsciiInteger(num, expSubstrStart);
|
||||
}
|
||||
else {
|
||||
cs.appendInvariantChars(num, status);
|
||||
}
|
||||
|
||||
DecimalQuantity dl;
|
||||
dl.setToDecNumber(cs.toStringPiece(), status);
|
||||
if (U_FAILURE(status)) {
|
||||
init(0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t decimalPoint = num.indexOf(DOT);
|
||||
double n = dl.toDouble();
|
||||
if (decimalPoint == -1) {
|
||||
init(n, 0, 0);
|
||||
init(n, 0, 0, parsedExponent);
|
||||
} else {
|
||||
int32_t v = num.length() - decimalPoint - 1;
|
||||
init(n, v, getFractionalDigits(n, v));
|
||||
int32_t fractionNumLength = exponentIdx < 0 ? num.length() : cs.length();
|
||||
int32_t v = fractionNumLength - decimalPoint - 1;
|
||||
init(n, v, getFractionalDigits(n, v), parsedExponent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1608,6 +1681,7 @@ FixedDecimal::FixedDecimal(const FixedDecimal &other) {
|
|||
decimalDigits = other.decimalDigits;
|
||||
decimalDigitsWithoutTrailingZeros = other.decimalDigitsWithoutTrailingZeros;
|
||||
intValue = other.intValue;
|
||||
exponent = other.exponent;
|
||||
_hasIntegerValue = other._hasIntegerValue;
|
||||
isNegative = other.isNegative;
|
||||
_isNaN = other._isNaN;
|
||||
|
@ -1616,6 +1690,10 @@ FixedDecimal::FixedDecimal(const FixedDecimal &other) {
|
|||
|
||||
FixedDecimal::~FixedDecimal() = default;
|
||||
|
||||
FixedDecimal FixedDecimal::createWithExponent(double n, int32_t v, int32_t e) {
|
||||
return FixedDecimal(n, v, getFractionalDigits(n, v), e);
|
||||
}
|
||||
|
||||
|
||||
void FixedDecimal::init(double n) {
|
||||
int32_t numFractionDigits = decimals(n);
|
||||
|
@ -1624,10 +1702,17 @@ void FixedDecimal::init(double n) {
|
|||
|
||||
|
||||
void FixedDecimal::init(double n, int32_t v, int64_t f) {
|
||||
int32_t exponent = 0;
|
||||
init(n, v, f, exponent);
|
||||
}
|
||||
|
||||
|
||||
void FixedDecimal::init(double n, int32_t v, int64_t f, int32_t e) {
|
||||
isNegative = n < 0.0;
|
||||
source = fabs(n);
|
||||
_isNaN = uprv_isNaN(source);
|
||||
_isInfinite = uprv_isInfinite(source);
|
||||
exponent = e;
|
||||
if (_isNaN || _isInfinite) {
|
||||
v = 0;
|
||||
f = 0;
|
||||
|
@ -1757,7 +1842,7 @@ double FixedDecimal::getPluralOperand(PluralOperand operand) const {
|
|||
case PLURAL_OPERAND_F: return static_cast<double>(decimalDigits);
|
||||
case PLURAL_OPERAND_T: return static_cast<double>(decimalDigitsWithoutTrailingZeros);
|
||||
case PLURAL_OPERAND_V: return visibleDecimalDigitCount;
|
||||
case PLURAL_OPERAND_E: return 0;
|
||||
case PLURAL_OPERAND_E: return exponent;
|
||||
default:
|
||||
UPRV_UNREACHABLE; // unexpected.
|
||||
}
|
||||
|
@ -1783,6 +1868,23 @@ int32_t FixedDecimal::getVisibleFractionDigitCount() const {
|
|||
return visibleDecimalDigitCount;
|
||||
}
|
||||
|
||||
bool FixedDecimal::operator==(const FixedDecimal &other) const {
|
||||
return source == other.source && visibleDecimalDigitCount == other.visibleDecimalDigitCount
|
||||
&& decimalDigits == other.decimalDigits && exponent == other.exponent;
|
||||
}
|
||||
|
||||
UnicodeString FixedDecimal::toString() const {
|
||||
char pattern[15];
|
||||
char buffer[20];
|
||||
if (exponent == 0) {
|
||||
snprintf(pattern, sizeof(pattern), "%%.%df", visibleDecimalDigitCount);
|
||||
snprintf(buffer, sizeof(buffer), pattern, source);
|
||||
} else {
|
||||
snprintf(pattern, sizeof(pattern), "%%.%dfe%%d", visibleDecimalDigitCount);
|
||||
snprintf(buffer, sizeof(buffer), pattern, source, exponent);
|
||||
}
|
||||
return UnicodeString(buffer, -1, US_INV);
|
||||
}
|
||||
|
||||
|
||||
PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode &status) {
|
||||
|
|
|
@ -30,6 +30,12 @@
|
|||
#include "hash.h"
|
||||
#include "uassert.h"
|
||||
|
||||
/**
|
||||
* A FixedDecimal version of UPLRULES_NO_UNIQUE_VALUE used in PluralRulesTest
|
||||
* for parsing of samples.
|
||||
*/
|
||||
#define UPLRULES_NO_UNIQUE_VALUE_DECIMAL (FixedDecimal((double)-0.00123456777))
|
||||
|
||||
class PluralRulesTest;
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
@ -274,7 +280,9 @@ class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject {
|
|||
* @param n the number, e.g. 12.345
|
||||
* @param v The number of visible fraction digits, e.g. 3
|
||||
* @param f The fraction digits, e.g. 345
|
||||
* @param e The exponent, e.g. 7 in 1.2e7 (for compact/scientific)
|
||||
*/
|
||||
FixedDecimal(double n, int32_t v, int64_t f, int32_t e);
|
||||
FixedDecimal(double n, int32_t v, int64_t f);
|
||||
FixedDecimal(double n, int32_t);
|
||||
explicit FixedDecimal(double n);
|
||||
|
@ -283,6 +291,8 @@ class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject {
|
|||
FixedDecimal(const UnicodeString &s, UErrorCode &ec);
|
||||
FixedDecimal(const FixedDecimal &other);
|
||||
|
||||
static FixedDecimal createWithExponent(double n, int32_t v, int32_t e);
|
||||
|
||||
double getPluralOperand(PluralOperand operand) const U_OVERRIDE;
|
||||
bool isNaN() const U_OVERRIDE;
|
||||
bool isInfinite() const U_OVERRIDE;
|
||||
|
@ -292,6 +302,7 @@ class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject {
|
|||
|
||||
int32_t getVisibleFractionDigitCount() const;
|
||||
|
||||
void init(double n, int32_t v, int64_t f, int32_t e);
|
||||
void init(double n, int32_t v, int64_t f);
|
||||
void init(double n);
|
||||
UBool quickInit(double n); // Try a fast-path only initialization,
|
||||
|
@ -300,11 +311,16 @@ class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject {
|
|||
static int64_t getFractionalDigits(double n, int32_t v);
|
||||
static int32_t decimals(double n);
|
||||
|
||||
bool operator==(const FixedDecimal &other) const;
|
||||
|
||||
UnicodeString toString() const;
|
||||
|
||||
double source;
|
||||
int32_t visibleDecimalDigitCount;
|
||||
int64_t decimalDigits;
|
||||
int64_t decimalDigitsWithoutTrailingZeros;
|
||||
int64_t intValue;
|
||||
int32_t exponent;
|
||||
UBool _hasIntegerValue;
|
||||
UBool isNegative;
|
||||
UBool _isNaN;
|
||||
|
|
|
@ -46,6 +46,7 @@ U_NAMESPACE_BEGIN
|
|||
|
||||
class Hashtable;
|
||||
class IFixedDecimal;
|
||||
class FixedDecimal;
|
||||
class RuleChain;
|
||||
class PluralRuleParser;
|
||||
class PluralKeywordEnumeration;
|
||||
|
@ -475,6 +476,32 @@ public:
|
|||
double *dest, int32_t destCapacity,
|
||||
UErrorCode& status);
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
/**
|
||||
* Internal-only function that returns FixedDecimals instead of doubles.
|
||||
*
|
||||
* Returns sample values for which select() would return the keyword. If
|
||||
* the keyword is unknown, returns no values, but this is not an error.
|
||||
*
|
||||
* The number of returned values is typically small.
|
||||
*
|
||||
* @param keyword The keyword.
|
||||
* @param dest Array into which to put the returned values. May
|
||||
* be NULL if destCapacity is 0.
|
||||
* @param destCapacity The capacity of the array, must be at least 0.
|
||||
* @param status The error code.
|
||||
* @return The count of values written.
|
||||
* If more than destCapacity samples are available, then
|
||||
* only destCapacity are written, and destCapacity is returned as the count,
|
||||
* rather than setting a U_BUFFER_OVERFLOW_ERROR.
|
||||
* (The actual number of keyword values could be unlimited.)
|
||||
* @internal
|
||||
*/
|
||||
int32_t getSamples(const UnicodeString &keyword,
|
||||
FixedDecimal *dest, int32_t destCapacity,
|
||||
UErrorCode& status);
|
||||
#endif /* U_HIDE_INTERNAL_API */
|
||||
|
||||
/**
|
||||
* Returns true if the given keyword is defined in this
|
||||
* <code>PluralRules</code> object.
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "unicode/numberrangeformatter.h"
|
||||
|
||||
#include "cmemory.h"
|
||||
#include "cstr.h"
|
||||
#include "plurrule_impl.h"
|
||||
#include "plurults.h"
|
||||
#include "uhash.h"
|
||||
|
@ -49,6 +50,8 @@ void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &na
|
|||
TESTCASE_AUTO(testAPI);
|
||||
// TESTCASE_AUTO(testGetUniqueKeywordValue);
|
||||
TESTCASE_AUTO(testGetSamples);
|
||||
TESTCASE_AUTO(testGetFixedDecimalSamples);
|
||||
TESTCASE_AUTO(testSamplesWithExponent);
|
||||
TESTCASE_AUTO(testWithin);
|
||||
TESTCASE_AUTO(testGetAllKeywordValues);
|
||||
TESTCASE_AUTO(testCompactDecimalPluralKeyword);
|
||||
|
@ -356,124 +359,245 @@ UBool testEquality(const PluralRules &test) {
|
|||
|
||||
void
|
||||
PluralRulesTest::assertRuleValue(const UnicodeString& rule, double expected) {
|
||||
assertRuleKeyValue("a:" + rule, "a", expected);
|
||||
assertRuleKeyValue("a:" + rule, "a", expected);
|
||||
}
|
||||
|
||||
void
|
||||
PluralRulesTest::assertRuleKeyValue(const UnicodeString& rule,
|
||||
const UnicodeString& key, double expected) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
PluralRules *pr = PluralRules::createRules(rule, status);
|
||||
double result = pr->getUniqueKeywordValue(key);
|
||||
delete pr;
|
||||
if (expected != result) {
|
||||
errln("expected %g but got %g", expected, result);
|
||||
}
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
PluralRules *pr = PluralRules::createRules(rule, status);
|
||||
double result = pr->getUniqueKeywordValue(key);
|
||||
delete pr;
|
||||
if (expected != result) {
|
||||
errln("expected %g but got %g", expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: UniqueKeywordValue() is not currently supported.
|
||||
// If it never will be, this test code should be removed.
|
||||
void PluralRulesTest::testGetUniqueKeywordValue() {
|
||||
assertRuleValue("n is 1", 1);
|
||||
assertRuleValue("n in 2..2", 2);
|
||||
assertRuleValue("n within 2..2", 2);
|
||||
assertRuleValue("n in 3..4", UPLRULES_NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n within 3..4", UPLRULES_NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 or n is 2", 2);
|
||||
assertRuleValue("n is 2 and n is 2", 2);
|
||||
assertRuleValue("n is 2 or n is 3", UPLRULES_NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 and n is 3", UPLRULES_NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 or n in 2..3", UPLRULES_NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 and n in 2..3", 2);
|
||||
assertRuleKeyValue("a: n is 1", "not_defined", UPLRULES_NO_UNIQUE_VALUE); // key not defined
|
||||
assertRuleKeyValue("a: n is 1", "other", UPLRULES_NO_UNIQUE_VALUE); // key matches default rule
|
||||
assertRuleValue("n is 1", 1);
|
||||
assertRuleValue("n in 2..2", 2);
|
||||
assertRuleValue("n within 2..2", 2);
|
||||
assertRuleValue("n in 3..4", UPLRULES_NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n within 3..4", UPLRULES_NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 or n is 2", 2);
|
||||
assertRuleValue("n is 2 and n is 2", 2);
|
||||
assertRuleValue("n is 2 or n is 3", UPLRULES_NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 and n is 3", UPLRULES_NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 or n in 2..3", UPLRULES_NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 and n in 2..3", 2);
|
||||
assertRuleKeyValue("a: n is 1", "not_defined", UPLRULES_NO_UNIQUE_VALUE); // key not defined
|
||||
assertRuleKeyValue("a: n is 1", "other", UPLRULES_NO_UNIQUE_VALUE); // key matches default rule
|
||||
}
|
||||
|
||||
void PluralRulesTest::testGetSamples() {
|
||||
// TODO: fix samples, re-enable this test.
|
||||
// TODO: fix samples, re-enable this test.
|
||||
|
||||
// no get functional equivalent API in ICU4C, so just
|
||||
// test every locale...
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
int32_t numLocales;
|
||||
const Locale* locales = Locale::getAvailableLocales(numLocales);
|
||||
// no get functional equivalent API in ICU4C, so just
|
||||
// test every locale...
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
int32_t numLocales;
|
||||
const Locale* locales = Locale::getAvailableLocales(numLocales);
|
||||
|
||||
double values[1000];
|
||||
for (int32_t i = 0; U_SUCCESS(status) && i < numLocales; ++i) {
|
||||
if (uprv_strcmp(locales[i].getLanguage(), "fr") == 0 &&
|
||||
logKnownIssue("21299", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
|
||||
continue;
|
||||
}
|
||||
PluralRules *rules = PluralRules::forLocale(locales[i], status);
|
||||
if (U_FAILURE(status)) {
|
||||
break;
|
||||
}
|
||||
StringEnumeration *keywords = rules->getKeywords(status);
|
||||
if (U_FAILURE(status)) {
|
||||
delete rules;
|
||||
break;
|
||||
}
|
||||
const UnicodeString* keyword;
|
||||
while (NULL != (keyword = keywords->snext(status))) {
|
||||
int32_t count = rules->getSamples(*keyword, values, UPRV_LENGTHOF(values), status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln(UNICODE_STRING_SIMPLE("getSamples() failed for locale ") +
|
||||
locales[i].getName() +
|
||||
UNICODE_STRING_SIMPLE(", keyword ") + *keyword);
|
||||
continue;
|
||||
}
|
||||
if (count == 0) {
|
||||
// TODO: Lots of these.
|
||||
// errln(UNICODE_STRING_SIMPLE("no samples for keyword ") + *keyword + UNICODE_STRING_SIMPLE(" in locale ") + locales[i].getName() );
|
||||
}
|
||||
if (count > UPRV_LENGTHOF(values)) {
|
||||
errln(UNICODE_STRING_SIMPLE("getSamples()=") + count +
|
||||
UNICODE_STRING_SIMPLE(", too many values, for locale ") +
|
||||
locales[i].getName() +
|
||||
UNICODE_STRING_SIMPLE(", keyword ") + *keyword);
|
||||
count = UPRV_LENGTHOF(values);
|
||||
}
|
||||
for (int32_t j = 0; j < count; ++j) {
|
||||
if (values[j] == UPLRULES_NO_UNIQUE_VALUE) {
|
||||
errln("got 'no unique value' among values");
|
||||
} else {
|
||||
UnicodeString resultKeyword = rules->select(values[j]);
|
||||
// if (strcmp(locales[i].getName(), "uk") == 0) { // Debug only.
|
||||
// std::cout << " uk " << US(resultKeyword).cstr() << " " << values[j] << std::endl;
|
||||
// }
|
||||
if (*keyword != resultKeyword) {
|
||||
errln("file %s, line %d, Locale %s, sample for keyword \"%s\": %g, select(%g) returns keyword \"%s\"",
|
||||
__FILE__, __LINE__, locales[i].getName(), US(*keyword).cstr(), values[j], values[j], US(resultKeyword).cstr());
|
||||
}
|
||||
double values[1000];
|
||||
for (int32_t i = 0; U_SUCCESS(status) && i < numLocales; ++i) {
|
||||
if (uprv_strcmp(locales[i].getLanguage(), "fr") == 0 &&
|
||||
logKnownIssue("21299", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
|
||||
continue;
|
||||
}
|
||||
LocalPointer<PluralRules> rules(PluralRules::forLocale(locales[i], status));
|
||||
if (U_FAILURE(status)) {
|
||||
break;
|
||||
}
|
||||
LocalPointer<StringEnumeration> keywords(rules->getKeywords(status));
|
||||
if (U_FAILURE(status)) {
|
||||
break;
|
||||
}
|
||||
const UnicodeString* keyword;
|
||||
while (NULL != (keyword = keywords->snext(status))) {
|
||||
int32_t count = rules->getSamples(*keyword, values, UPRV_LENGTHOF(values), status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln(UnicodeString(u"getSamples() failed for locale ") +
|
||||
locales[i].getName() +
|
||||
UnicodeString(u", keyword ") + *keyword);
|
||||
continue;
|
||||
}
|
||||
if (count == 0) {
|
||||
// TODO: Lots of these.
|
||||
// errln(UnicodeString(u"no samples for keyword ") + *keyword + UnicodeString(u" in locale ") + locales[i].getName() );
|
||||
}
|
||||
if (count > UPRV_LENGTHOF(values)) {
|
||||
errln(UnicodeString(u"getSamples()=") + count +
|
||||
UnicodeString(u", too many values, for locale ") +
|
||||
locales[i].getName() +
|
||||
UnicodeString(u", keyword ") + *keyword);
|
||||
count = UPRV_LENGTHOF(values);
|
||||
}
|
||||
for (int32_t j = 0; j < count; ++j) {
|
||||
if (values[j] == UPLRULES_NO_UNIQUE_VALUE) {
|
||||
errln("got 'no unique value' among values");
|
||||
} else {
|
||||
UnicodeString resultKeyword = rules->select(values[j]);
|
||||
// if (strcmp(locales[i].getName(), "uk") == 0) { // Debug only.
|
||||
// std::cout << " uk " << US(resultKeyword).cstr() << " " << values[j] << std::endl;
|
||||
// }
|
||||
if (*keyword != resultKeyword) {
|
||||
errln("file %s, line %d, Locale %s, sample for keyword \"%s\": %g, select(%g) returns keyword \"%s\"",
|
||||
__FILE__, __LINE__, locales[i].getName(), US(*keyword).cstr(), values[j], values[j], US(resultKeyword).cstr());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete keywords;
|
||||
delete rules;
|
||||
}
|
||||
}
|
||||
|
||||
void PluralRulesTest::testGetFixedDecimalSamples() {
|
||||
// TODO: fix samples, re-enable this test.
|
||||
|
||||
// no get functional equivalent API in ICU4C, so just
|
||||
// test every locale...
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
int32_t numLocales;
|
||||
const Locale* locales = Locale::getAvailableLocales(numLocales);
|
||||
|
||||
FixedDecimal values[1000];
|
||||
for (int32_t i = 0; U_SUCCESS(status) && i < numLocales; ++i) {
|
||||
if (uprv_strcmp(locales[i].getLanguage(), "fr") == 0 &&
|
||||
logKnownIssue("21299", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
|
||||
continue;
|
||||
}
|
||||
LocalPointer<PluralRules> rules(PluralRules::forLocale(locales[i], status));
|
||||
if (U_FAILURE(status)) {
|
||||
break;
|
||||
}
|
||||
LocalPointer<StringEnumeration> keywords(rules->getKeywords(status));
|
||||
if (U_FAILURE(status)) {
|
||||
break;
|
||||
}
|
||||
const UnicodeString* keyword;
|
||||
while (NULL != (keyword = keywords->snext(status))) {
|
||||
int32_t count = rules->getSamples(*keyword, values, UPRV_LENGTHOF(values), status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln(UnicodeString(u"getSamples() failed for locale ") +
|
||||
locales[i].getName() +
|
||||
UnicodeString(u", keyword ") + *keyword);
|
||||
continue;
|
||||
}
|
||||
if (count == 0) {
|
||||
// TODO: Lots of these.
|
||||
// errln(UnicodeString(u"no samples for keyword ") + *keyword + UnicodeString(u" in locale ") + locales[i].getName() );
|
||||
}
|
||||
if (count > UPRV_LENGTHOF(values)) {
|
||||
errln(UnicodeString(u"getSamples()=") + count +
|
||||
UnicodeString(u", too many values, for locale ") +
|
||||
locales[i].getName() +
|
||||
UnicodeString(u", keyword ") + *keyword);
|
||||
count = UPRV_LENGTHOF(values);
|
||||
}
|
||||
for (int32_t j = 0; j < count; ++j) {
|
||||
if (values[j] == UPLRULES_NO_UNIQUE_VALUE_DECIMAL) {
|
||||
errln("got 'no unique value' among values");
|
||||
} else {
|
||||
UnicodeString resultKeyword = rules->select(values[j]);
|
||||
// if (strcmp(locales[i].getName(), "uk") == 0) { // Debug only.
|
||||
// std::cout << " uk " << US(resultKeyword).cstr() << " " << values[j] << std::endl;
|
||||
// }
|
||||
if (*keyword != resultKeyword) {
|
||||
errln("file %s, line %d, Locale %s, sample for keyword \"%s\": %s, select(%s) returns keyword \"%s\"",
|
||||
__FILE__, __LINE__, locales[i].getName(), US(*keyword).cstr(), values[j].toString().getBuffer(), values[j].toString().getBuffer(), US(resultKeyword).cstr());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PluralRulesTest::testSamplesWithExponent() {
|
||||
// integer samples
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UnicodeString description(
|
||||
u"one: i = 0,1 @integer 0, 1, 1e5 @decimal 0.0~1.5, 1.1e5; "
|
||||
u"many: e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5"
|
||||
u" @integer 1000000, 2e6, 3e6, 4e6, 5e6, 6e6, 7e6, … @decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …; "
|
||||
u"other: @integer 2~17, 100, 1000, 10000, 100000, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, …"
|
||||
u" @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …"
|
||||
);
|
||||
LocalPointer<PluralRules> test(PluralRules::createRules(description, status));
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Couldn't create plural rules from a string using exponent notation, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
checkNewSamples(description, test, u"one", u"@integer 0, 1, 1e5", FixedDecimal(0));
|
||||
checkNewSamples(description, test, u"many", u"@integer 1000000, 2e6, 3e6, 4e6, 5e6, 6e6, 7e6, …", FixedDecimal(1000000));
|
||||
checkNewSamples(description, test, u"other", u"@integer 2~17, 100, 1000, 10000, 100000, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, …", FixedDecimal(2));
|
||||
|
||||
// decimal samples
|
||||
status = U_ZERO_ERROR;
|
||||
UnicodeString description2(
|
||||
u"one: i = 0,1 @decimal 0.0~1.5, 1.1e5; "
|
||||
u"many: e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5"
|
||||
u" @decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …; "
|
||||
u"other: @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …"
|
||||
);
|
||||
LocalPointer<PluralRules> test2(PluralRules::createRules(description2, status));
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Couldn't create plural rules from a string using exponent notation, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
checkNewSamples(description2, test2, u"one", u"@decimal 0.0~1.5, 1.1e5", FixedDecimal(0, 1));
|
||||
checkNewSamples(description2, test2, u"many", u"@decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …", FixedDecimal::createWithExponent(2.1, 1, 6));
|
||||
checkNewSamples(description2, test2, u"other", u"@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …", FixedDecimal(2.0, 1));
|
||||
}
|
||||
|
||||
void PluralRulesTest::checkNewSamples(
|
||||
UnicodeString description,
|
||||
const LocalPointer<PluralRules> &test,
|
||||
UnicodeString keyword,
|
||||
UnicodeString samplesString,
|
||||
FixedDecimal firstInRange) {
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
FixedDecimal samples[1000];
|
||||
|
||||
test->getSamples(keyword, samples, UPRV_LENGTHOF(samples), status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Couldn't retrieve plural samples, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
FixedDecimal actualFirstSample = samples[0];
|
||||
|
||||
if (!(firstInRange == actualFirstSample)) {
|
||||
CStr descCstr(description);
|
||||
CStr samplesCstr(samplesString);
|
||||
char errMsg[1000];
|
||||
snprintf(errMsg, sizeof(errMsg), "First parsed sample FixedDecimal not equal to expected for samples: %s in rule string: %s\n", descCstr(), samplesCstr());
|
||||
errln(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
void PluralRulesTest::testWithin() {
|
||||
// goes to show you what lack of testing will do.
|
||||
// of course, this has been broken for two years and no one has noticed...
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
PluralRules *rules = PluralRules::createRules("a: n mod 10 in 5..8", status);
|
||||
if (!rules) {
|
||||
errln("couldn't instantiate rules");
|
||||
return;
|
||||
}
|
||||
// goes to show you what lack of testing will do.
|
||||
// of course, this has been broken for two years and no one has noticed...
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
PluralRules *rules = PluralRules::createRules("a: n mod 10 in 5..8", status);
|
||||
if (!rules) {
|
||||
errln("couldn't instantiate rules");
|
||||
return;
|
||||
}
|
||||
|
||||
UnicodeString keyword = rules->select((int32_t)26);
|
||||
if (keyword != "a") {
|
||||
errln("expected 'a' for 26 but didn't get it.");
|
||||
}
|
||||
UnicodeString keyword = rules->select((int32_t)26);
|
||||
if (keyword != "a") {
|
||||
errln("expected 'a' for 26 but didn't get it.");
|
||||
}
|
||||
|
||||
keyword = rules->select(26.5);
|
||||
if (keyword != "other") {
|
||||
errln("expected 'other' for 26.5 but didn't get it.");
|
||||
}
|
||||
keyword = rules->select(26.5);
|
||||
if (keyword != "other") {
|
||||
errln("expected 'other' for 26.5 but didn't get it.");
|
||||
}
|
||||
|
||||
delete rules;
|
||||
delete rules;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -30,6 +30,8 @@ private:
|
|||
void testAPI();
|
||||
void testGetUniqueKeywordValue();
|
||||
void testGetSamples();
|
||||
void testGetFixedDecimalSamples();
|
||||
void testSamplesWithExponent();
|
||||
void testWithin();
|
||||
void testGetAllKeywordValues();
|
||||
void testCompactDecimalPluralKeyword();
|
||||
|
@ -45,6 +47,11 @@ private:
|
|||
void assertRuleValue(const UnicodeString& rule, double expected);
|
||||
void assertRuleKeyValue(const UnicodeString& rule, const UnicodeString& key,
|
||||
double expected);
|
||||
void checkNewSamples(UnicodeString description,
|
||||
const LocalPointer<PluralRules> &test,
|
||||
UnicodeString keyword,
|
||||
UnicodeString samplesString,
|
||||
FixedDecimal firstInRange);
|
||||
UnicodeString getPluralKeyword(const LocalPointer<PluralRules> &rules,
|
||||
Locale locale, double number, const char16_t* skeleton);
|
||||
void checkSelect(const LocalPointer<PluralRules> &rules, UErrorCode &status,
|
||||
|
|
Loading…
Add table
Reference in a new issue