Skip to content
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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
35 changes: 35 additions & 0 deletions src/commonMain/kotlin/com/notkamui/keval/KevalBuilder.kt
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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]) },
Expand All @@ -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).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 } },
Expand Down