Skip to content

Updating number format data generation and NodeJS number_fmt #468

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jun 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 73 additions & 20 deletions executors/cpp/number_fmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <cstdlib>

#include <iostream>
#include <regex>
#include <string>
#include <cstring>

Expand Down Expand Up @@ -220,6 +221,42 @@ auto TestNumfmt(json_object *json_in) -> string {
locale_string = json_object_get_string(locale_label_obj);
}

// JSON for the results
json_object *return_json = json_object_new_object();
json_object_object_add(return_json, "label", label_obj);

json_object *pattern_obj = json_object_object_get(json_in, "pattern");
string pattern = "";
if (pattern_obj) {
pattern = json_object_get_string(pattern_obj);

// Check for unsupported patterns.
std::regex check1("(0+0\\.#+E)");
std::regex check2("(^\\.0#*E)");
std::smatch m;

if (std::regex_search(pattern, m, check1) || std::regex_search(pattern, m, check2)) {
// No, not a supported pattern
// Report a failure
json_object_object_add(
return_json,
"error_type",
json_object_new_string("unsupported"));
json_object_object_add(
return_json,
"unsupported",
json_object_new_string("unsupported pattern"));
json_object_object_add(
return_json,
"error_detail",
json_object_new_string(pattern.c_str()));

// Nothing more to do here.
string return_string = json_object_to_json_string(return_json);
return return_string;
}
}

const Locale displayLocale(locale_string.c_str());

