Problem: coin_change

Your task is to implement the Coin Change algorithm (finding minimum number of coins) and verify its correctness in Dafny. You are given an integer array `coins` representing coins of different denominations and an integer `amount` representing a total amount of money. Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1. You may assume that you have an infinite number of each kind of coin. In the incomplete code, the specification defines what constitutes a valid change combination (counts of each coin that sum to amount) and the metric to minimize (total count of coins). Your task is to implement the algorithm (typically DP) and prove: (1) Lower Bound: Any valid combination has a total coin count greater than or equal to the result (if result != -1). (2) Existence: If result != -1, there exists a valid combination with exactly that many coins. (3) Impossibility: If result == -1, no valid combination exists.


// Following is the block for necessary definitions
// <preamble>
// Calculates total monetary value: sum(counts[i] * coins[i])
function total_amount(counts: seq<int>, coins: seq<int>): int
    requires |counts| == |coins|
    decreases |counts|
{
    if |counts| == 0 then
        0
    else
        (counts[0] * coins[0]) + 
        total_amount(counts[1..], coins[1..])
}

// Calculates total number of coins used: sum(counts[i])
function total_coins(counts: seq<int>): int
    decreases |counts|
{
    if |counts| == 0 then
        0
    else
        counts[0] + total_coins(counts[1..])
}

// Definition of a valid change strategy:
// 1. One count per coin type
// 2. All counts non-negative
// 3. Total value equals target amount
predicate is_valid_change(counts: seq<int>, coins: seq<int>, amount: int) {
    |counts| == |coins| &&
    (forall i: int :: 0 <= i < |counts| ==> counts[i] >= 0) &&
    total_amount(counts, coins) == amount
}
// </preamble>

// Following is the block for potential helper specifications
// <helpers>

// </helpers>

// Following is the block for proofs of lemmas
// <proofs>

// </proofs>

// Following is the block for the main specification
// <spec>
// Returns -1 if impossible, otherwise the minimum number of coins.
method coin_change(coins: seq<int>, amount: int) returns (min_coins: int)
    requires |coins| > 0
    requires |coins| <= 100
    requires amount <= 10000
    // Coins must be positive
    requires forall i: int :: 0 <= i < |coins| ==> coins[i] > 0
    requires forall i: int :: 0 <= i < |coins| ==> coins[i] <= 10000
    ensures
        // Case 1: Solution exists
        min_coins != -1 ==> (
            // Existence
            (exists counts: seq<int> :: is_valid_change(counts, coins, amount) && total_coins(counts) == min_coins)
            &&
            // Lower Bound (Optimality)
            (forall counts: seq<int> :: is_valid_change(counts, coins, amount) ==> total_coins(counts) >= min_coins)
        )
    ensures
        // Case 2: Impossible
        min_coins == -1 ==> (
            forall counts: seq<int> :: is_valid_change(counts, coins, amount) ==> false
        )
// </spec>
// <code>
{
    // Implement and verify Coin Change DP here.
}
// </code>