diff --git a/engine.fhir/src/test/java/org/hl7/fhirpath/TestFhirPath.java b/engine.fhir/src/test/java/org/hl7/fhirpath/TestFhirPath.java
index d2e7cb692..864da1475 100644
--- a/engine.fhir/src/test/java/org/hl7/fhirpath/TestFhirPath.java
+++ b/engine.fhir/src/test/java/org/hl7/fhirpath/TestFhirPath.java
@@ -503,7 +503,7 @@ private void runTests(String testsFilePath, int expectedTestCount, int expectedP
@Test
public void testFhirPathR4() {
- runTests("r4/tests-fhir-r4.xml", 721, 550, 18);
+ runTests("r4/tests-fhir-r4.xml", 721, 551, 18);
}
@Test
@@ -518,7 +518,7 @@ public void testCqlAggregate() {
@Test
public void testCqlArithmeticFunctions() {
- runTests("cql/CqlArithmeticFunctionsTest.xml", 192, 183, 0);
+ runTests("cql/CqlArithmeticFunctionsTest.xml", 192, 192, 0);
}
@Test
@@ -573,7 +573,7 @@ public void testCqlTypeOperators() {
@Test
public void testCqlTypes() {
- runTests("cql/CqlTypesTest.xml", 27, 25, 0);
+ runTests("cql/CqlTypesTest.xml", 27, 27, 0);
}
@Test
diff --git a/engine.fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml b/engine.fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml
index 2b0e5e6f1..ff9a9dd17 100644
--- a/engine.fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml
+++ b/engine.fhir/src/test/resources/org/hl7/fhirpath/cql/CqlArithmeticFunctionsTest.xml
@@ -295,13 +295,14 @@
Ln(-1)
+
- Round(Ln(1000), 8)
-
+ Round(Ln(1000), 7)
+
- Round(Ln(1000.0), 8)
-
+ Round(Ln(1000.0), 7)
+
@@ -310,13 +311,14 @@
+
minimum Long
-
+
minimum Decimal
-
+
@@ -341,9 +343,10 @@
maximum Long
+
maximum Decimal
-
+
@@ -825,9 +828,10 @@
10 div 5.0
+
10.1 'cm' div -3.1 'cm'
-
+
diff --git a/engine.fhir/src/test/resources/org/hl7/fhirpath/cql/CqlTypesTest.xml b/engine.fhir/src/test/resources/org/hl7/fhirpath/cql/CqlTypesTest.xml
index f3a281788..21a8e342e 100644
--- a/engine.fhir/src/test/resources/org/hl7/fhirpath/cql/CqlTypesTest.xml
+++ b/engine.fhir/src/test/resources/org/hl7/fhirpath/cql/CqlTypesTest.xml
@@ -76,13 +76,18 @@
DateTime(2015, 2, 10)
-
+
+
+
+
+ days between DateTime(2015, 2, 10) and DateTime(2015, 3, 1)
+
DateTime(0001, 1, 1, 0, 0, 0, 0)
@@ -166,9 +171,15 @@
@T23:59:60.999
-
- @T23:59:59.10000
+
+
+
+
+
+ @T23:59:59.-999
+
@T10:25:12.863
diff --git a/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/DivideEvaluator.java b/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/DivideEvaluator.java
index 627c809e3..0e335239b 100644
--- a/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/DivideEvaluator.java
+++ b/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/DivideEvaluator.java
@@ -2,6 +2,8 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.opencds.cqf.cql.engine.exception.InvalidOperatorArgument;
import org.opencds.cqf.cql.engine.execution.Context;
@@ -48,8 +50,24 @@ public static Object divide(Object left, Object right) {
}
else if (left instanceof Quantity && right instanceof Quantity) {
+ //@@@CQF-1348 unit calculation in division
+ String unit = "";
+ String unitLeft = ((Quantity) left).getUnit();
+ String unitRight = ((Quantity) right).getUnit();
+ if (unitLeft.equals("1") && !unitRight.equals("1") ) {
+ throw new InvalidOperatorArgument(
+ "Dividend and divisor must have the same unit",
+ String.format("Divide(%s, %s)", ((Quantity) left).getUnit(), ((Quantity) right).getUnit())
+ );
+ }
+ else if (!unitLeft.equals("1") && unitRight.equals("1")) {
+ unit = unitLeft;
+ }
+ else if (!unitLeft.equals("1") && !unitRight.equals("1")) {
+ unit = unitCalculator(unitLeft, unitRight);
+ }
BigDecimal value = divideHelper(((Quantity) left).getValue(), ((Quantity) right).getValue());
- return new Quantity().withValue(Value.verifyPrecision(value, null)).withUnit(((Quantity) left).getUnit());
+ return new Quantity().withValue(Value.verifyPrecision(value, null)).withUnit(unit);
}
else if (left instanceof Quantity && right instanceof BigDecimal) {
@@ -73,6 +91,27 @@ else if (left instanceof Interval && right instanceof Interval) {
);
}
+ public static String unitCalculator(String s1, String s2) {
+ Pattern integerPattern = Pattern.compile("-?\\d+");
+ Matcher matcher1 = integerPattern.matcher(s1);
+ Matcher matcher2 = integerPattern.matcher(s2);
+ int exp1 = 1, exp2 = 1;
+ String root = s1;
+ if (matcher1.find()) {
+ exp1 = Integer.parseInt(matcher1.group());
+ root = s1.substring(0, s1.indexOf(matcher1.group()));
+ }
+ if (matcher2.find()) {
+ exp2 = Integer.parseInt(matcher2.group());
+ }
+ int exp = exp1 - exp2;
+ if (exp == 0)
+ root = "1";
+ else if (exp > 1 || exp < 0)
+ root += String.valueOf(exp);
+ return root;
+ }
+
@Override
protected Object internalEvaluate(Context context) {
Object left = getOperand().get(0).evaluate(context);
diff --git a/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/LogEvaluator.java b/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/LogEvaluator.java
index fca86da4e..f2966c76b 100644
--- a/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/LogEvaluator.java
+++ b/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/LogEvaluator.java
@@ -22,6 +22,10 @@ public static Object log(Object left, Object right) {
}
if (left instanceof BigDecimal) {
+ //@@@CQF-1348 handle log1base1 as special case
+ if (((BigDecimal)left).intValue() == 1 && ((BigDecimal)right).intValue() == 1) {
+ return null;
+ }
Double base = Math.log(((BigDecimal)right).doubleValue());
Double value = Math.log(((BigDecimal)left).doubleValue());
diff --git a/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MultiplyEvaluator.java b/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MultiplyEvaluator.java
index ee4b94211..39f58b6ac 100644
--- a/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MultiplyEvaluator.java
+++ b/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/MultiplyEvaluator.java
@@ -1,6 +1,8 @@
package org.opencds.cqf.cql.engine.elm.execution;
import java.math.BigDecimal;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.opencds.cqf.cql.engine.exception.InvalidOperatorArgument;
import org.opencds.cqf.cql.engine.execution.Context;
@@ -48,8 +50,20 @@ else if (left instanceof BigDecimal && right instanceof BigDecimal) {
// *(Quantity, Quantity)
else if (left instanceof Quantity && right instanceof Quantity) {
- // TODO: unit multiplication i.e. cm*cm = cm^2
- String unit = ((Quantity) left).getUnit().equals("1") ? ((Quantity) right).getUnit() : ((Quantity) left).getUnit();
+ //@@@CQF-1348 unit calculation in multiplication
+ String unit = "1";
+ String unitLeft = ((Quantity) left).getUnit();
+ String unitRight = ((Quantity) right).getUnit();
+ if (unitLeft.equals("1") && !unitRight.equals("1") ) {
+ unit = unitRight;
+ }
+ else if (!unitLeft.equals("1") && unitRight.equals("1")) {
+ unit = unitLeft;
+ }
+ else if (!unitLeft.equals("1") && !unitRight.equals("1")) {
+ unit = unitCalculator(unitLeft, unitRight);
+ }
+ //String unit = ((Quantity) left).getUnit().equals("1") ? ((Quantity) right).getUnit() : ((Quantity) left).getUnit();
BigDecimal value = Value.verifyPrecision((((Quantity)left).getValue()).multiply(((Quantity)right).getValue()), null);
return new Quantity().withValue(value).withUnit(unit);
}
@@ -79,6 +93,22 @@ else if (left instanceof Interval && right instanceof Interval) {
);
}
+ private static String unitCalculator(String s1, String s2) {
+ Pattern integerPattern = Pattern.compile("-?\\d+");
+ Matcher matcher1 = integerPattern.matcher(s1);
+ Matcher matcher2 = integerPattern.matcher(s2);
+ int exp1 = 1, exp2 = 1;
+ String root = s1;
+ if (matcher1.find()) {
+ exp1 = Integer.parseInt(matcher1.group());
+ root = s1.substring(0, s1.indexOf(matcher1.group()));
+ }
+ if (matcher2.find()) {
+ exp2 = Integer.parseInt(matcher2.group());
+ }
+ return root + String.valueOf(exp1+ exp2);
+ }
+
@Override
protected Object internalEvaluate(Context context) {
Object left = getOperand().get(0).evaluate(context);
diff --git a/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/TruncatedDivideEvaluator.java b/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/TruncatedDivideEvaluator.java
index 816c312dc..0803e6fa3 100644
--- a/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/TruncatedDivideEvaluator.java
+++ b/engine/src/main/java/org/opencds/cqf/cql/engine/elm/execution/TruncatedDivideEvaluator.java
@@ -32,6 +32,15 @@ public static Object div(Object left, Object right) {
return (Integer)left / (Integer)right;
}
+ //@@@CQF-1348 handle Long data type
+ if (left instanceof Long) {
+ if ((Long)right == 0) {
+ return null;
+ }
+
+ return (Long)left / (Long)right;
+ }
+
else if (left instanceof BigDecimal) {
if (EqualEvaluator.equal(right, new BigDecimal("0.0"))) {
return null;
@@ -44,8 +53,24 @@ else if (left instanceof Quantity) {
if (EqualEvaluator.equal(((Quantity) right).getValue(), new BigDecimal("0.0"))) {
return null;
}
+ //@@@CQF-1348 unit calculation in division
+ String unit = ((Quantity) left).getUnit();
+ if (right instanceof Quantity) {
+ String unitLeft = ((Quantity) left).getUnit();
+ String unitRight = ((Quantity) right).getUnit();
+ if (unitLeft.equals("1") && !unitRight.equals("1")) {
+ throw new InvalidOperatorArgument(
+ "Dividend and divisor must have the same unit",
+ String.format("Divide(%s, %s)", ((Quantity) left).getUnit(), ((Quantity) right).getUnit())
+ );
+ } else if (!unitLeft.equals("1") && unitRight.equals("1")) {
+ unit = unitLeft;
+ } else if (!unitLeft.equals("1") && !unitRight.equals("1")) {
+ unit = DivideEvaluator.unitCalculator(unitLeft, unitRight);
+ }
+ }
return new Quantity()
- .withUnit(((Quantity) left).getUnit())
+ .withUnit(unit)
.withValue(((Quantity) left).getValue().divideAndRemainder(((Quantity) right).getValue())[0]);
}
diff --git a/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java b/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java
index 036b7ffea..66292edd9 100644
--- a/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java
+++ b/engine/src/test/java/org/opencds/cqf/cql/engine/execution/CqlArithmeticFunctionsTest.java
@@ -280,7 +280,9 @@ public void testLog() {
assertThat(result, is(nullValue()));
result = context.resolveExpressionRef("Log1Base1").getExpression().evaluate(context);
- assertThat((BigDecimal)result, comparesEqualTo(new BigDecimal(0d)));
+ //@@@CQF-1348 handle log1base1 as special case
+ assertThat(result, is(nullValue()));
+ //assertThat((BigDecimal)result, comparesEqualTo(new BigDecimal(0d)));
result = context.resolveExpressionRef("Log1Base2").getExpression().evaluate(context);
assertThat((BigDecimal)result, comparesEqualTo(new BigDecimal(0d)));
@@ -860,11 +862,12 @@ public void testTruncatedDivide() {
result = context.resolveExpressionRef("TruncatedDivide10By5DQuantity").getExpression().evaluate(context);
assertThat(((Quantity)result).getValue(), comparesEqualTo(new BigDecimal("2.0")));
- assertThat(((Quantity)result).getUnit(), is("g"));
+ //@@@CQF-1348 unit calculation in division
+ assertThat(((Quantity)result).getUnit(), is("1"));
result = context.resolveExpressionRef("TruncatedDivide414By206DQuantity").getExpression().evaluate(context);
assertThat(((Quantity)result).getValue(), comparesEqualTo(new BigDecimal("2.0")));
- assertThat(((Quantity)result).getUnit(), is("m"));
+ assertThat(((Quantity)result).getUnit(), is("1"));
result = context.resolveExpressionRef("TruncatedDivide10By0DQuantity").getExpression().evaluate(context);
assertThat(result, nullValue());