///- --package .

coins (test)

test coins: prelude
///- --test static --symbol

:< testing
:| coin =
    | GoldCoin
    | PlatinumCoin
    | CrystalCoin

:= worth (kind : coin) : i32 =
    ?= kind ?|
    | GoldCoin => 1
    | PlatinumCoin => 100
    | CrystalCoin => 10000

:== purse = { gold : i32; platinum : i32; crystal : i32 }

:= purse gold platinum crystal : purse = { gold platinum crystal }

:= total_gold (held : purse) : i32 =
    held::gold * worth GoldCoin + held::platinum * worth PlatinumCoin + held::crystal * worth CrystalCoin

:= fewest_coins (amount : i32) : purse =
    := crystal = amount / worth CrystalCoin
    := after_crystal = amount - crystal * worth CrystalCoin
    := platinum = after_crystal / worth PlatinumCoin
    purse (after_crystal - platinum * worth PlatinumCoin) platinum crystal

:| payment =
    | Paid : purse
    | ShortBy : i32

:= pay (price : i32) (held : purse) : payment =
    := available = total_gold held
    ? available < price ?> ShortBy (price - available) |: Paid (fewest_coins (available - price))

:= remaining_gold (outcome : payment) : i32 =
    ?= outcome ?|
    | Paid held => total_gold held
    | ShortBy _ => -1

fewest_coins_conserves_value (test)

test fewest_coins_conserves_value: passed
///- --test --symbol
///> c

:= ~amount = 1234567i32
fewest_coins amount |> total_gold |> _assert_eq amount
__assert_eq { expected = 1234567 }

fewest_coins_breakdown (test)

test fewest_coins_breakdown: passed
///- --test --symbol
///> c

:= ~amount = 123456i32
:= held = fewest_coins amount
held::crystal |> _assert_eq 12i32
held::platinum |> _assert_eq 34i32
held::gold |> _assert_eq 56i32
__assert_eq { expected = 12 }
__assert_eq { expected = 34 }
__assert_eq { expected = 56 }

paying_conserves_value (test)

test paying_conserves_value: passed
///- --test --symbol
///> c

:= ~price = 2750i32
purse 50 30 1 |> pay price |> remaining_gold |> _assert_eq 10300i32
__assert_eq { expected = 10300 }

paying_too_much_reports_shortfall (test)

test paying_too_much_reports_shortfall: passed
///- --test --symbol
///> c

:= ~price = 20000i32
?= purse 0 0 1 |> pay price ?|
| ShortBy missing => missing |> _assert_eq 10000i32
| Paid _ => _assert_eq 0i32 1i32
__assert_eq { expected = 10000 }

fewest_coins_breakdown_on_gpu (test)

test fewest_coins_breakdown_on_gpu: passed
///- --test --symbol
///> wgsl

:= ~amount = 123456i32
:= held = fewest_coins amount
? held::crystal = 12i32 && held::platinum = 34i32 && held::gold = 56i32 && total_gold held = amount
?> 0i32
|: 1i32