Giter Club home page Giter Club logo

multiplatform-expressions-evaluator's Introduction

Kotlin multiplatform expressions evaluator

Version

This is a kotlin multiplatform runtime infix expressions evaluator.

Overview

Operators

The library supports the following operators and special symbols:

  • +, -, *, / - mathematical operators
  • % - modulo. Returns the remainder of a division, after one number is divided by another
  • ^ - exponentiation. a^b means a raised to the power of b
  • &&, ||, ! - logical 'and', 'or', 'not' operators
  • ==, != - equality operators
  • <, >, <=, >= - comparison operators
  • a ? b : c - ternary operator

Constants

The library supports the following constants:

  • pi - PI
  • e - e
  • any custom constants.

Functions

The library supports the following functions:

Variables

Any symbols other than constants and function calls are resolved as variables during evaluation.

How to get

Gradle

The library is distributed via maven central repositories.

Kotlin DSL

repositories {
    mavenCentral()
}

dependencies {
    implementation("io.github.murzagalin:multiplatform-expressions-evaluator:x.y.z")
}

Groovy

repositories {
    mavenCentral()
}

dependencies {
    implementation "io.github.murzagalin:multiplatform-expressions-evaluator:x.y.z"
}

Kotlin

Usage

import com.github.murzagalin.evaluator.Evaluator

val evaluator = Evaluator()

//numeric values
evaluator.evaluateDouble("23 + 0.123 * (124 / -60.124)")

//boolean values
evaluator.evaluateBoolean(
    "x > 1 && var",
    mapOf("x" to 3, "var" to true)
)

//variables
evaluator.evaluateDouble(
    "1 + x + y",
    mapOf("x" to 3, "y" to 4)
)

//functions
evaluator.evaluateDouble(
    "sin(x)^2+cos(x)^2",
    mapOf("x" to 2.0)
)

//constants
evaluator.evaluateDouble("cos(pi)")
evaluator.evaluateDouble("ln(e^3)")

Custom functions

The library supports custom functions with any number of arguments. Supported argument and return types are Double and Boolean

Functions with constant number of arguments

For the example I will explain how to create a function that represents the general form of normal distribution probability density function: img.png

The function will have the following syntax: normal_dist(x, m, sigma). The parameter m is the mean of the distribution, while the parameter sigma is its standard deviation.

We define a function which is named "normal_dist" and has 3 arguments:

import com.github.murzagalin.evaluator.Function

object NormalDist : Function("normal_dist", 3) {
    override fun invoke(vararg args: Any): Any {
        val x = args.getAsDouble(0) { "$name argument must be a number" }
        val m = args.getAsDouble(1) { "$name mean must be a number" }
        val sigma = args.getAsDouble(2) { "$name sigma must be a number" }

        return 1.0 / sigma / sqrt(2 * PI) * exp(-1.0 / 2.0 * ((x - m) / sigma).pow(2))
    }
}

Note: the library checks if the number of arguments in an expression is equal to 3, otherwise it throws an exception. But you have to check the types of the arguments by yourself. Functions getAsDouble(index, lazyMessage) and getAsBoolean(index, lazyMessage) return an element at position index, and throw IllegalArgumentException with the message returned from lazyMessage if it has a wrong type

Then we add this function to the evaluator:

import com.github.murzagalin.evaluator.DefaultFunctions
import com.github.murzagalin.evaluator.Evaluator

fun main() {
    val evaluator = Evaluator(functions = DefaultFunctions.ALL + NormalDist)
    print(evaluator.evaluateDouble("normal_dist(12, 9, 3)"))
}

Functions with variable number of arguments

The process of creating functions with variable number of arguments is pretty much the same. The difference is how we define the function.

As an example I will create a function mult(a1, a2, ..., an) which is defined as a1 * a2 * ... * an:

object Mult: Function("mult", 2..Int.MAX_VALUE) {
    override fun invoke(vararg args: Any): Any {
        require(args.all { it is Double }) { "$name function requires all arguments to be numbers" }

        return args.fold(1.0) { acc, x -> acc * (x as Double) }
    }
}

Note: we define minimum and maximum number of arguments as a range. It is also possible to define them separately: Function("mult", 2, Int.MAX_VALUE)

Then we add this function to the evaluator:

import com.github.murzagalin.evaluator.DefaultFunctions
import com.github.murzagalin.evaluator.Evaluator

fun main() {
    val evaluator = Evaluator(functions = DefaultFunctions.ALL + Mult)
    print(evaluator.evaluateDouble("mult(2, 3, 4)"))
}

Custom constants

