rework decimal lowBoundary() and highBoundary() after discussion on Zulip, and add extensive testing

This commit is contained in:
Grahame Grieve 2024-08-19 07:08:59 +08:00
parent db9d788a9a
commit 00ad185882
2 changed files with 58 additions and 30 deletions

View File

@ -3735,20 +3735,20 @@ public class FHIRPathEngine {
case LowBoundary:
case HighBoundary: {
checkContextContinuous(focus, exp.getFunction().toCode(), exp);
checkContextContinuous(focus, exp.getFunction().toCode(), exp, true);
if (paramTypes.size() > 0) {
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer));
}
if (focus.hasType("decimal") && (focus.hasType("date") || focus.hasType("datetime") || focus.hasType("instant"))) {
if ((focus.hasType("date") || focus.hasType("datetime") || focus.hasType("instant"))) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal, TypeDetails.FP_DateTime);
} else if (focus.hasType("decimal")) {
} else if (focus.hasType("decimal") || focus.hasType("integer")) {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal);
} else {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime);
}
}
case Precision: {
checkContextContinuous(focus, exp.getFunction().toCode(), exp);
checkContextContinuous(focus, exp.getFunction().toCode(), exp, false);
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer);
}
case hasTemplateIdOf: {
@ -3897,8 +3897,8 @@ public class FHIRPathEngine {
}
}
private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException {
if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time") && !focus.hasType("Quantity")) {
private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr, boolean allowInteger) throws PathEngineException {
if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time") && !focus.hasType("Quantity") && !(allowInteger && focus.hasType("integer"))) {
throw makeException(expr, I18nConstants.FHIRPATH_CONTINUOUS_ONLY, name, focus.describe());
}
}
@ -4295,7 +4295,7 @@ public class FHIRPathEngine {
if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "lowBoundary", focus.size());
}
int precision = 0;
Integer precision = null;
if (expr.getParameters().size() > 0) {
List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true);
if (n1.size() != 1) {
@ -4308,17 +4308,23 @@ public class FHIRPathEngine {
List<Base> result = new ArrayList<Base>();
if (base.hasType("decimal")) {
result.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == 0 ? 8 : precision)));
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("integer")) {
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("date")) {
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 10 : precision)));
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == null ? 10 : precision)));
} else if (base.hasType("dateTime")) {
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == null ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.lowBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
result.add(new TimeType(Utilities.lowBoundaryForTime(base.primitiveValue(), precision == null ? 9 : precision)));
} else if (base.hasType("Quantity")) {
String value = getNamedValue(base, "value");
Base v = base.copy();
v.setProperty("value", new DecimalType(Utilities.lowBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
v.setProperty("value", new DecimalType(Utilities.lowBoundaryForDecimal(value, precision == null ? 8 : precision)));
result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");
@ -4333,7 +4339,7 @@ public class FHIRPathEngine {
if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "highBoundary", focus.size());
}
int precision = 0;
Integer precision = null;
if (expr.getParameters().size() > 0) {
List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true);
if (n1.size() != 1) {
@ -4346,17 +4352,23 @@ public class FHIRPathEngine {
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("decimal")) {
result.add(new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == 0 ? 8 : precision)));
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("integer")) {
if (precision == null || (precision >= 0 && precision < 17)) {
result.add(new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == null ? 8 : precision)));
}
} else if (base.hasType("date")) {
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 10 : precision)));
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == null ? 10 : precision)));
} else if (base.hasType("dateTime")) {
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == null ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.highBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
result.add(new TimeType(Utilities.highBoundaryForTime(base.primitiveValue(), precision == null ? 9 : precision)));
} else if (base.hasType("Quantity")) {
String value = getNamedValue(base, "value");
Base v = base.copy();
v.setProperty("value", new DecimalType(Utilities.highBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
v.setProperty("value", new DecimalType(Utilities.highBoundaryForDecimal(value, precision == null ? 8 : precision)));
result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");

View File

@ -1695,31 +1695,47 @@ public class Utilities {
value = value.substring(0, value.indexOf("e"));
}
if (isZero(value)) {
return applyPrecision("-0.5000000000000000000000000", precision);
return applyPrecision("-0.5000000000000000000000000", precision, true);
} else if (value.startsWith("-")) {
return "-"+highBoundaryForDecimal(value.substring(1), precision)+(e == null ? "" : e);
} else {
if (value.contains(".")) {
return applyPrecision(minusOne(value)+"50000000000000000000000000000", precision)+(e == null ? "" : e);
return applyPrecision(minusOne(value)+"50000000000000000000000000000", precision, true)+(e == null ? "" : e);
} else {
return applyPrecision(minusOne(value)+".50000000000000000000000000000", precision)+(e == null ? "" : e);
return applyPrecision(minusOne(value)+".50000000000000000000000000000", precision, true)+(e == null ? "" : e);
}
}
}
private static String applyPrecision(String v, int p) {
private static String applyPrecision(String v, int p, boolean down) {
String nv = v;
int dp = -1;
if (nv.contains(".")) {
dp = nv.indexOf(".");
nv = nv.substring(0, dp)+nv.substring(dp+1);
}
String s = null;
int d = p - getDecimalPrecision(v);
if (d == 0) {
return v;
s = nv;
} else if (d > 0) {
return v + padLeft("", '0', d);
s = nv + padLeft("", '0', d);
} else {
if (v.charAt(v.length()+d) >= '6') {
return v.substring(0, v.length()+d-1)+((char) (v.charAt(v.length()+d)+1));
int l = v.length();
int ld = l+d;
if (dp > -1) {
ld--;
}
if (nv.charAt(ld) >= '5' && !down) {
s = nv.substring(0, ld-1)+((char) (nv.charAt(ld-1)+1));
} else {
return v.substring(0, v.length()+d);
s = nv.substring(0, ld);
}
}
if (s.endsWith(".")) {
s = s.substring(0, s.length()-1);
}
return dp == -1 || dp >= s.length() ? s : s.substring(0, dp)+"."+s.substring(dp);
}
private static String minusOne(String value) {
@ -1831,14 +1847,14 @@ public class Utilities {
value = value.substring(0, value.indexOf("e"));
}
if (isZero(value)) {
return applyPrecision("0.50000000000000000000000000000", precision);
return applyPrecision("0.50000000000000000000000000000", precision, false);
} else if (value.startsWith("-")) {
return "-"+lowBoundaryForDecimal(value.substring(1), precision)+(e == null ? "" : e);
} else {
if (value.contains(".")) {
return applyPrecision(value+"50000000000000000000000000000", precision)+(e == null ? "" : e);
return applyPrecision(value+"50000000000000000000000000000", precision, false)+(e == null ? "" : e);
} else {
return applyPrecision(value+".50000000000000000000000000000", precision)+(e == null ? "" : e);
return applyPrecision(value+".50000000000000000000000000000", precision, false)+(e == null ? "" : e);
}
}
}