Coin change, minimum number of coins to make an amount (unbounded knapsack)
This is the "fewest coins" variant — distinct from counting the number of ways. Demonstrates: a 1D DP seeded with a big sentinel, the min-over-coins recurrence, and returning -1 when an amount is unreachable.
A Soma program from the algorithms part of the verified corpus: 49 lines, cells MinCoins, MinCoinsTests. It passes soma check with soma 3.2.4.
How it is built
The cell MinCoins declares 2 signals in its face: fewest(amount: Int) -> Int and solve(coins: List, amount: Int) -> Int. Handlers: fewest (line 15) and solve (line 19).
The file also holds 1 test cell (MinCoinsTests) with 7 assertions, run by soma test.
The source
min_coins.cell
// Coin change, minimum number of coins to make an amount (unbounded knapsack).
// This is the "fewest coins" variant — distinct from counting the number of
// ways. Demonstrates: a 1D DP seeded with a big sentinel, the min-over-coins
// recurrence, and returning -1 when an amount is unreachable.
//
// soma run examples/corpus/algorithms/min_coins.cell fewest 11
// soma test examples/corpus/algorithms/min_coins.cell
cell MinCoins {
face {
signal fewest(amount: Int) -> Int
signal solve(coins: List, amount: Int) -> Int
}
on fewest(amount: Int) {
return solve(list(1, 2, 5), amount)
}
on solve(coins: List, amount: Int) {
let inf = 1000000
// dp[a] = fewest coins to make a, or inf if impossible
let dp = list()
for a in range(0, amount + 1) { dp = push(dp, inf) }
dp[0] = 0
for a in range(1, amount + 1) {
for c in coins {
if c <= a {
let cand = dp[a - c] + 1
if cand < dp[a] { dp[a] = cand }
}
}
}
if dp[amount] >= inf { return -1 }
return dp[amount]
}
}
cell test MinCoinsTests {
rules {
assert fewest(11) == 3 // 5 + 5 + 1
assert fewest(0) == 0
assert fewest(3) == 2 // 2 + 1
assert fewest(25) == 5 // 5*5
assert solve(list(2), 3) == -1
assert solve(list(2), 4) == 2
assert solve(list(1, 5, 10, 25), 30) == 2
}
}What the toolchain says
$ soma check min_coins.cell
✓ All checks passed.$ soma test min_coins.cell
test MinCoinsTests ...
✓ assert fewest(11) == 3
✓ assert fewest(0) == 0
✓ assert fewest(3) == 2
✓ assert fewest(25) == 5
✓ assert solve(list(2), 3) == -1
✓ assert solve(list(2), 4) == 2
✓ assert solve(list(1, 5, 10, 25), 30) == 2
7 tests: 7 passed, 0 failed$ soma verify min_coins.cell
Verifying min_coins.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)$ soma run min_coins.cell fewest 11
3