Problem: edmond_karp

Your task is to implement the Edmonds-Karp Algorithm to find the maximum flow in a directed graph with edge capacities and verify its functional correctness in Dafny. Preconditions: You can assume the graph contains at most 1,000 nodes. You can assume edge capacities are between 0 and 100,000. The Source s and Sink t are valid, distinct nodes. Crucially, the graph is a Simple Graph (no parallel edges between the same two nodes), ensuring that capacities between any pair of nodes are unique. Requirements: Implement the function max_flow_value which returns the integer value of the max flow. Algorithm: You must implement the Edmonds-Karp strategy, which repeatedly finds the shortest augmenting path (in terms of number of edges) from s to t in the residual graph using BFS, and pushes the maximum possible flow along that path. Output: The returned value must be equal to the net flow out of the source in a valid maximum flow assignment. Verification Challenges: (1) Residual Graph Correctness: Proving that your calculation of "remaining capacity" is correct and that pushing flow conserves mass (Flow In == Flow Out) for all internal nodes. (2) Termination: Edmonds-Karp terminates because the shortest path distance from s to t in the residual graph is non-decreasing and strictly increases periodically. Proving this requires reasoning about BFS Layers and global topology. (3) Optimality (Min-Cut): Proving that when no augmenting path exists, the current flow is maximum. This typically requires identifying a "Cut" (a set of vertices reachable from s) and showing that all edges crossing this cut are saturated.

// Following is the block for necessary definitions
// <preamble>
datatype CapacityGraph = CapacityGraph(
    // Adjacency list: adj[u] contains list of (neighbor, capacity)
    // u ranges from 0 to size - 1.
    // Capacity is int (was i64 in Verus)
    adj: seq<seq<(int, int)>>
)

// Helper predicates/functions for the Graph datatype
ghost function size(g: CapacityGraph): int {
    |g.adj|
}

ghost function view(g: CapacityGraph): seq<seq<(int, int)>> {
    g.adj
}

ghost predicate well_formed(g: CapacityGraph) {
    // 1. Basic Bounds checks
    (forall u: int, i: int :: 
        0 <= u < |g.adj| && 0 <= i < |g.adj[u]| 
        ==> 
        0 <= g.adj[u][i].0 < |g.adj|)
    &&
    // 2. SIMPLE GRAPH CONSTRAINT: No multigraphs allowed.
    // For any node u, all outgoing edges must have distinct targets.
    // This is critical because FlowMap uses (u, v) as a key, which cannot distinguish parallel edges.
    (forall u: int, i: int, j: int :: 
        0 <= u < |g.adj| 
        && 0 <= i < |g.adj[u]| 
        && 0 <= j < |g.adj[u]| 
        && i != j
        ==> 
        g.adj[u][i].0 != g.adj[u][j].0)
}

// --- MAX FLOW THEORY ---

// 1. Basic Definitions
ghost predicate has_capacity(g: seq<seq<(int, int)>>, u: int, v: int, c: int) {
    exists i: int :: 
        0 <= u < |g| && 0 <= i < |g[u]| 
        && g[u][i].0 == v 
        && g[u][i].1 == c
}

type FlowMap = map<(int, int), int>

ghost function get_flow(f: FlowMap, u: int, v: int): int {
    if (u, v) in f then f[(u, v)] else 0
}

// 2. Capacity Constraint
ghost predicate respects_capacity(g: seq<seq<(int, int)>>, f: FlowMap) {
    forall u: int, v: int :: 
        (u, v) in f ==> (
            f[(u, v)] > 0 ==> (
                exists c: int :: has_capacity(g, u, v, c) && f[(u, v)] <= c
            )
        )
}

// 3. Flow Summation Helpers
ghost function sum_flow_out_recursive(g: seq<seq<(int, int)>>, f: FlowMap, u: int, idx: int): int
    decreases idx
{
    if idx <= 0 || u < 0 || u >= |g| then 
        0 
    else
        // Inlined 'neighbor' to fix syntax error
        if idx - 1 < |g[u]| then
            sum_flow_out_recursive(g, f, u, idx - 1) + get_flow(f, u, g[u][idx - 1].0)
        else 
            sum_flow_out_recursive(g, f, u, idx - 1)
}

ghost function sum_flow_in_recursive(g: seq<seq<(int, int)>>, f: FlowMap, u: int, v_idx: int): int
    decreases v_idx
{
    if v_idx <= 0 then
        0
    else
        sum_flow_in_recursive(g, f, u, v_idx - 1) + get_flow(f, v_idx - 1, u)
}

ghost function total_flow_out(g: seq<seq<(int, int)>>, f: FlowMap, u: int): int {
    if 0 <= u < |g| then 
        sum_flow_out_recursive(g, f, u, |g[u]|)
    else 0
}

ghost function total_flow_in(g: seq<seq<(int, int)>>, f: FlowMap, u: int): int {
    sum_flow_in_recursive(g, f, u, |g|)
}

// 4. Flow Conservation
ghost predicate is_conserved(g: seq<seq<(int, int)>>, f: FlowMap, s: int, t: int) {
    forall u: int :: 
        0 <= u < |g| && u != s && u != t 
        ==> total_flow_in(g, f, u) == total_flow_out(g, f, u)
}

// 5. Validity
ghost predicate is_valid_flow(g: seq<seq<(int, int)>>, f: FlowMap, s: int, t: int) {
    && respects_capacity(g, f)
    && is_conserved(g, f, s, t)
    // Non-negative flow constraint
    && (forall u: int, v: int :: (u, v) in f ==> f[(u, v)] >= 0)
}

// 6. Value of the Flow
ghost function flow_val(g: seq<seq<(int, int)>>, f: FlowMap, s: int): int {
    total_flow_out(g, f, s) - total_flow_in(g, f, s)
}

// 7. Global Optimality
ghost predicate is_max_flow(g: seq<seq<(int, int)>>, f: FlowMap, s: int, t: int) {
    && is_valid_flow(g, f, s, t)
    && (forall other: FlowMap {:trigger is_valid_flow(g, other, s, t)} :: 
            is_valid_flow(g, other, s, t) ==> flow_val(g, f, s) >= flow_val(g, other, s))
}

// 8. Bounds Check
ghost predicate capacities_bounded(g: seq<seq<(int, int)>>) {
    |g| <= 1000 &&
    (forall u: int, v: int, c: int :: 
         has_capacity(g, u, v, c) ==> (c >= 0 && c <= 100_000))
}
// </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>
// PROBLEM: Max Flow
method max_flow_value(graph: CapacityGraph, s: int, t: int) returns (max_val: int)
    requires well_formed(graph)
    requires capacities_bounded(view(graph))
    requires 0 <= s < size(graph)
    requires 0 <= t < size(graph)
    requires s != t
    ensures exists f: FlowMap :: 
            is_max_flow(view(graph), f, s, t) 
            && flow_val(view(graph), f, s) == max_val
// </spec>
// <code>
{
    // Implement and verify the Edmonds-Karp algorithm here. 
}
// </code>