diff --git a/api/expression-parser.api b/api/expression-parser.api index bccf6dd..00d1425 100644 --- a/api/expression-parser.api +++ b/api/expression-parser.api @@ -113,6 +113,7 @@ public final class org/hisp/dhis/lib/expression/ast/NamedFunction : java/lang/En public static final field d2_countIfValue Lorg/hisp/dhis/lib/expression/ast/NamedFunction; public static final field d2_countIfZeroPos Lorg/hisp/dhis/lib/expression/ast/NamedFunction; public static final field d2_daysBetween Lorg/hisp/dhis/lib/expression/ast/NamedFunction; + public static final field d2_exponent Lorg/hisp/dhis/lib/expression/ast/NamedFunction; public static final field d2_extractDataMatrixValue Lorg/hisp/dhis/lib/expression/ast/NamedFunction; public static final field d2_floor Lorg/hisp/dhis/lib/expression/ast/NamedFunction; public static final field d2_hasUserRole Lorg/hisp/dhis/lib/expression/ast/NamedFunction; @@ -122,6 +123,7 @@ public final class org/hisp/dhis/lib/expression/ast/NamedFunction : java/lang/En public static final field d2_lastEventDate Lorg/hisp/dhis/lib/expression/ast/NamedFunction; public static final field d2_left Lorg/hisp/dhis/lib/expression/ast/NamedFunction; public static final field d2_length Lorg/hisp/dhis/lib/expression/ast/NamedFunction; + public static final field d2_log Lorg/hisp/dhis/lib/expression/ast/NamedFunction; public static final field d2_maxValue Lorg/hisp/dhis/lib/expression/ast/NamedFunction; public static final field d2_minValue Lorg/hisp/dhis/lib/expression/ast/NamedFunction; public static final field d2_minutesBetween Lorg/hisp/dhis/lib/expression/ast/NamedFunction; @@ -1398,6 +1400,7 @@ public abstract interface class org/hisp/dhis/lib/expression/spi/ExpressionFunct public fun d2_countIfValue (Lorg/hisp/dhis/lib/expression/spi/VariableValue;Ljava/lang/String;)I public fun d2_countIfZeroPos (Lorg/hisp/dhis/lib/expression/spi/VariableValue;)I public fun d2_daysBetween (Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I + public fun d2_exponent (Ljava/lang/Number;Ljava/lang/Number;)D public fun d2_extractDataMatrixValue (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; public fun d2_floor (Ljava/lang/Number;)D public fun d2_hasUserRole (Ljava/lang/String;Ljava/util/List;)Z @@ -1460,6 +1463,7 @@ public final class org/hisp/dhis/lib/expression/spi/ExpressionFunctions$DefaultI public static fun d2_countIfValue (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Lorg/hisp/dhis/lib/expression/spi/VariableValue;Ljava/lang/String;)I public static fun d2_countIfZeroPos (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Lorg/hisp/dhis/lib/expression/spi/VariableValue;)I public static fun d2_daysBetween (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Lkotlinx/datetime/LocalDate;Lkotlinx/datetime/LocalDate;)I + public static fun d2_exponent (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Ljava/lang/Number;Ljava/lang/Number;)D public static fun d2_extractDataMatrixValue (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; public static fun d2_floor (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Ljava/lang/Number;)D public static fun d2_hasUserRole (Lorg/hisp/dhis/lib/expression/spi/ExpressionFunctions;Ljava/lang/String;Ljava/util/List;)Z diff --git a/build.gradle.kts b/build.gradle.kts index d24ca67..7456fd5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,7 +11,7 @@ repositories { mavenCentral() } -version = "1.4.2-SNAPSHOT" +version = "1.4.3-SNAPSHOT" group = "org.hisp.dhis.lib.expression" if (project.hasProperty("removeSnapshotSuffix")) { diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/NamedFunction.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/NamedFunction.kt index 014a8e9..ea94282 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/NamedFunction.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/NamedFunction.kt @@ -55,6 +55,7 @@ enum class NamedFunction( d2_countIfValue("d2:countIfValue", ValueType.NUMBER, ValueType.MIXED, ValueType.MIXED), d2_countIfZeroPos("d2:countIfZeroPos", ValueType.NUMBER, ValueType.NUMBER), d2_daysBetween("d2:daysBetween", ValueType.NUMBER, ValueType.DATE, ValueType.DATE), + d2_exponent("d2:exponent", ValueType.NUMBER, ValueType.NUMBER), d2_extractDataMatrixValue("d2:extractDataMatrixValue", ValueType.STRING, ValueType.STRING, ValueType.STRING), d2_floor("d2:floor", ValueType.NUMBER, ValueType.NUMBER), d2_hasUserRole("d2:hasUserRole", ValueType.BOOLEAN, ValueType.STRING), @@ -64,6 +65,7 @@ enum class NamedFunction( d2_lastEventDate("d2:lastEventDate", ValueType.DATE, ValueType.STRING), d2_left("d2:left", ValueType.STRING, ValueType.STRING, ValueType.NUMBER), d2_length("d2:length", ValueType.NUMBER, ValueType.STRING), + d2_log("d2:log", ValueType.NUMBER, ValueType.NUMBER, ValueType.NUMBER), d2_maxValue("d2:maxValue", ValueType.NUMBER, ValueType.MIXED), d2_minutesBetween("d2:minutesBetween", ValueType.NUMBER, ValueType.DATE, ValueType.DATE), d2_minValue("d2:minValue", ValueType.NUMBER, ValueType.MIXED), diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/eval/Calculator.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/eval/Calculator.kt index 57fc36e..c92586b 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/eval/Calculator.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/eval/Calculator.kt @@ -68,7 +68,7 @@ internal class Calculator( NamedFunction.isNotNull -> functions.isNotNull(evalToMixed(fn.child(0))) NamedFunction.isNull -> functions.isNull(evalToMixed(fn.child(0))) NamedFunction.least -> functions.least(evalToNumbers(fn.children())) - NamedFunction.log -> if (fn.size() == 1) functions.log(evalToNumber(fn.child(0))) + NamedFunction.log, NamedFunction.d2_log -> if (fn.size() == 1) functions.log(evalToNumber(fn.child(0))) else functions.log(evalToNumber(fn.child(0))) / functions.log(evalToNumber(fn.child(1))) NamedFunction.log10 -> functions.log10(evalToNumber(fn.child(0))) NamedFunction.removeZeros -> functions.removeZeros(evalToNumber(fn.child(0))) @@ -124,6 +124,9 @@ internal class Calculator( evalToDate(fn.child(0)), evalToDate(fn.child(1))) NamedFunction.d2_oizp -> functions.d2_oizp(evalToNumber(fn.child(0))) + NamedFunction.d2_exponent -> functions.d2_exponent( + evalToNumber(fn.child(0)), + evalToNumber(fn.child(1))) NamedFunction.d2_right -> functions.d2_right( evalToString(fn.child(0)), evalToInteger(fn.child(1))) diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/ExpressionFunctions.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/ExpressionFunctions.kt index 007cbed..eea619a 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/ExpressionFunctions.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/ExpressionFunctions.kt @@ -10,6 +10,7 @@ import org.hisp.dhis.lib.expression.math.ZScore import kotlin.math.ceil import kotlin.math.floor import kotlin.math.ln +import kotlin.math.pow /** * Implementation API for all expression languages functions. @@ -208,6 +209,10 @@ fun interface ExpressionFunctions { return start.daysUntil(end) } + fun d2_exponent(base: Number?, exponent: Number?): Double { + return (base ?: 1.0).toDouble().pow((exponent ?: 0).toDouble()) + } + fun d2_extractDataMatrixValue(gs1Key: String?, value: String?): String? { return fromKey(gs1Key!!).format(value) } diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/ExpressionGrammar.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/ExpressionGrammar.kt index 0398e35..97df64c 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/ExpressionGrammar.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/ExpressionGrammar.kt @@ -104,7 +104,9 @@ object ExpressionGrammar { fn(NamedFunction.d2_count, dataItem), fn(NamedFunction.d2_countIfValue, dataItem, expr), fn(NamedFunction.d2_daysBetween, expr, expr), + fn(NamedFunction.d2_exponent, expr, expr), fn(NamedFunction.d2_hasValue, dataItem), + fn(NamedFunction.d2_log, expr, expr.maybe()), fn(NamedFunction.d2_maxValue, dataItem), fn(NamedFunction.d2_minutesBetween, expr, expr), fn(NamedFunction.d2_minValue, dataItem), diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/ExponentTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/ExponentTest.kt new file mode 100644 index 0000000..9f936ed --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/ExponentTest.kt @@ -0,0 +1,37 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import org.hisp.dhis.lib.expression.ExpressionMode +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests the `d2:exponent` function + * + * @author Tony Valle + */ +internal class ExponentTest { + + @Test + fun testExponent() { + assertEquals(1.0, evaluate("d2:exponent(49, 0)")) + assertEquals(7.0, evaluate("d2:exponent(49, 0.5)")) + assertEquals(49.0, evaluate("d2:exponent(49, 1)")) + assertEquals(64.0, evaluate("d2:exponent(32, 1.2)") as Double, errorMargin) + assertEquals(25.0, evaluate("d2:exponent(0.2, -2)") as Double, errorMargin) + } + + @Test + fun testExponent_Whitespace() { + assertEquals(2.0, evaluate("d2:exponent(2,1)")) + assertEquals(2.0, evaluate("d2:exponent( 2, 1)")) + assertEquals(2.0, evaluate("d2:exponent(2 , 1 )")) + assertEquals(2.0, evaluate("d2:exponent( 2 , 1 )")) + } + + private val errorMargin: Double = 0.00000001 + + private fun evaluate(expression: String): Any? { + return Expression(expression, ExpressionMode.RULE_ENGINE_ACTION).evaluate() + } +} diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LogTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LogTest.kt index b4070d5..2414087 100644 --- a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LogTest.kt +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LogTest.kt @@ -1,6 +1,7 @@ package org.hisp.dhis.lib.expression.function import org.hisp.dhis.lib.expression.Expression +import org.hisp.dhis.lib.expression.ExpressionMode import kotlin.test.Test import kotlin.test.assertEquals @@ -25,6 +26,11 @@ internal class LogTest { assertEquals(Double.POSITIVE_INFINITY, evaluate("log( 2 , 1 )")) } + @Test + fun testD2Log() { + assertEquals(3.0, Expression("d2:log(8, 2)", ExpressionMode.RULE_ENGINE_ACTION).evaluate()) + } + private fun evaluate(expression: String): Any? { return Expression(expression).evaluate() }