// Get options
Expand All @@ -244,6 +281,7 @@ auto TestNumfmt(json_object *json_in) -> string {
string unitDisplay_string;
string style_string;
string compactDisplay_string;
string roundingMode_string;

// Defaults for settings.
CurrencyUnit currency_unit_setting = CurrencyUnit();
Expand Down Expand Up @@ -338,13 +376,15 @@ auto TestNumfmt(json_object *json_in) -> string {
// TODO: Make this a function rather than inline.
roundingMode_obj = json_object_object_get(options_obj, "roundingMode");
if (roundingMode_obj != nullptr) {
string roundingMode_string = json_object_get_string(roundingMode_obj);
roundingMode_string = json_object_get_string(roundingMode_obj);
if (roundingMode_string == "floor") {
rounding_setting = UNUM_ROUND_FLOOR;
} else if (roundingMode_string == "ceil") {
rounding_setting = UNUM_ROUND_CEILING;
} else if (roundingMode_string == "halfEven") {
rounding_setting = UNUM_ROUND_HALFEVEN;
} else if (roundingMode_string == "halfOdd") {
rounding_setting = UNUM_ROUND_HALF_ODD;
} else if (roundingMode_string == "halfTrunc") {
rounding_setting = UNUM_ROUND_HALFDOWN;
} else if (roundingMode_string == "halfExpand") {
Expand All @@ -353,26 +393,28 @@ auto TestNumfmt(json_object *json_in) -> string {
rounding_setting = UNUM_ROUND_DOWN;
} else if (roundingMode_string == "expand") {
rounding_setting = UNUM_ROUND_UP;
} else if (roundingMode_string == "unnecessary") {
rounding_setting = UNUM_ROUND_UNNECESSARY;
}
// TODO: Finish this
// UNUM_ROUND_HALFEVEN , UNUM_FOUND_HALFEVEN = UNUM_ROUND_HALFEVEN ,
// UNUM_ROUND_HALFDOWN = UNUM_ROUND_HALFEVEN + 1 , UNUM_ROUND_HALFUP ,
// UNUM_ROUND_UNNECESSARY , UNUM_ROUND_HALF_ODD ,
// UNUM_ROUND_HALF_CEILING , UNUM_ROUND_HALF_FLOOR
}

// TODO: make a function
// TODO: make a function for group_setting
group_obj = json_object_object_get(options_obj, "useGrouping");
if (group_obj != nullptr) {
string group_string = json_object_get_string(group_obj);
if (group_string == "false") {
if (group_string == "false" || group_string == "off") {
grouping_setting = UNUM_GROUPING_OFF;
} else if (group_string == "true") {
} else if (group_string == "true" || group_string == "auto") {
grouping_setting = UNUM_GROUPING_AUTO;
} else if (group_string == "on_aligned") {
grouping_setting = UNUM_GROUPING_ON_ALIGNED;
} else if (group_string == "mim2") {
grouping_setting = UNUM_GROUPING_MIN2;
} else if (group_string == "thousands") {
grouping_setting = UNUM_GROUPING_THOUSANDS;
} else if (group_string == "count") {
grouping_setting = UNUM_GROUPING_COUNT;
}
// TODO: FINISH - could be OFF, MIN2, AUTO, ON_ALIGNED, THOUSANDS
}

// Need to avoid resetting when not options are specifierd.
Expand Down Expand Up @@ -422,10 +464,6 @@ auto TestNumfmt(json_object *json_in) -> string {

// Start using these things

// JSON for the results
json_object *return_json = json_object_new_object();
json_object_object_add(return_json, "label", label_obj);

int32_t chars_out; // Results of extracting characters from Unicode string
bool no_error = true;

Expand Down Expand Up @@ -504,12 +542,27 @@ auto TestNumfmt(json_object *json_in) -> string {

if (U_FAILURE(status) != 0) {
// Report a failure
const char* error_name = u_errorName(status);
json_object_object_add(
return_json, "error",
json_object_new_string("error in string extract"));
json_object_object_add(
return_json, "error_detail", json_object_new_string(error_name));
if (status == U_FORMAT_INEXACT_ERROR) {
const char* error_name = u_errorName(status);
// Inexact result is unsupported.
json_object_object_add(
return_json,
"error_type",
json_object_new_string("unsupported"));
json_object_object_add(
return_json,
"unsupported",
json_object_new_string(error_name));
}
else {
const char* error_name = u_errorName(status);

json_object_object_add(
return_json, "error",
json_object_new_string("error getting result string"));
json_object_object_add(
return_json, "error_detail", json_object_new_string(error_name));
}
} else {
// It worked!
json_object_object_add(return_json,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ public class NumberFormatterOutputJson implements ITestTypeOutputJson {

public String result;

public String unsupported;
public String error;

public String error_detail;
public String error_type;
public String error_message;
}

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import org.unicode.conformance.ExecutorUtils;
import org.unicode.conformance.testtype.ITestType;
import org.unicode.conformance.testtype.ITestTypeInputJson;
Expand Down Expand Up @@ -108,13 +110,6 @@ public ITestTypeInputJson inputMapToJson(Map<String, Object> inputMapData) {
NumberFormatterTestOptionKey.roundingMode,
RoundingModeVal.getFromString(
(String) parsedOptionsMap.get("roundingMode")));
int roundingIncrement =
(int) parsedOptionsMap.getOrDefault("roundingIncrement", RoundingIncrementUtil.DEFAULT);
assert RoundingIncrementUtil.isValidVal(roundingIncrement);
options.put(
NumberFormatterTestOptionKey.roundingIncrement,
roundingIncrement
);
options.put(
NumberFormatterTestOptionKey.trailingZeroDisplay,
TrailingZeroDispalyVal.getFromString(
Expand All @@ -136,11 +131,37 @@ public ITestTypeOutputJson execute(ITestTypeInputJson inputJson) {
NumberFormatterOutputJson output = new NumberFormatterOutputJson();
output.label = input.label;

// Check for unsupported patterns
if (input.pattern != null && input.pattern != "") {
Pattern check1 = Pattern.compile("0+0\\.#+E");
Matcher matcher1 = check1.matcher(input.pattern);
Pattern check2 = Pattern.compile("^\\.0#*E");
Matcher matcher2 = check2.matcher(input.pattern);
if (matcher1.find() || matcher2.find()) {
output.error_type = "unsupported";
output.unsupported = "unsupported pattern";
output.error_detail = input.pattern;
return output;
}
}

// Check unsupport options in skeleton
if (input.skeleton != null) {
Pattern check_half_odd = Pattern.compile("rounding-mode-(half-odd)");
Matcher matcher1 = check_half_odd.matcher(input.skeleton);
if (matcher1.find()) {
output.error_type = "unsupported";
output.unsupported = "skeleton option";
output.error_detail = matcher1.group();
return output;
}

}
try {
output.result = getFormattedNumber(input);
} catch (Exception e) {
output.error = e.getMessage();
output.error_message = e.getMessage();
output.error_type = "formatting number";
return output;
}

Expand Down Expand Up @@ -354,13 +375,6 @@ public String getFormattedNumber(NumberFormatterInputJson input) {
precision = fractionPrecision;
}

if (precision == null
&& input.options.containsKey(NumberFormatterTestOptionKey.roundingIncrement)) {
int roundingIncrement =
(int) input.options.get(NumberFormatterTestOptionKey.roundingIncrement);
precision = Precision.increment(new BigDecimal(roundingIncrement));
}

if (precision != null
&& input.options.containsKey(NumberFormatterTestOptionKey.trailingZeroDisplay)) {
TrailingZeroDispalyVal trailingZeroDisplayVal =
Expand Down Expand Up @@ -421,10 +435,12 @@ public String getFormattedNumber(NumberFormatterInputJson input) {
break;
case halfEven:
roundingMode = RoundingMode.HALF_EVEN;
case NONE: // default = halfEven
default:
break;
case unnecessary:
roundingMode = RoundingMode.UNNECESSARY;
break;
case NONE: // default = halfEven
default:
break;
}
if (roundingMode != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import static org.junit.Assert.assertEquals;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.Ignore;
import org.junit.Test;
import org.unicode.conformance.testtype.numberformatter.NumberFormatterOutputJson;
import org.unicode.conformance.testtype.numberformatter.NumberFormatterTester;
Expand Down Expand Up @@ -41,4 +44,47 @@ public void testDebug2() {
assertEquals("-0.2", output.result);
}

@Test
public void testDebugBadPattern() {
String testInput = "{\"test_type\": \"number_fmt\", \"label\":\"5865\",\"op\":\"format\",\"pattern\":\"00.##E0\",\"input\":\"1234567\",\"options\":{\"roundingMode\":\"halfEven\",\"notation\":\"scientific\",\"minimumIntegerDigits\":2,\"minimumFractionDigits\":1,\"maximumFractionDigits\":3,\"useGrouping\":false},\"hexhash\":\"bbaf139c38c5be2b1028124e8da7f1497b19d0a3\"}";

NumberFormatterOutputJson output =
(NumberFormatterOutputJson) NumberFormatterTester.INSTANCE.getStructuredOutputFromInputStr(testInput);

assertEquals("unsupported pattern", output.unsupported);
}

@Test
public void testRoundingUnnecesary() {
String testInput = "{\"test_type\": \"number_fmt\", \"label\":\"5919\",\"op\":\"format\",\"pattern\":\"0.00\",\"skeleton\":\".00 rounding-mode-unnecessary\",\"input\":\"-32.045\",\"options\":{\"roundingMode\":\"unnecessary\",\"minimumIntegerDigits\":1,\"minimumFractionDigits\":2,\"maximumFractionDigits\":2,\"useGrouping\":false},\"hexhash\":\"bbbd9b3299dab2d42ea10ea0b068680b99aef9b1\"}";
NumberFormatterOutputJson output =
(NumberFormatterOutputJson) NumberFormatterTester.INSTANCE.getStructuredOutputFromInputStr(testInput);

Pattern check_error = Pattern.compile("Rounding is required");
Matcher matcher_error = check_error.matcher(output.error);
assertEquals(matcher_error.find(), true);
}

@Test
public void testRoundingHalfOdd() {
String testInput =
"{\"test_type\": \"number_fmt\", \"label\":\"5927\",\"op\":\"format\",\"pattern\":\"0.00\",\"skeleton\":\".00 rounding-mode-half-odd\",\"input\":\"1.235\",\"options\":{\"roundingMode\":\"halfOdd\",\"minimumIntegerDigits\":1,\"minimumFractionDigits\":2,\"maximumFractionDigits\":2,\"useGrouping\":false},\"hexhash\":\"0f81db6894d53d8bb8973e658acc50726ae11295\"}";

NumberFormatterOutputJson output =
(NumberFormatterOutputJson) NumberFormatterTester.INSTANCE.getStructuredOutputFromInputStr(testInput);

assertEquals("rounding-mode-half-odd", output.error_detail);
}

@Test
public void testPrecisionIncrement() {
String testInput =
"{\"test_type\": \"number_fmt\", \"label\":\"5868\",\"op\":\"format\",\"pattern\":\"0005\",\"skeleton\":\"integer-width/0000 precision-increment/0005 group-off rounding-mode-half-even\",\"input\":\"1234\",\"options\":{\"roundingMode\":\"halfEven\",\"minimumIntegerDigits\":4,\"roundingIncrement\":5,\"maximumFractionDigits\":0,\"roundingPriority\":\"auto\",\"useGrouping\":false},\"hexhash\":\"1fd49a0510450f4c3cc4dd7a33072488ac9e12ad\"}";

NumberFormatterOutputJson output =
(NumberFormatterOutputJson) NumberFormatterTester.INSTANCE.getStructuredOutputFromInputStr(testInput);

assertEquals("1235", output.result);
}

}
Loading
Loading