///- --package .
:# coins static =
    :< 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

///> c
:# fewest_coins_conserves_value =
    := ~amount = 1234567i32
    fewest_coins amount |> total_gold |> _assert_eq amount

///> c
:# fewest_coins_breakdown =
    := ~amount = 123456i32
    := held = fewest_coins amount
    held::crystal |> _assert_eq 12i32
    held::platinum |> _assert_eq 34i32
    held::gold |> _assert_eq 56i32

///> c
:# paying_conserves_value =
    := ~price = 2750i32
    purse 50 30 1 |> pay price |> remaining_gold |> _assert_eq 10300i32

///> c
:# paying_too_much_reports_shortfall =
    := ~price = 20000i32
    ?= purse 0 0 1 |> pay price ?|
    | ShortBy missing => missing |> _assert_eq 10000i32
    | Paid _ => _assert_eq 0i32 1i32

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