Sally

Sally is a simple solver for Simple Algebraic Linear equations.
Platform-agnostic as a common module in a Kotlin/Multiplatform library.

Why?

Occasionally, even in commercial programming you may run into a problem, which can be represented by a simple equation.
Let’s assume, we run a logistic business.
For example, basic business requirement may be to calculate box volume:

boxVolume

fun boxVolume(length: Double, width: Double, height: Double) = length * width * height

The next business requirement may be to calculate length, width or height based on any other 3 known parameters.

fun boxLength(volume: Double, width: Double, height: Double) = volume / width / height
fun boxWidth(volume: Double, length: Double, height: Double) = volume / length / height
fun boxHeight(volume: Double, length: Double, width: Double) = volume / length / width

It is obvious that for the same relationship, which can be represented by a single formula we need 4 methods and tests for each of those.

As an option, we may represent relationship as an expression and delegate methods to that expression.

private fun boxVolume(length: Expr, width: Expr, height: Expr, volume: Expr) = (
    volume - length * width * height
).solve()

fun boxVolume(length: Double, width: Double, height: Double) = boxVolume(
    length = length.asExpr(),
    width = width.asExpr(),
    height = height.asExpr(),
    volume = x
)
fun boxLength(volume: Double, width: Double, height: Double) = boxVolume(
    length = x,
    width = width.asExpr(),
    height = height.asExpr(),
    volume = volume.asExpr()
)
fun boxWidth(volume: Double, length: Double, height: Double) = boxVolume(
    length = length.asExpr(),
    width = x,
    height = height.asExpr(),
    volume = volume.asExpr()
)
fun boxHeight(volume: Double, length: Double, width: Double) = boxVolume(
    length = length.asExpr(),
    width = width.asExpr(),
    height = x,
    volume = volume.asExpr()
)

How-to

Unknown part is marked with an x.

val expr = x - 1
println(expr.solve()) // 1

For explicit conversion of number to expressions use asExpr()

val expr = 10.asExpr() - x * 2.asExpr()
println(expr.solve()) // 5

If there is no x in expression, it will be solved as a simple expression.

val expr = 10.asExpr() - 3 * 2
println(expr.solve()) // 4

More complicated example is in jvmMain/kotlin/main.kt

fun bank(
    fv: Expr,
    pv: Expr, pmt: Expr,
    rate: Double,
    m: Int, n: Int
): Double {
    val i = (rate / 100.00 / m).asExpr()
    val mn = m * n

    return (
        fv - pv * (1 + i).pow(mn) - pmt * ((1 + i).pow(mn) - 1) / i
    ).solve()
}

fun deposit() {
    val fv = bank(
        fv = x,
        pv = 1000.00.asExpr(),
        pmt = 0.asExpr(),
        rate = 10.00,
        m = 12,
        n = 2
    )
    "Deposit FV = %.2f".format(fv).let(::println)
}

fun credit() {
    val pv = 1000.00
    val i = 5.00
    val n = 2

    val fv = bank(
        fv = x,
        pv = pv.asExpr(),
        pmt = 0.asExpr(),
        rate = i,
        m = 1,
        n = n
    )

    val pmt = bank(
        fv = fv.asExpr(),
        pv = 0.asExpr(),
        pmt = x,
        rate = i,
        m = 12,
        n = n
    )

    "Credit PMT = %.2f".format(pmt).let(::println)
}

fun main() {
    deposit()
    credit()
}

Reasoning

Consider common financial formula that represents relationship between Future Value (FV), Present Value (PV), and Periodic Payments (PMT).

fv - pv*(1+i/m)^mn - pmt*((1+i/m)^(mn)-1)/i/m

Where:
is an annual rate.
is a number of years.
is a number of periods of capitalization of interest per year.

For a simple deposit for 2 years with a rate of 10% and an initial amount of 1000 we would need next formula:

For a credit for 2 years with a rate of 10% and an initial amount of 1000 we may use a set of 2 formulas:
This is not an optimal solution, but it allows us to use same relationship.
Firstly, we have to find FV, the value of a loan in 2 years.

Then, we have to find out monthly payments:

As one can observe, same formula is used in all the examples, but in some cases FV, PV or PMT is equal to zero.
However, as a code naive solution would be represented by 3 methods, one for each unknown variable FV, PV or PMT.
This results in an unnecessary growth of codebase, which can be substituted with a simple algebraic solver for linear equations.

Installation

Fork or use Jitpack.
Maven support will be done on request.

GitHub

View Github