The library supports custom constants. For the example I will show you how to add a golden ratio constant. We will define the constant named phi with the value 1.6180339887:

import com.github.murzagalin.evaluator.Constant
import com.github.murzagalin.evaluator.DefaultConstants
import com.github.murzagalin.evaluator.Evaluator

fun main() {
    val PHI = Constant("phi", 1.6180339887)
    val evaluator = Evaluator(constants = DefaultConstants.ALL + PHI)
    print(evaluator.evaluateDouble("x * phi", mapOf("x" to 2)))
}

Expressions preprocessing

By default, the library does the following steps to evaluate an expression:

  1. Tokenizing - splitting the expression into a list of units (operations, numbers, constants, function calls, etc.)
  2. Converting the expression from infix notation to abstract syntax tree.
  3. Evaluating the abstract syntax tree.

In case you have an expression with variables, it might make sense to preprocess the expression (do steps 1 and 2 in advance) to improve the performance:

import com.github.murzagalin.evaluator.Evaluator

fun main() {
    val evaluator = Evaluator()
    
    //step 1 and 2
    val preprocessedExpression = evaluator.preprocessExpression("1 + x + y^2")
    
    //step 3
    val result = evaluator.evaluateDouble(preprocessedExpression, mapOf("x" to 2, "y" to 4))
}

JVM

TBD

JS

TBD

IOS

TBD

License

This library is available for free under Apache 2.0 license.

Copyright (c) 2021 Azamat Murzagalin.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

multiplatform-expressions-evaluator's People

Contributors

flixlo avatar murzagalin avatar xian avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

multiplatform-expressions-evaluator's Issues

Expressions Evaluator package requires authentication

Hi again @murzagalin!

After trying to add your library to my kotlin multiplatform project, the gradle build failed with an error. So I tried to setup a new empty test project (via the project wizard of Android Studio) to verify that my configuration is not responsible for the error.

I tested with a new Kotlin Multiplatform Library and a Kotlin Multiplatform App project. In both cases, I added your library as suggested in the README.

    repositories {
        maven {
            url = uri("https://maven.pkg.github.com/murzagalin/multiplatform-expressions-evaluator")
        }
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("com.github.murzagalin:multiplatform-expressions-evaluator:0.11.0")
            }
        }
        // other stuff ...
    }

Building the project gives the following error in both cases

Could not GET 'https://maven.pkg.github.com/murzagalin/multiplatform-expressions-evaluator/com/github/murzagalin/multiplatform-expressions-evaluator/0.11.0/multiplatform-expressions-evaluator-0.11.0.pom'. Received status code 401 from server: Unauthorized
Disable Gradle 'offline mode' and sync project

Manually trying to access https://maven.pkg.github.com/murzagalin/multiplatform-expressions-evaluator/com/github/murzagalin/multiplatform-expressions-evaluator/0.11.0/multiplatform-expressions-evaluator-0.11.0.pom also requires authentication.

It appears that the Github Package is private. Is that intended, or is it possible to make it publicly accessible without authentication?

Update

Hi
This library is very good and helpful
Please update the version of libraries that used in it.
Thank you.

Complex expressions are not working

I have an expression like this -> "(0.341 * 8000.0) / (1 - (1 + 0.341) ^ -84)"

Expected result - "109.71908469357446"
Actual result - "1.538906384258725e-36"

calculation should be done like below snippet

fun calculateMonthlyInstallment(loanAmount: Double, monthlyInterestRate: Double, numberOfMonths: Int): Double {
    val monthlyInterestRateDecimal = monthlyInterestRate / 100.0  // Convert percentage to decimal
    val power = Math.pow(1 + monthlyInterestRateDecimal, -numberOfMonths.toDouble())
    return (monthlyInterestRateDecimal * loanAmount) / (1 - power)
}

fun main() {
    val loanAmount = 8000.0
    val monthlyInterestRate = 0.341  // 0.5% interest rate
    val numberOfMonths = 84
    val monthlyInstallment = calculateMonthlyInstallment(loanAmount, monthlyInterestRate, numberOfMonths)
    println("Monthly Installment: $monthlyInstallment")
}

Distribution via Maven Central

Hi @murzagalin!

At the moment, multiplatform-expressions-evaluator is distributed via Github Packages. The fact, that Github Packages requires authentication to fetch dependencies makes the setup kinda cumbersome. For example, in a team of multiple developers working on the same project, each developer needs to manually configure the authentication credentials. Also, if you want to use CI & CD it requires additional configuration for the credentials.

That's why I'd highly recommend to upload the library to the Maven Central Repository. This would also make it easier for other developers to use your library, because it drastically simplifies the setup process.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.