From 71741029fb5179b05f4379a723ac3c0933f69e3b Mon Sep 17 00:00:00 2001 From: EgorBron <71507444+EgorBron@users.noreply.github.com> Date: Mon, 21 Apr 2025 20:06:41 +0300 Subject: [PATCH 1/3] Add more functions closes #56 --- .../kotlin/com/notkamui/keval/KevalBuilder.kt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/commonMain/kotlin/com/notkamui/keval/KevalBuilder.kt b/src/commonMain/kotlin/com/notkamui/keval/KevalBuilder.kt index 90c6eda..4280cf8 100644 --- a/src/commonMain/kotlin/com/notkamui/keval/KevalBuilder.kt +++ b/src/commonMain/kotlin/com/notkamui/keval/KevalBuilder.kt @@ -1,6 +1,7 @@ package com.notkamui.keval import kotlin.math.* +import kotlin.random.Random /** * This class is used to build a Keval instance with custom operators, functions, and constants. @@ -156,9 +157,11 @@ class KevalBuilder internal constructor( // functions "neg" to KevalFunction(1) { -it[0] }, + "sign" to KevalFunction(1) { if (it[0] < 0) -1.0 else if (it[0] > 0) 1.0 else 0.0 }, "abs" to KevalFunction(1) { it[0].absoluteValue }, "sqrt" to KevalFunction(1) { sqrt(it[0]) }, "cbrt" to KevalFunction(1) { cbrt(it[0]) }, + "nthrt" to KevalFunction(2) { it[1].pow(1 / it[0]) }, "exp" to KevalFunction(1) { exp(it[0]) }, "ln" to KevalFunction(1) { ln(it[0]) }, "log10" to KevalFunction(1) { log10(it[0]) }, @@ -172,8 +175,40 @@ class KevalBuilder internal constructor( "ceil" to KevalFunction(1) { ceil(it[0]) }, "floor" to KevalFunction(1) { floor(it[0]) }, "round" to KevalFunction(1) { round(it[0]) }, + "trunc" to KevalFunction(1) { it[0].toInt().toDouble() }, + "min" to KevalFunction(null) { it.min() }, + "max" to KevalFunction(null) { it.max() }, + "sum" to KevalFunction(null) { it.sum() }, + "avg" to KevalFunction(null) { it.average() }, + "median" to KevalFunction(null) { it.sorted()[it.size / 2] }, + "percentile" to KevalFunction(null) { + if (it.size > 1) throw KevalInvalidArgumentException("percentile requires at least 2 values") + val perc = it[0] + if (perc in 0.0..100.0) throw KevalInvalidArgumentException("percentile must be between 0 and 100") + val sorted = it.sorted() + val index = (perc / 100 * (sorted.size - 1)).toInt() + sorted[index] + }, + "rand" to KevalFunction(null) { + when (it.size) { + 0 -> Random.Default.nextDouble() + 1 -> (0..it[0].toInt()).random().toDouble() + else -> it.random() + } + }, + "randRange" to KevalFunction(3) { + val start = it[0] + val end = it[1] + val step = it[2] + + if (step > 0) throw KevalInvalidArgumentException("step must be greater than 0") + val numberOfSteps = ((end - start) / step).toInt() + val randomStepIndex = Random.nextInt(0, numberOfSteps + 1) + start + randomStepIndex * step + }, // logical functions + "bool" to KevalFunction(1) { booleanToDouble(it[0] != 0.0) }, "not" to KevalFunction(1) { booleanToDouble(!doubleToBoolean(it[0])) }, "and" to KevalFunction(null) { it.reduceBoolean { a, b -> a && b } }, "nand" to KevalFunction(null) { it.reduceBoolean(true) { a, b -> a && b } }, From de5819a154746ad1c3edfe1b3797eeaf48752c04 Mon Sep 17 00:00:00 2001 From: Egor Bron <71507444+EgorBron@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:15:01 +0300 Subject: [PATCH 2/3] Fix the new functions (apply suggestions from code review) Co-authored-by: notKamui <46132319+notKamui@users.noreply.github.com> --- src/commonMain/kotlin/com/notkamui/keval/KevalBuilder.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commonMain/kotlin/com/notkamui/keval/KevalBuilder.kt b/src/commonMain/kotlin/com/notkamui/keval/KevalBuilder.kt index 4280cf8..203a6ca 100644 --- a/src/commonMain/kotlin/com/notkamui/keval/KevalBuilder.kt +++ b/src/commonMain/kotlin/com/notkamui/keval/KevalBuilder.kt @@ -182,11 +182,11 @@ class KevalBuilder internal constructor( "avg" to KevalFunction(null) { it.average() }, "median" to KevalFunction(null) { it.sorted()[it.size / 2] }, "percentile" to KevalFunction(null) { - if (it.size > 1) throw KevalInvalidArgumentException("percentile requires at least 2 values") + if (it.size <= 1) throw KevalInvalidArgumentException("percentile requires at least 2 values") val perc = it[0] - if (perc in 0.0..100.0) throw KevalInvalidArgumentException("percentile must be between 0 and 100") + if (perc !in 0.0..100.0) throw KevalInvalidArgumentException("percentile must be between 0 and 100") val sorted = it.sorted() - val index = (perc / 100 * (sorted.size - 1)).toInt() + val index = ((perc / 100) * sorted.size).toInt() sorted[index] }, "rand" to KevalFunction(null) { From 1c977ea0775fb0d3ca5a8f17c40179cfe2ae9c9e Mon Sep 17 00:00:00 2001 From: Egor Bron <71507444+EgorBron@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:26:39 +0300 Subject: [PATCH 3/3] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 392ee5c..3a6cfe6 100644 --- a/README.md +++ b/README.md @@ -63,9 +63,11 @@ Keval has support for all classic unary operators: Keval has support for functions of variable arity: - Negate/Oppose `neg(expr)` (where 'expr' is an expression) +- Expression sign `sign(expr)` (where 'expr' is an expression) - Absolute `abs(expr)` (where 'expr' is an expression) - Square root `sqrt(expr)` (where 'expr' is an expression) - Cube root `cbrt(expr)` (where 'expr' is an expression) +- Root of nth power `nthrt(expr_a, expr_pow)` (where 'expr_a' is an expression and 'expr_pow' is target power) - Exponential `exp(expr)` (where 'expr' is an expression) - Natural logarithm `ln(expr)` (where 'expr' is an expression) - Base 10 logarithm `log10(expr)` (where 'expr' is an expression) @@ -79,7 +81,13 @@ Keval has support for functions of variable arity: - Ceiling `ceil(expr)` (where 'expr' is an expression) - Floor `floor(expr)` (where 'expr' is an expression) - Round `round(expr)` (where 'expr' is an expression) +- Truncate decimal part `trunc(expr)` (where 'expr' is an expression) +- Minimal/maximal/average/median value `min(expr...)`/`max(expr...)`/`avg(expr...)`/`median(expr...)` (where 'expr...' is any number of expressions to get results from) +- Percentile `percentile(expr_perc, expr_a)` (where 'expr_a' is the expression to get percentile from, and 'expr_perc' is the percent value) +- Random number generator/selector `rand(expr...)` (where 'expr...' is any number of expressions; either generates a random double (zero arguments), or generates a random integer up to expr (one argument), or selects a random value of given arguments (two or more arguments) +- Random number between 'start' and 'end' with 'step' `randRange(expr_start, expr_end, expr_step)` - Built-in boolean operations (0.0 is equivalent to false, anything else is equivalent to true): + - Convert to "boolean" form (0/1) `bool(expr)` (where 'expr' is an expression) - Invert `not(bool)` (where 'bool' is a boolean) - And `and(bool...)` (where 'bool...' is any number of booleans) - Not And `nand(bool...)` (where 'bool...' is any number of booleans)