soma.cell

Compound interest: future value, with periodic compounding and a continuous-compounding variant

Pure numeric handlers, tolerance-based float assertions (never == on floats).

A Soma program from the finance part of the verified corpus: 46 lines, cells CompoundInterest, CompoundTests. It passes soma check with soma 3.2.4.

How it is built

The cell CompoundInterest declares 4 signals in its face: future_value(principal: Float, rate: Float, years: Int) -> Float, future_value_n(principal: Float, rate: Float, n: Int, years: Int) -> Float, continuous(principal: Float, rate: Float, years: Float) -> Float and doubling_time(rate: Float) -> Float. Handlers: future_value (line 14), future_value_n (line 19), continuous (line 24) and doubling_time (line 29).

The file also holds 1 test cell (CompoundTests) with 4 assertions, run by soma test.

Generated from soma describe on this file.

The source

compound_interest.cell

// Compound interest: future value, with periodic compounding and a
// continuous-compounding variant. Pure numeric handlers, tolerance-based
// float assertions (never == on floats).

cell CompoundInterest {
    face {
        signal future_value(principal: Float, rate: Float, years: Int) -> Float
        signal future_value_n(principal: Float, rate: Float, n: Int, years: Int) -> Float
        signal continuous(principal: Float, rate: Float, years: Float) -> Float
        signal doubling_time(rate: Float) -> Float
    }

    // FV = P * (1 + r)^t   (annual compounding)
    on future_value(principal: Float, rate: Float, years: Int) {
        return principal * pow(1.0 + rate, years)
    }

    // FV = P * (1 + r/n)^(n*t)  (n compounding periods per year)
    on future_value_n(principal: Float, rate: Float, n: Int, years: Int) {
        return principal * pow(1.0 + rate / n, n * years)
    }

    // FV = P * e^(r*t)  (continuous compounding)
    on continuous(principal: Float, rate: Float, years: Float) {
        return principal * exp(rate * years)
    }

    // Rule of ln(2): years to double = ln(2) / ln(1 + r)
    on doubling_time(rate: Float) {
        return log(2.0) / log(1.0 + rate)
    }
}

cell test CompoundTests {
    rules {
        // 1000 at 5% for 2 years = 1102.50
        assert abs(future_value(1000.0, 0.05, 2) - 1102.5) < 0.001
        // monthly compounding beats annual
        assert future_value_n(1000.0, 0.12, 12, 1) > future_value(1000.0, 0.12, 1)
        // continuous is the upper bound
        assert abs(continuous(1000.0, 0.10, 1.0) - 1105.170918) < 0.001
        // at 7%, doubling takes ~10.24 years
        assert abs(doubling_time(0.07) - 10.244768) < 0.001
    }
}

What the toolchain says

Run on a fresh copy of the program at build time; the outputs are pasted unedited.

$ soma check compound_interest.cell
✓ All checks passed.
$ soma test compound_interest.cell
test CompoundTests ...
  ✓ assert abs(future_value(1000.0, 0.05, 2) - 1102.5) < 0.001
  ✓ assert future_value_n(1000.0, 0.12, 12, 1) > future_value(1000.0, 0.12, 1)
  ✓ assert abs(continuous(1000.0, 0.10, 1.0) - 1105.170918) < 0.001
  ✓ assert abs(doubling_time(0.07) - 10.244768) < 0.001

4 tests: 4 passed, 0 failed
$ soma verify compound_interest.cell
Verifying compound_interest.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
VERIFY OK (vacuous — no state machine)

Builtins used

abs, exp, log, pow

Source file: /corpus/finance/compound_interest.cell · examples/corpus/finance/compound_interest.cell