Tasks / trusted base

LeanModels.Sv.Mono2

ls-sv semantics

Lines
971
file
LeanModels/Sv/Mono2.lean
sha256
f5aee8e39ee6a8e5a76586845ff9cf98fe05a5ac54a25b8abec3c60461d30f4c
statements that rely on it
_example, add, aes_sbox, arbiter, clmul, fdivp, matmul, mul, sorter
download
LeanModels.Sv.Mono2.lean
LeanModels/Sv/Mono2.leanLeanModels.Sv.Mono2.lean
import LeanModels.Sv.Spec2
import LeanModels.Sv.Pure2

/-!
# Fuel monotonicity and determinism of the Design2 interpreter (`LeanModels.Sv`)

The semantic tier's twin of `Obs.lean`'s fuel layer. `Sem2.lean`'s
interpreter threads ONE fuel parameter through expression evaluation,
statement execution, and the comb-settle pass counter; this file proves that
the parameter is a pure implementation detail of the executable semantics:

* **Adding fuel never changes a decided result.** Every interpreter function
  `F` is monotone in the flat approximation order `⊑` (`Res.le`, reused from
  `Obs.lean`): `F fuel ⊑ F fuel'` for `fuel ≤ fuel'`, i.e. the smaller run
  timed out or the two runs agree exactly (`.unsupported` messages
  included).
* **At fixed schedule σ a run is a function of the stimulus.** Two completed
  runs at any two fuels yield the same trace (`run2_det`), so the
  fuel-abstracted `Runs2` judgment (`Spec2.lean`) is functional
  (`Runs2.functional`), and `Deterministic2 d` — every legal schedule yields
  the same trace — is a meaningful property of a design.

## Design laws

* **One induction, one conjunction.** `fuelMono2` is a single induction on
  fuel over the whole of `Sem2`'s mutual block, in its declaration order:
  `evalExpr2`, `evalExprs2`, `resolveLhs2`, `resolveConcatLhs2`,
  `execStmt2`, `execCase2`, `execStmts2` (`writeConcatLhs2` is fuel-free and
  needs no conjunct). The glue is exactly `Obs.lean`'s: `Res.le_bind` /
  `Res.le_ite` congruences, `Res.le_refl` for fuel-free continuations, plus
  `Res.le_foldlM` for the `List.foldlM` walk of a concatenated target.
* **Same-fuel delegation is not induction.** `resolveLhs2` and
  `resolveConcatLhs2` do not match on fuel: they call `evalExpr2` at the SAME
  fuel (and the latter recurses structurally on the target). Their conjuncts
  are therefore derived at every level from the `evalExpr2` conjunct at that
  level (`resolveLhs2_le_of`, `resolveConcatLhs2_le_of` — the latter by
  well-founded recursion on the target's size), including fuel `0`, where
  `resolveLhs2 0 _ (.slice …)` is NOT a timeout.
* **The scheduler lifts compositionally.** `runCombProcess2`, `combPass2`,
  `runResetProcess2`, `resetPass2`, `runEdgeProcess2`, `edgePass2`,
  `cycleStep2`, `runFrom2`, `run2` consume fuel only through the mutual
  block, so their `_le` lemmas are congruence chains; `combSettle2` (the
  settle fixpoint loop, the scheduler-level fuel consumer) is its own small
  fuel induction, as in M0.
* **Fuel-free endpoint.** `evalExpr2_le_evalPure2`: every fueled evaluation
  approximates the fuel-free evaluator of `Pure2.lean` — the fuel-indexed
  chain converges to `evalPure2`, `.unsupported` outcomes included (this
  strengthens `evalPure2_of_evalExpr2_ok`, which covers `.ok` only).
* **Consumption forms.** Per-function `_mono` corollaries in implication form
  (`F fuel = r → r ≠ .timeout → ∀ fuel' ≥ fuel, F fuel' = r`), `_at_least`
  threshold forms (`∃ f₀, ∀ F, f₀ ≤ F → … = .ok p`, the shape spliced by
  `simp (disch := omega) only [h]`), and cross-fuel determinism `_det`
  lemmas for `combSettle2`, `cycleStep2`, and `run2`.

## What later layers consume

* `Runs2.functional` / `Deterministic2` — trace uniqueness at fixed σ and
  across schedules; `Terminates2.existsUnique` packages `Terminates2` with
  functionality (exactly one trace per schedule and stimulus).
* `cycleStep2_det` / `combSettle2_det` — two snapshot decompositions of the
  same cycle (e.g. two `Runs2.snapshot` witnesses with different fuels and
  the same counters) agree.
* `Runs2.at_least` — a completed run completes at every larger fuel, the
  fuel-uniformisation step when several runs must be read at one fuel.

The file ends with the two poles of `Deterministic2`:
`Deterministic2.of_processes_empty` (a process-free design's trace is
`holdTrace2`, whatever the schedule) and a two-process blocking race (the
Design2 twin of M0's `race_blk`) whose two schedules provably disagree, with
`#guard` rows pinning cross-fuel agreement and the timeout below it.
-/

namespace LeanModels.Sv

/-! ## Glue: `⊑` under `List.foldlM` -/

/-- Congruence of `⊑` under `List.foldlM`: if every step is `⊑`-related
pointwise (on list members), so is the whole fold. Used for the `foldlM`
walk of a concatenated assignment target (`resolveConcatLhs2`). -/
theorem Res.le_foldlM {α β : Type} {f g : β → α → Res β} :
    ∀ (l : List α) (init : β), (∀ b a, a ∈ l → f b a ⊑ g b a) →
      l.foldlM f init ⊑ l.foldlM g init
  | [], _, _ => Res.le_refl _
  | a :: as, init, h => by
      simp only [List.foldlM]
      exact Res.le_bind (h init a (List.mem_cons_self ..)) fun b =>
        Res.le_foldlM as b fun b' a' ha' => h b' a' (List.mem_cons_of_mem _ ha')

/-! ## Same-fuel delegation: target resolution -/

/-- `resolveLhs2` at fuel `fuel` is `⊑` its run at `fuel'` whenever
`evalExpr2` is (it evaluates only a dynamic index, at the SAME fuel). -/
theorem resolveLhs2_le_of {fuel fuel' : Nat} {st : SvState}
    (hE : ∀ e, evalExpr2 fuel st e ⊑ evalExpr2 fuel' st e) (t : Lhs2) :
    resolveLhs2 fuel st t ⊑ resolveLhs2 fuel' st t := by
  cases t with
  | ident n => exact Res.le_refl _
  | slice n lo w => exact Res.le_refl _
  | index n idx elemW =>
    simp only [resolveLhs2]
    exact Res.le_bind (hE idx) fun _ => Res.le_refl _
  | concat parts => exact Res.le_refl _

/-- `resolveConcatLhs2` at fuel `fuel` is `⊑` its run at `fuel'` whenever
`resolveLhs2` is (it resolves each leaf through `resolveLhs2` at the SAME
fuel, recursing structurally through nested concatenations). -/
theorem resolveConcatLhs2_le_of {fuel fuel' : Nat} {st : SvState}
    (hR : ∀ t, resolveLhs2 fuel st t ⊑ resolveLhs2 fuel' st t) :
    ∀ t : Lhs2, resolveConcatLhs2 fuel st t ⊑ resolveConcatLhs2 fuel' st t
  | .concat parts => by
      rw [resolveConcatLhs2.eq_1, resolveConcatLhs2.eq_1]
      exact Res.le_foldlM parts [] fun acc part hmem =>
        Res.le_bind (resolveConcatLhs2_le_of hR part) fun _ => Res.le_refl _
  | .ident n => by
      simp only [resolveConcatLhs2]
      exact Res.le_bind (hR _) fun _ => Res.le_refl _
  | .slice n lo w => by
      simp only [resolveConcatLhs2]
      exact Res.le_bind (hR _) fun _ => Res.le_refl _
  | .index n idx elemW => by
      simp only [resolveConcatLhs2]
      exact Res.le_bind (hR _) fun _ => Res.le_refl _
termination_by t => sizeOf t
decreasing_by
  have := List.sizeOf_lt_of_mem hmem
  simp_wf
  omega

/-! ## Fuel monotonicity of the mutual block -/

/-- **Fuel monotonicity** of `Sem2`'s mutual block, one conjunction proved by
a single induction on fuel: for every interpreter function `F` and
`fuel ≤ fuel'`, `F fuel ⊑ F fuel'` — a run that decided keeps its exact
result at any higher fuel. Conjunct order is the mutual block's
declaration order: `evalExpr2`, `evalExprs2`, `resolveLhs2`,
`resolveConcatLhs2`, `execStmt2`, `execCase2`, `execStmts2`. Consume it
through the per-function `_le`/`_mono` corollaries below. -/
theorem fuelMono2 (fuel : Nat) :
    (∀ (st : SvState) (e : Expr2) (fuel' : Nat), fuel ≤ fuel' →
      evalExpr2 fuel st e ⊑ evalExpr2 fuel' st e) ∧
    (∀ (st : SvState) (es : List Expr2) (fuel' : Nat), fuel ≤ fuel' →
      evalExprs2 fuel st es ⊑ evalExprs2 fuel' st es) ∧
    (∀ (st : SvState) (t : Lhs2) (fuel' : Nat), fuel ≤ fuel' →
      resolveLhs2 fuel st t ⊑ resolveLhs2 fuel' st t) ∧
    (∀ (st : SvState) (t : Lhs2) (fuel' : Nat), fuel ≤ fuel' →
      resolveConcatLhs2 fuel st t ⊑ resolveConcatLhs2 fuel' st t) ∧
    (∀ (st : SvState) (nba : NbaQueue2) (s : Stmt2) (fuel' : Nat), fuel ≤ fuel' →
      execStmt2 fuel st nba s ⊑ execStmt2 fuel' st nba s) ∧
    (∀ (st : SvState) (nba : NbaQueue2) (subj : LVec) (items : List CaseItem2)
        (default? : Option Stmt2) (mode : CaseMode) (fuel' : Nat), fuel ≤ fuel' →
      execCase2 fuel st nba subj items default? mode ⊑
        execCase2 fuel' st nba subj items default? mode) ∧
    (∀ (st : SvState) (nba : NbaQueue2) (ss : List Stmt2) (fuel' : Nat), fuel ≤ fuel' →
      execStmts2 fuel st nba ss ⊑ execStmts2 fuel' st nba ss) := by
  induction fuel with
  | zero =>
    -- Fuel 0 is `.timeout` for every fuel-matching function (the bottom of
    -- `⊑`); the two same-fuel resolvers are derived from `evalExpr2`.
    have hE : ∀ (st : SvState) (e : Expr2) (fuel' : Nat), 0 ≤ fuel' →
        evalExpr2 0 st e ⊑ evalExpr2 fuel' st e :=
      fun st e fuel' _ => Or.inl (by simp [evalExpr2])
    have hR : ∀ (st : SvState) (t : Lhs2) (fuel' : Nat), 0 ≤ fuel' →
        resolveLhs2 0 st t ⊑ resolveLhs2 fuel' st t :=
      fun st t fuel' h => resolveLhs2_le_of (fun e => hE st e fuel' h) t
    refine ⟨hE, ?_, hR, ?_, ?_, ?_, ?_⟩
    · exact fun st es fuel' _ => Or.inl (by simp [evalExprs2])
    · exact fun st t fuel' h => resolveConcatLhs2_le_of (fun t => hR st t fuel' h) t
    · exact fun st nba s fuel' _ => Or.inl (by simp [execStmt2])
    · intro st nba subj items dflt mode fuel' _
      cases items with
      | nil => exact Or.inl (by simp [execCase2])
      | cons it rest => cases it; exact Or.inl (by simp [execCase2])
    · exact fun st nba ss fuel' _ => Or.inl (by simp [execStmts2])
  | succ fuel ih =>
    obtain ⟨ihE, ihEs, ihR, ihCR, ihS, ihC, ihSs⟩ := ih
    -- evalExpr2: every child is evaluated at the decremented fuel, every
    -- continuation after the binds is fuel-free.
    have hE : ∀ (st : SvState) (e : Expr2) (fuel' : Nat), fuel + 1 ≤ fuel' →
        evalExpr2 (fuel + 1) st e ⊑ evalExpr2 fuel' st e := by
      intro st e fuel' hf
      cases fuel' with
      | zero => exact absurd hf (Nat.not_succ_le_zero fuel)
      | succ k =>
        have hk : fuel ≤ k := Nat.le_of_succ_le_succ hf
        cases e <;> simp only [evalExpr2] <;>
          repeat (first
            | exact Res.le_refl _
            | refine Res.le_bind (ihE st _ k hk) fun _ => ?_
            | refine Res.le_bind (ihEs st _ k hk) fun _ => ?_)
    have hR : ∀ (st : SvState) (t : Lhs2) (fuel' : Nat), fuel + 1 ≤ fuel' →
        resolveLhs2 (fuel + 1) st t ⊑ resolveLhs2 fuel' st t :=
      fun st t fuel' h => resolveLhs2_le_of (fun e => hE st e fuel' h) t
    refine ⟨hE, ?_, hR, ?_, ?_, ?_, ?_⟩
    -- evalExprs2
    · intro st es fuel' hf
      cases fuel' with
      | zero => exact absurd hf (Nat.not_succ_le_zero fuel)
      | succ k =>
        have hk : fuel ≤ k := Nat.le_of_succ_le_succ hf
        cases es with
        | nil => simp only [evalExprs2]; exact Res.le_refl _
        | cons e rest =>
          simp only [evalExprs2]
          exact Res.le_bind (ihE st e k hk) fun v =>
            Res.le_bind (ihEs st rest k hk) fun vs => Res.le_refl _
    -- resolveConcatLhs2
    · exact fun st t fuel' h => resolveConcatLhs2_le_of (fun t => hR st t fuel' h) t
    -- execStmt2
    · intro st nba s fuel' hf
      cases fuel' with
      | zero => exact absurd hf (Nat.not_succ_le_zero fuel)
      | succ k =>
        have hk : fuel ≤ k := Nat.le_of_succ_le_succ hf
        cases s with
        | blockingAssign target value =>
          simp only [execStmt2]
          refine Res.le_bind (ihE st value k hk) fun v => ?_
          cases target with
          | concat parts =>
            exact Res.le_bind (ihCR st _ k hk) fun _ => Res.le_refl _
          | ident n => exact Res.le_bind (ihR st _ k hk) fun _ => Res.le_refl _
          | slice n lo w => exact Res.le_bind (ihR st _ k hk) fun _ => Res.le_refl _
          | index n idx elemW => exact Res.le_bind (ihR st _ k hk) fun _ => Res.le_refl _
        | nbaAssign target value =>
          simp only [execStmt2]
          exact Res.le_bind (ihE st value k hk) fun v =>
            Res.le_bind (ihR st target k hk) fun _ => Res.le_refl _
        | ifStmt cond thenB elseB =>
          simp only [execStmt2]
          refine Res.le_bind (ihE st cond k hk) fun c => ?_
          refine Res.le_ite (ihS st nba thenB k hk) ?_
          cases elseB with
          | some s => exact ihS st nba s k hk
          | none => exact Res.le_refl _
        | caseStmt subject items dflt mode check =>
          simp only [execStmt2]
          exact Res.le_bind (ihE st subject k hk) fun sv =>
            ihC st nba sv items.toList dflt mode k hk
        | block body =>
          simp only [execStmt2]
          exact ihSs st nba body.toList k hk
        | unsupported svKind text => simp only [execStmt2]; exact Res.le_refl _
    -- execCase2
    · intro st nba subj items dflt mode fuel' hf
      cases fuel' with
      | zero => exact absurd hf (Nat.not_succ_le_zero fuel)
      | succ k =>
        have hk : fuel ≤ k := Nat.le_of_succ_le_succ hf
        cases items with
        | nil =>
          cases dflt with
          | some s => simp only [execCase2]; exact ihS st nba s k hk
          | none => simp only [execCase2]; exact Res.le_refl _
        | cons it rest =>
          obtain ⟨pats, body⟩ := it
          simp only [execCase2]
          exact Res.le_bind (ihEs st pats.toList k hk) fun pvs =>
            Res.le_ite (ihS st nba body k hk) (ihC st nba subj rest dflt mode k hk)
    -- execStmts2
    · intro st nba ss fuel' hf
      cases fuel' with
      | zero => exact absurd hf (Nat.not_succ_le_zero fuel)
      | succ k =>
        have hk : fuel ≤ k := Nat.le_of_succ_le_succ hf
        cases ss with
        | nil => simp only [execStmts2]; exact Res.le_refl _
        | cons s rest =>
          simp only [execStmts2]
          refine Res.le_bind (ihS st nba s k hk) fun p => ?_
          obtain ⟨st', nba'⟩ := p
          exact ihSs st' nba' rest k hk

/-! ## Per-function `⊑` projections

`fuelMono2`'s conjuncts, one per function of the mutual block, in the
building-block form `F fuel ⊑ F fuel'` for `fuel ≤ fuel'`. The scheduler
chain below is assembled from them; the implication-form `_mono` and the
threshold-form `_at_least` corollaries further down are what proofs
consume. -/

/-- `⊑`-monotonicity of `evalExpr2` in the fuel (`fuelMono2`, conjunct 1). -/
theorem evalExpr2_le {fuel fuel' : Nat} (h : fuel ≤ fuel') (st : SvState) (e : Expr2) :
    evalExpr2 fuel st e ⊑ evalExpr2 fuel' st e :=
  (fuelMono2 fuel).1 st e fuel' h

/-- `⊑`-monotonicity of `evalExprs2` in the fuel (conjunct 2). -/
theorem evalExprs2_le {fuel fuel' : Nat} (h : fuel ≤ fuel') (st : SvState)
    (es : List Expr2) : evalExprs2 fuel st es ⊑ evalExprs2 fuel' st es :=
  (fuelMono2 fuel).2.1 st es fuel' h

/-! The qualified-case checker is a post-run observation.  It consumes the
same fuel-indexed expression evaluator as the run, so it must sit on the
same approximation chain before `run2` can expose it without weakening the
existing cross-fuel theorems. -/

theorem caseHitRows2_le {fuel fuel' : Nat} (h : fuel ≤ fuel')
    (st : SvState) (subj : LVec) (mode : CaseMode) :
    ∀ (items : List CaseItem2),
      caseHitRows2 fuel st subj mode items ⊑ caseHitRows2 fuel' st subj mode items := by
  intro items
  induction items with
  | nil => exact Res.le_refl _
  | cons item rest ih =>
      cases item with
      | mk pats body =>
          simp only [caseHitRows2]
          refine Res.le_bind (evalExprs2_le h st pats.toList) ?_
          intro pvs
          refine Res.le_bind (ih) ?_
          intro rows
          exact Res.le_refl _

theorem validateFuelMono2 (fuel : Nat) :
    (∀ (st : SvState) (stmt : Stmt2) (fuel' : Nat), fuel ≤ fuel' →
      validateCaseChecks2 fuel st stmt ⊑ validateCaseChecks2 fuel' st stmt) ∧
    (∀ (st : SvState) (ss : List Stmt2) (fuel' : Nat), fuel ≤ fuel' →
      validateCaseChecksList2 fuel st ss ⊑ validateCaseChecksList2 fuel' st ss) := by
  induction fuel with
  | zero =>
      constructor
      · intro st stmt fuel' h
        exact Or.inl (by simp [validateCaseChecks2])
      · intro st ss fuel' h
        exact Or.inl (by simp [validateCaseChecksList2])
  | succ fuel ih =>
      obtain ⟨ihS, ihSs⟩ := ih
      constructor
      · intro st stmt fuel' hf
        cases fuel' with
        | zero => exact absurd hf (Nat.not_succ_le_zero fuel)
        | succ fuel' =>
            have hf' : fuel ≤ fuel' := Nat.le_of_succ_le_succ hf
            cases stmt with
            | blockingAssign target value =>
                exact Res.le_refl _
            | nbaAssign target value =>
                exact Res.le_refl _
            | unsupported kind text =>
                exact Res.le_refl _
            | ifStmt cond thenB elseB =>
                simp only [validateCaseChecks2]
                refine Res.le_bind (evalExpr2_le hf' st cond) ?_
                intro cv
                refine Res.le_ite (ihS st thenB fuel' hf') ?_
                cases elseB with
                | none => exact Res.le_refl _
                | some s => exact ihS st s fuel' hf'
            | block body =>
                simp only [validateCaseChecks2]
                exact ihSs st body.toList fuel' hf'
            | caseStmt subj items dflt mode check =>
                simp only [validateCaseChecks2]
                refine Res.le_bind (evalExpr2_le hf' st subj) ?_
                intro sv
                refine Res.le_bind (caseHitRows2_le hf' st sv mode items.toList) ?_
                intro hits
                refine Res.le_bind (Res.le_refl _) ?_
                intro _
                cases hfind : hits.find? (·.1) with
                | none =>
                    cases dflt with
                    | none => exact Res.le_refl _
                    | some s => exact ihS st s fuel' hf'
                | some pair =>
                    exact ihS st pair.2 fuel' hf'
      · intro st ss fuel' hf
        cases fuel' with
        | zero => exact absurd hf (Nat.not_succ_le_zero fuel)
        | succ fuel' =>
            have hf' : fuel ≤ fuel' := Nat.le_of_succ_le_succ hf
            cases ss with
            | nil => exact Res.le_refl _
            | cons s rest =>
                simp only [validateCaseChecksList2]
                refine Res.le_bind (ihS st s fuel' hf') ?_
                intro _
                exact ihSs st rest fuel' hf'

theorem validateCaseChecks2_le {fuel fuel' : Nat} (h : fuel ≤ fuel')
    (st : SvState) (stmt : Stmt2) :
    validateCaseChecks2 fuel st stmt ⊑ validateCaseChecks2 fuel' st stmt :=
  (validateFuelMono2 fuel).1 st stmt fuel' h

theorem validateCaseChecksList2_le {fuel fuel' : Nat} (h : fuel ≤ fuel')
    (st : SvState) (ss : List Stmt2) :
    validateCaseChecksList2 fuel st ss ⊑ validateCaseChecksList2 fuel' st ss :=
  (validateFuelMono2 fuel).2 st ss fuel' h

theorem validateProcessChecks2_le {fuel fuel' : Nat} (h : fuel ≤ fuel')
    (st : SvState) : ∀ (ps : List Process2),
      validateProcessChecks2 fuel st ps ⊑ validateProcessChecks2 fuel' st ps := by
  intro ps
  induction ps with
  | nil => exact Res.le_refl _
  | cons p rest ih =>
      cases p with
      | alwaysComb body =>
          simp only [validateProcessChecks2]
          by_cases hq : body.hasQualifiedCase = true
          · simp only [if_pos hq]
            exact Res.le_bind (validateCaseChecks2_le h st body) (fun _ => ih)
          · simp only [if_neg hq]
            exact Res.le_bind (Res.le_refl _) (fun _ => ih)
      | alwaysFF clock body =>
          simp only [validateProcessChecks2]
          by_cases hq : body.hasQualifiedCase = true
          · simp only [if_pos hq]
            exact Res.le_bind (validateCaseChecks2_le h st body) (fun _ => ih)
          · simp only [if_neg hq]
            exact Res.le_bind (Res.le_refl _) (fun _ => ih)
      | alwaysPlain clock body =>
          simp only [validateProcessChecks2]
          by_cases hq : body.hasQualifiedCase = true
          · simp only [if_pos hq]
            exact Res.le_bind (validateCaseChecks2_le h st body) (fun _ => ih)
          · simp only [if_neg hq]
            exact Res.le_bind (Res.le_refl _) (fun _ => ih)
      | alwaysFFR clock rn body =>
          simp only [validateProcessChecks2]
          by_cases hq : body.hasQualifiedCase = true
          · simp only [if_pos hq]
            exact Res.le_bind (validateCaseChecks2_le h st body) (fun _ => ih)
          · simp only [if_neg hq]
            exact Res.le_bind (Res.le_refl _) (fun _ => ih)
      | assign target value =>
          simp only [validateProcessChecks2]
          exact Res.le_bind (Res.le_refl _) (fun _ => ih)
      | unsupported kind text =>
          simp only [validateProcessChecks2]
          exact Res.le_bind (Res.le_refl _) (fun _ => ih)

theorem validateTraceChecks2_le {fuel fuel' : Nat} (h : fuel ≤ fuel')
    (d : Design2) : ∀ (tr : List SvState),
      validateTraceChecks2 fuel d tr ⊑ validateTraceChecks2 fuel' d tr := by
  intro tr
  induction tr with
  | nil => exact Res.le_refl _
  | cons st rest ih =>
      simp only [validateTraceChecks2]
      exact Res.le_bind (validateProcessChecks2_le h st d.processes.toList) (fun _ => ih)

/-- `⊑`-monotonicity of `resolveLhs2` in the fuel (conjunct 3). -/
theorem resolveLhs2_le {fuel fuel' : Nat} (h : fuel ≤ fuel') (st : SvState) (t : Lhs2) :
    resolveLhs2 fuel st t ⊑ resolveLhs2 fuel' st t :=
  (fuelMono2 fuel).2.2.1 st t fuel' h

/-- `⊑`-monotonicity of `resolveConcatLhs2` in the fuel (conjunct 4). -/
theorem resolveConcatLhs2_le {fuel fuel' : Nat} (h : fuel ≤ fuel') (st : SvState)
    (t : Lhs2) : resolveConcatLhs2 fuel st t ⊑ resolveConcatLhs2 fuel' st t :=
  (fuelMono2 fuel).2.2.2.1 st t fuel' h

/-- `⊑`-monotonicity of `execStmt2` in the fuel (conjunct 5). -/
theorem execStmt2_le {fuel fuel' : Nat} (h : fuel ≤ fuel') (st : SvState)
    (nba : NbaQueue2) (s : Stmt2) : execStmt2 fuel st nba s ⊑ execStmt2 fuel' st nba s :=
  (fuelMono2 fuel).2.2.2.2.1 st nba s fuel' h

/-- `⊑`-monotonicity of `execCase2` in the fuel (conjunct 6). -/
theorem execCase2_le {fuel fuel' : Nat} (h : fuel ≤ fuel') (st : SvState)
    (nba : NbaQueue2) (subj : LVec) (items : List CaseItem2) (default? : Option Stmt2)
    (mode : CaseMode) :
    execCase2 fuel st nba subj items default? mode ⊑
      execCase2 fuel' st nba subj items default? mode :=
  (fuelMono2 fuel).2.2.2.2.2.1 st nba subj items default? mode fuel' h

/-- `⊑`-monotonicity of `execStmts2` in the fuel (conjunct 7). -/
theorem execStmts2_le {fuel fuel' : Nat} (h : fuel ≤ fuel') (st : SvState)
    (nba : NbaQueue2) (ss : List Stmt2) :
    execStmts2 fuel st nba ss ⊑ execStmts2 fuel' st nba ss :=
  (fuelMono2 fuel).2.2.2.2.2.2 st nba ss fuel' h

/-! ## The fuel-free endpoint (`Pure2.lean`) -/

/-- Every fueled evaluation approximates the fuel-free evaluator: it timed
out, or it IS `evalPure2 st e` — `.ok` and `.unsupported` outcomes alike.
(The `.ok` half is `evalPure2_of_evalExpr2_ok`; this adds the loud half.) -/
theorem evalExpr2_le_evalPure2 (fuel : Nat) (st : SvState) (e : Expr2) :
    evalExpr2 fuel st e ⊑ evalPure2 st e := by
  have h := evalExpr2_le (Nat.le_add_right fuel (e.size + 1)) st e
  rwa [evalExpr2_eq_evalPure2 (fuel := fuel + (e.size + 1)) (by omega)] at h

/-- List twin of `evalExpr2_le_evalPure2` (the `concat` part walk). -/
theorem evalExprs2_le_evalPures2 (fuel : Nat) (st : SvState) (es : List Expr2) :
    evalExprs2 fuel st es ⊑ evalPures2 st es := by
  have h := evalExprs2_le (Nat.le_add_right fuel (Expr2.listSize es + 1)) st es
  rwa [evalExprs2_eq_evalPures2 (fuel := fuel + (Expr2.listSize es + 1)) (by omega)] at h

/-! ## The `⊑`-chain through the scheduler

The scheduler consumes fuel only through the mutual block and
`combSettle2`'s pass counter, so `⊑`-monotonicity lifts compositionally;
`combSettle2_le` is its own small fuel induction. -/

/-- `⊑`-monotonicity of one comb process run (assign or `always_comb`). -/
theorem runCombProcess2_le {fuel fuel' : Nat} (h : fuel ≤ fuel') (st : SvState)
    (p : Process2) : runCombProcess2 fuel st p ⊑ runCombProcess2 fuel' st p := by
  cases p with
  | assign target value =>
    simp only [runCombProcess2]
    refine Res.le_bind (evalExpr2_le h st value) fun v => ?_
    cases target with
    | concat parts =>
      exact Res.le_bind (resolveConcatLhs2_le h st _) fun _ => Res.le_refl _
    | ident n => exact Res.le_bind (resolveLhs2_le h st _) fun _ => Res.le_refl _
    | slice n lo w => exact Res.le_bind (resolveLhs2_le h st _) fun _ => Res.le_refl _
    | index n idx elemW => exact Res.le_bind (resolveLhs2_le h st _) fun _ => Res.le_refl _
  | alwaysComb body =>
    simp only [runCombProcess2]
    exact Res.le_bind (execStmt2_le h st [] body) fun _ => Res.le_refl _
  | alwaysFF clock body => exact Res.le_refl _
  | alwaysPlain clock body => exact Res.le_refl _
  | alwaysFFR clock rstn body => exact Res.le_refl _
  | unsupported svKind text => exact Res.le_refl _

/-- `⊑`-monotonicity of one comb pass, by induction on the index list. -/
theorem combPass2_le (d : Design2) {fuel fuel' : Nat} (h : fuel ≤ fuel') :
    ∀ (is : List Nat) (st : SvState), combPass2 d fuel st is ⊑ combPass2 d fuel' st is := by
  intro is
  induction is with
  | nil => intro st; exact Res.le_refl _
  | cons i rest ih =>
    intro st
    simp only [combPass2]
    cases d.processes[i]? with
    | some p => exact Res.le_bind (runCombProcess2_le h st p) fun st' => ih st'
    | none => exact Res.le_bind (Res.le_refl _) fun st' => ih st'

/-- `combSettle2` is the scheduler-level fuel consumer (fuel bounds the pass
count), so its monotonicity is its own small induction on fuel. -/
theorem combSettle2_le (d : Design2) (σ : ScheduleOracle) :
    ∀ {fuel fuel' : Nat}, fuel ≤ fuel' → ∀ (st : SvState) (k : Nat),
      combSettle2 d σ fuel st k ⊑ combSettle2 d σ fuel' st k := by
  intro fuel
  induction fuel with
  | zero => intro fuel' h st k; exact Or.inl (by simp [combSettle2])
  | succ f ih =>
    intro fuel' h st k
    cases fuel' with
    | zero => exact absurd h (Nat.not_succ_le_zero f)
    | succ f' =>
      have hf : f ≤ f' := Nat.le_of_succ_le_succ h
      simp only [combSettle2]
      refine Res.le_bind (combPass2_le d hf _ st) fun st' => ?_
      exact Res.le_ite (Res.le_refl _) (ih hf st' (k + 1))

/-- `⊑`-monotonicity of one async-reset process run. -/
theorem runResetProcess2_le {fuel fuel' : Nat} (h : fuel ≤ fuel') (st : SvState)
    (nba : NbaQueue2) (p : Process2) :
    runResetProcess2 fuel st nba p ⊑ runResetProcess2 fuel' st nba p := by
  cases p with
  | alwaysFFR clock rstn body => exact execStmt2_le h st nba body
  | alwaysFF clock body => exact Res.le_refl _
  | alwaysPlain clock body => exact Res.le_refl _
  | alwaysComb body => exact Res.le_refl _
  | assign target value => exact Res.le_refl _
  | unsupported svKind text => exact Res.le_refl _

/-- `⊑`-monotonicity of the reset-event pass, by induction on the index list. -/
theorem resetPass2_le (d : Design2) {fuel fuel' : Nat} (h : fuel ≤ fuel') :
    ∀ (is : List Nat) (st : SvState) (nba : NbaQueue2),
      resetPass2 d fuel st nba is ⊑ resetPass2 d fuel' st nba is := by
  intro is
  induction is with
  | nil => intro st nba; exact Res.le_refl _
  | cons i rest ih =>
    intro st nba
    simp only [resetPass2]
    cases d.processes[i]? with
    | some p => exact Res.le_bind (runResetProcess2_le h st nba p) fun q => ih q.1 q.2
    | none => exact Res.le_bind (Res.le_refl _) fun q => ih q.1 q.2

/-- `⊑`-monotonicity of one edge-triggered process run. -/
theorem runEdgeProcess2_le {fuel fuel' : Nat} (h : fuel ≤ fuel') (st : SvState)
    (nba : NbaQueue2) (p : Process2) :
    runEdgeProcess2 fuel st nba p ⊑ runEdgeProcess2 fuel' st nba p := by
  cases p with
  | alwaysFF clock body => exact execStmt2_le h st nba body
  | alwaysPlain clock body => exact execStmt2_le h st nba body
  | alwaysFFR clock rstn body => exact execStmt2_le h st nba body
  | alwaysComb body => exact Res.le_refl _
  | assign target value => exact Res.le_refl _
  | unsupported svKind text => exact Res.le_refl _

/-- `⊑`-monotonicity of the edge pass, by induction on the index list. -/
theorem edgePass2_le (d : Design2) {fuel fuel' : Nat} (h : fuel ≤ fuel') :
    ∀ (is : List Nat) (st : SvState) (nba : NbaQueue2),
      edgePass2 d fuel st nba is ⊑ edgePass2 d fuel' st nba is := by
  intro is
  induction is with
  | nil => intro st nba; exact Res.le_refl _
  | cons i rest ih =>
    intro st nba
    simp only [edgePass2]
    cases d.processes[i]? with
    | some p => exact Res.le_bind (runEdgeProcess2_le h st nba p) fun q => ih q.1 q.2
    | none => exact Res.le_bind (Res.le_refl _) fun q => ih q.1 q.2

/-- `cycleStep2`'s do-block compiles the reset-phase `match` as a join
point: the edge phase and final settle are duplicated into both arms, so the
proof splits on the reset ready list and runs one congruence chain per arm. -/
theorem cycleStep2_le (d : Design2) (σ : ScheduleOracle) {fuel fuel' : Nat}
    (h : fuel ≤ fuel') (inputs st : SvState) (k : Nat) :
    cycleStep2 d σ fuel inputs st k ⊑ cycleStep2 d σ fuel' inputs st k := by
  simp only [cycleStep2]
  refine Res.le_bind (combSettle2_le d σ h _ _) fun p => ?_
  obtain ⟨st1, k1⟩ := p
  dsimp only
  cases d.resetIndices st st1 with
  | nil =>
    refine Res.le_bind (Res.le_refl _) fun q => ?_
    refine Res.le_bind (edgePass2_le d h _ _ _) fun r => ?_
    exact combSettle2_le d σ h _ _
  | cons i is =>
    refine Res.le_bind (resetPass2_le d h _ _ _) fun r => ?_
    refine Res.le_bind (combSettle2_le d σ h _ _) fun q => ?_
    refine Res.le_bind (edgePass2_le d h _ _ _) fun r => ?_
    exact combSettle2_le d σ h _ _

/-- `⊑`-monotonicity of a run from a state, by induction on the stimulus. -/
theorem runFrom2_le (d : Design2) (σ : ScheduleOracle) {fuel fuel' : Nat}
    (h : fuel ≤ fuel') :
    ∀ (stim : List SvState) (st : SvState) (k : Nat),
      runFrom2 d σ fuel st k stim ⊑ runFrom2 d σ fuel' st k stim := by
  intro stim
  induction stim with
  | nil => intro st k; exact Res.le_refl _
  | cons inputs rest ih =>
    intro st k
    simp only [runFrom2]
    refine Res.le_bind (cycleStep2_le d σ h inputs st k) fun p => ?_
    obtain ⟨st', k'⟩ := p
    exact Res.le_bind (ih st' k') fun tr => Res.le_refl _

/-- `⊑`-monotonicity of `run2` (`runFrom2_le` from the startup state). -/
theorem run2_le (d : Design2) (σ : ScheduleOracle) {fuel fuel' : Nat}
    (h : fuel ≤ fuel') (stim : List SvState) :
    run2 d σ fuel stim ⊑ run2 d σ fuel' stim :=
by
  simp only [run2]
  refine Res.le_bind (runFrom2_le d σ h stim (initState2 d) 0) ?_
  intro tr
  by_cases hq : d.processes.toList.any Process2.hasQualifiedCase = true
  · simp only [if_pos hq]
    exact Res.le_bind (validateTraceChecks2_le h d tr) (fun _ => Res.le_refl _)
  · simp only [if_neg hq]
    exact Res.le_refl _

/-! ## Per-function corollaries (implication and threshold forms) -/

private theorem mono_of_le {α : Type} {x y r : Res α}
    (hle : x ⊑ y) (h : x = r) (hr : r ≠ .timeout) : y = r := by
  subst h; exact (Res.le_eq hle hr).symm

/-- Fuel monotonicity for `evalExpr2`: a decided result survives any fuel
increase, exactly. -/
theorem evalExpr2_mono {fuel : Nat} {st : SvState} {e : Expr2} {r : Res LVec}
    (h : evalExpr2 fuel st e = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, evalExpr2 fuel' st e = r := fun _ hf =>
  mono_of_le (evalExpr2_le hf st e) h hr

/-- Fuel monotonicity for `evalExprs2`. -/
theorem evalExprs2_mono {fuel : Nat} {st : SvState} {es : List Expr2}
    {r : Res (List LVec)} (h : evalExprs2 fuel st es = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, evalExprs2 fuel' st es = r := fun _ hf =>
  mono_of_le (evalExprs2_le hf st es) h hr

/-- Fuel monotonicity for `resolveLhs2`. -/
theorem resolveLhs2_mono {fuel : Nat} {st : SvState} {t : Lhs2}
    {r : Res (String × Option (Nat × Nat))} (h : resolveLhs2 fuel st t = r)
    (hr : r ≠ .timeout) : ∀ fuel' ≥ fuel, resolveLhs2 fuel' st t = r := fun _ hf =>
  mono_of_le (resolveLhs2_le hf st t) h hr

/-- Fuel monotonicity for `resolveConcatLhs2`. -/
theorem resolveConcatLhs2_mono {fuel : Nat} {st : SvState} {t : Lhs2}
    {r : Res (List (String × Nat × Nat))} (h : resolveConcatLhs2 fuel st t = r)
    (hr : r ≠ .timeout) : ∀ fuel' ≥ fuel, resolveConcatLhs2 fuel' st t = r :=
  fun _ hf => mono_of_le (resolveConcatLhs2_le hf st t) h hr

/-- Fuel monotonicity for `execStmt2`. -/
theorem execStmt2_mono {fuel : Nat} {st : SvState} {nba : NbaQueue2} {s : Stmt2}
    {r : Res (SvState × NbaQueue2)} (h : execStmt2 fuel st nba s = r)
    (hr : r ≠ .timeout) : ∀ fuel' ≥ fuel, execStmt2 fuel' st nba s = r :=
  fun _ hf => mono_of_le (execStmt2_le hf st nba s) h hr

/-- Fuel monotonicity for `execCase2`. -/
theorem execCase2_mono {fuel : Nat} {st : SvState} {nba : NbaQueue2} {subj : LVec}
    {items : List CaseItem2} {default? : Option Stmt2} {mode : CaseMode}
    {r : Res (SvState × NbaQueue2)}
    (h : execCase2 fuel st nba subj items default? mode = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, execCase2 fuel' st nba subj items default? mode = r :=
  fun _ hf => mono_of_le (execCase2_le hf st nba subj items default? mode) h hr

/-- Fuel monotonicity for `execStmts2`. -/
theorem execStmts2_mono {fuel : Nat} {st : SvState} {nba : NbaQueue2}
    {ss : List Stmt2} {r : Res (SvState × NbaQueue2)}
    (h : execStmts2 fuel st nba ss = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, execStmts2 fuel' st nba ss = r := fun _ hf =>
  mono_of_le (execStmts2_le hf st nba ss) h hr

/-- Fuel monotonicity for `runCombProcess2`. -/
theorem runCombProcess2_mono {fuel : Nat} {st : SvState} {p : Process2}
    {r : Res SvState} (h : runCombProcess2 fuel st p = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, runCombProcess2 fuel' st p = r := fun _ hf =>
  mono_of_le (runCombProcess2_le hf st p) h hr

/-- Fuel monotonicity for `combPass2` (one settle pass). -/
theorem combPass2_mono {d : Design2} {fuel : Nat} {st : SvState} {is : List Nat}
    {r : Res SvState} (h : combPass2 d fuel st is = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, combPass2 d fuel' st is = r := fun _ hf =>
  mono_of_le (combPass2_le d hf is st) h hr

/-- Fuel monotonicity for `combSettle2`: a settled state (and pass counter)
survives any fuel increase. -/
theorem combSettle2_mono {d : Design2} {σ : ScheduleOracle} {fuel : Nat}
    {st : SvState} {k : Nat} {r : Res (SvState × Nat)}
    (h : combSettle2 d σ fuel st k = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, combSettle2 d σ fuel' st k = r := fun _ hf =>
  mono_of_le (combSettle2_le d σ hf st k) h hr

/-- Fuel monotonicity for `resetPass2`. -/
theorem resetPass2_mono {d : Design2} {fuel : Nat} {st : SvState} {nba : NbaQueue2}
    {is : List Nat} {r : Res (SvState × NbaQueue2)}
    (h : resetPass2 d fuel st nba is = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, resetPass2 d fuel' st nba is = r := fun _ hf =>
  mono_of_le (resetPass2_le d hf is st nba) h hr

/-- Fuel monotonicity for `edgePass2`. -/
theorem edgePass2_mono {d : Design2} {fuel : Nat} {st : SvState} {nba : NbaQueue2}
    {is : List Nat} {r : Res (SvState × NbaQueue2)}
    (h : edgePass2 d fuel st nba is = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, edgePass2 d fuel' st nba is = r := fun _ hf =>
  mono_of_le (edgePass2_le d hf is st nba) h hr

/-- Fuel monotonicity for `cycleStep2`. -/
theorem cycleStep2_mono {d : Design2} {σ : ScheduleOracle} {fuel : Nat}
    {inputs st : SvState} {k : Nat} {r : Res (SvState × Nat)}
    (h : cycleStep2 d σ fuel inputs st k = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, cycleStep2 d σ fuel' inputs st k = r := fun _ hf =>
  mono_of_le (cycleStep2_le d σ hf inputs st k) h hr

/-- Fuel monotonicity for `runFrom2`. -/
theorem runFrom2_mono {d : Design2} {σ : ScheduleOracle} {fuel : Nat}
    {st : SvState} {k : Nat} {stim : List SvState} {r : Res (List SvState)}
    (h : runFrom2 d σ fuel st k stim = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, runFrom2 d σ fuel' st k stim = r := fun _ hf =>
  mono_of_le (runFrom2_le d σ hf stim st k) h hr

/-- **Fuel monotonicity for `run2`**: a decided trace (`ok` or
`unsupported`) is the same at every larger fuel. -/
theorem run2_mono {d : Design2} {σ : ScheduleOracle} {fuel : Nat}
    {stim : List SvState} {r : Res (List SvState)}
    (h : run2 d σ fuel stim = r) (hr : r ≠ .timeout) :
    ∀ fuel' ≥ fuel, run2 d σ fuel' stim = r := fun _ hf =>
  mono_of_le (run2_le d σ hf stim) h hr

/-- Threshold form of a completed `combSettle2` (comb settle is the fixpoint
recursion a symbolic execution keeps frozen). -/
theorem combSettle2_at_least {d : Design2} {σ : ScheduleOracle} {st : SvState}
    {k : Nat} {p : SvState × Nat}
    (h : ∃ fuel, combSettle2 d σ fuel st k = .ok p) :
    ∃ f₀, ∀ F, f₀ ≤ F → combSettle2 d σ F st k = .ok p := by
  obtain ⟨fuel, hf⟩ := h
  exact ⟨fuel, fun F hF => combSettle2_mono hf (by simp) F hF⟩

/-- Threshold form of a completed `cycleStep2`: succeed once, succeed at
every sufficiently large fuel — a conditional rewrite rule for
`simp (disch := omega) only [h]`. -/
theorem cycleStep2_at_least {d : Design2} {σ : ScheduleOracle} {inputs st : SvState}
    {k : Nat} {p : SvState × Nat}
    (h : ∃ fuel, cycleStep2 d σ fuel inputs st k = .ok p) :
    ∃ f₀, ∀ F, f₀ ≤ F → cycleStep2 d σ F inputs st k = .ok p := by
  obtain ⟨fuel, hf⟩ := h
  exact ⟨fuel, fun F hF => cycleStep2_mono hf (by simp) F hF⟩

/-- Threshold form of a completed `run2`. -/
theorem run2_at_least {d : Design2} {σ : ScheduleOracle} {stim tr : List SvState}
    (h : ∃ fuel, run2 d σ fuel stim = .ok tr) :
    ∃ f₀, ∀ F, f₀ ≤ F → run2 d σ F stim = .ok tr := by
  obtain ⟨fuel, hf⟩ := h
  exact ⟨fuel, fun F hF => run2_mono hf (by simp) F hF⟩

/-! ## Cross-fuel determinism at fixed σ -/

/-- Generic cross-fuel agreement: two decided results of a `⊑`-monotone
fuel-indexed computation agree, whatever the two fuels. -/
private theorem res_det_of_le {α : Type} {F : Nat → Res α}
    (hle : ∀ {f f' : Nat}, f ≤ f' → F f ⊑ F f') {fuel₁ fuel₂ : Nat} {r₁ r₂ : Res α}
    (h₁ : F fuel₁ = r₁) (h₂ : F fuel₂ = r₂)
    (hr₁ : r₁ ≠ .timeout) (hr₂ : r₂ ≠ .timeout) : r₁ = r₂ := by
  rcases Nat.le_total fuel₁ fuel₂ with hf | hf
  · exact (mono_of_le (hle hf) h₁ hr₁).symm.trans h₂
  · exact h₁.symm.trans (mono_of_le (hle hf) h₂ hr₂)

/-- Two successful settles of the same state at the same counter, at any two
fuels, agree (settled state AND final counter). -/
theorem combSettle2_det {d : Design2} {σ : ScheduleOracle} {st : SvState} {k : Nat}
    {fuel₁ fuel₂ : Nat} {p₁ p₂ : SvState × Nat}
    (h₁ : combSettle2 d σ fuel₁ st k = .ok p₁)
    (h₂ : combSettle2 d σ fuel₂ st k = .ok p₂) : p₁ = p₂ :=
  Res.ok.inj (res_det_of_le (F := fun f => combSettle2 d σ f st k)
    (fun hf => combSettle2_le d σ hf st k) h₁ h₂ (by simp) (by simp))

/-- Two successful `cycleStep2`s from the same state, inputs, and counter, at
any two fuels, agree (snapshot AND next counter). -/
theorem cycleStep2_det {d : Design2} {σ : ScheduleOracle} {inputs st : SvState}
    {k : Nat} {fuel₁ fuel₂ : Nat} {p₁ p₂ : SvState × Nat}
    (h₁ : cycleStep2 d σ fuel₁ inputs st k = .ok p₁)
    (h₂ : cycleStep2 d σ fuel₂ inputs st k = .ok p₂) : p₁ = p₂ :=
  Res.ok.inj (res_det_of_le (F := fun f => cycleStep2 d σ f inputs st k)
    (fun hf => cycleStep2_le d σ hf inputs st k) h₁ h₂ (by simp) (by simp))

/-- Any two decided (non-`timeout`) results of the same run, at *any* two
fuels, are equal — `.unsupported` messages included. -/
theorem run2_res_det {d : Design2} {σ : ScheduleOracle} {stim : List SvState}
    {fuel₁ fuel₂ : Nat} {r₁ r₂ : Res (List SvState)}
    (h₁ : run2 d σ fuel₁ stim = r₁) (h₂ : run2 d σ fuel₂ stim = r₂)
    (hr₁ : r₁ ≠ .timeout) (hr₂ : r₂ ≠ .timeout) : r₁ = r₂ :=
  res_det_of_le (F := fun f => run2 d σ f stim) (fun hf => run2_le d σ hf stim)
    h₁ h₂ hr₁ hr₂

/-- **Cross-fuel determinism**: two successful traces of the same design,
stimulus, and schedule are equal, whatever the fuels. -/
theorem run2_det {d : Design2} {σ : ScheduleOracle} {stim : List SvState}
    {fuel₁ fuel₂ : Nat} {tr₁ tr₂ : List SvState}
    (h₁ : run2 d σ fuel₁ stim = .ok tr₁) (h₂ : run2 d σ fuel₂ stim = .ok tr₂) :
    tr₁ = tr₂ :=
  Res.ok.inj (run2_res_det h₁ h₂ (by simp) (by simp))

/-- A completed run cannot also be loud: if some fuel yields a trace, no fuel
yields `.unsupported`. -/
theorem run2_ok_unsupported {d : Design2} {σ : ScheduleOracle} {stim : List SvState}
    {fuel₁ fuel₂ : Nat} {tr : List SvState} {msg : String}
    (h₁ : run2 d σ fuel₁ stim = .ok tr) (h₂ : run2 d σ fuel₂ stim = .unsupported msg) :
    False := by
  have := run2_res_det h₁ h₂ (by simp) (by simp)
  simp at this

/-! ## The run judgment is functional -/

/-- `Runs2` is functional: at fixed σ the trace is unique across all fuels. -/
theorem Runs2.functional {d : Design2} {σ : ScheduleOracle} {stim tr₁ tr₂ : List SvState}
    (h₁ : Runs2 d σ stim tr₁) (h₂ : Runs2 d σ stim tr₂) : tr₁ = tr₂ := by
  obtain ⟨fuel₁, hf₁⟩ := h₁
  obtain ⟨fuel₂, hf₂⟩ := h₂
  exact run2_det hf₁ hf₂

/-- Threshold form of `Runs2` (the `CallsTo.at_least` analog): a completed
run completes, with the same trace, at every sufficiently large fuel. -/
theorem Runs2.at_least {d : Design2} {σ : ScheduleOracle} {stim tr : List SvState}
    (h : Runs2 d σ stim tr) : ∃ f₀, ∀ F, f₀ ≤ F → run2 d σ F stim = .ok tr :=
  run2_at_least h

/-- With `Terminates2`, every schedule and stimulus has EXACTLY one trace:
termination gives existence, `Runs2.functional` uniqueness. -/
theorem Terminates2.existsUnique {d : Design2} (ht : Terminates2 d)
    (σ : ScheduleOracle) (stim : List SvState) :
    ∃ tr, Runs2 d σ stim tr ∧ ∀ tr', Runs2 d σ stim tr' → tr' = tr := by
  obtain ⟨tr, hr⟩ := ht σ stim
  exact ⟨tr, hr, fun tr' hr' => Runs2.functional hr' hr⟩

/-- All legal schedules yield the same trace — `Obs.lean`'s `Deterministic`
at the semantic tier. (Cross-FUEL uniqueness at one schedule is
`Runs2.functional` and holds for every design; this is the cross-SCHEDULE
statement, a property of the design: race-free designs have it, a blocking
race refutes it.) -/
def Deterministic2 (d : Design2) : Prop :=
  ∀ (σ₁ σ₂ : ScheduleOracle) (stim tr₁ tr₂ : List SvState),
    Runs2 d σ₁ stim tr₁ → Runs2 d σ₂ stim tr₂ → tr₁ = tr₂

/-- A deterministic design's trace may be read off any one schedule: if `P`
holds of the trace under some schedule, it holds under every schedule. -/
theorem Deterministic2.transfer {d : Design2} (hd : Deterministic2 d)
    {σ₁ σ₂ : ScheduleOracle} {stim tr₁ tr₂ : List SvState}
    (h₁ : Runs2 d σ₁ stim tr₁) (h₂ : Runs2 d σ₂ stim tr₂)
    {P : List SvState → Prop} (hP : P tr₁) : P tr₂ :=
  hd σ₁ σ₂ stim tr₁ tr₂ h₁ h₂ ▸ hP

/-! ## `Deterministic2` holds of process-free designs

The smallest positive instance (it pairs with `Spec2.lean`'s process-free
`Terminates2` example): with no processes, comb settle is one no-op pass,
no reset or edge process ever fires, and each snapshot is the previous
state with the stimulus applied — whatever the schedule. -/

/-- The trace of a process-free design: each snapshot is the previous state
with the cycle's inputs applied. -/
def holdTrace2 (d : Design2) : SvState → List SvState → List SvState
  | _, [] => []
  | st, inputs :: rest =>
      applyInputs2 d inputs st :: holdTrace2 d (applyInputs2 d inputs st) rest

/-- With no processes, every successful cycle only applies the inputs. -/
theorem cycleStep2_of_processes_empty {d : Design2} (hd : d.processes = #[])
    {σ : ScheduleOracle} {fuel : Nat} {inputs st st' : SvState} {k k' : Nat}
    (h : cycleStep2 d σ fuel inputs st k = .ok (st', k')) :
    st' = applyInputs2 d inputs st := by
  have hc : d.combIndices = [] := by simp [Design2.combIndices, hd]
  have he : d.edgeIndices = [] := by simp [Design2.edgeIndices, hd]
  have hr : ∀ a b, d.resetIndices a b = [] := by
    intro a b; simp [Design2.resetIndices, hd]
  cases fuel with
  | zero => simp [cycleStep2, combSettle2] at h
  | succ f =>
    simp [cycleStep2, combSettle2, combPass2, hc, he, hr, σ.choose_nil, edgePass2,
      commitNba2] at h
    exact h.1.symm

/-- With no processes, every completed run is `holdTrace2`. -/
theorem runFrom2_of_processes_empty {d : Design2} (hd : d.processes = #[])
    {σ : ScheduleOracle} {fuel : Nat} :
    ∀ {stim : List SvState} {st : SvState} {k : Nat} {tr : List SvState},
      runFrom2 d σ fuel st k stim = .ok tr → tr = holdTrace2 d st stim
  | [], st, k, tr, h => by simpa [holdTrace2, eq_comm] using h
  | inputs :: rest, st, k, tr, h => by
      obtain ⟨st', k', tr', h1, h2, rfl⟩ := runFrom2_cons_eq_ok.mp h
      have := cycleStep2_of_processes_empty hd h1
      subst this
      simp [holdTrace2, runFrom2_of_processes_empty hd h2]

/-- A design with no processes is schedule-deterministic. -/
theorem Deterministic2.of_processes_empty {d : Design2} (hd : d.processes = #[]) :
    Deterministic2 d := by
  rintro σ₁ σ₂ stim tr₁ tr₂ ⟨f₁, h₁⟩ ⟨f₂, h₂⟩
  rw [runFrom2_of_processes_empty hd (run2_from_ok h₁),
    runFrom2_of_processes_empty hd (run2_from_ok h₂)]

/-! ## Examples: a blocking race refutes `Deterministic2`

Two `always_ff` processes blocking-assign `0` and `1` to the same `x` on
the same edge — the Design2 twin of M0's `race_blk`. Source order leaves
`1`, the reversed schedule leaves `0`; fuel never matters (the `#guard`
rows check one decided run at two fuels and the timeout below them). -/

/-- `always_ff @(posedge clk) x = 0;  always_ff @(posedge clk) x = 1;` -/
private def raceD : Design2 :=
  { name := "race"
    decls := #[{ name := "clk", width := 1, isInput := true }, { name := "x", width := 1 }]
    processes := #[
      .alwaysFF "clk" (.blockingAssign (.ident "x") (.lit (LVec.lit "0"))),
      .alwaysFF "clk" (.blockingAssign (.ident "x") (.lit (LVec.lit "1")))] }

-- Cross-fuel agreement of decided runs (`run2_det` in action) …
#guard run2 raceD σ_src 8 [[], []] == run2 raceD σ_src 64 [[], []]
#guard run2 raceD σ_rev 8 [[], []] == run2 raceD σ_rev 64 [[], []]
-- … above the timeout at fuel 1 (the bottom of `⊑`). The settle is NOT the
-- cause: `raceD` has no comb process, so `combSettle2` at fuel 1 runs one
-- empty pass and returns `.ok`. The edge phase times out: `execStmt2 1`
-- evaluates the right-hand side of `x = 0` with `evalExpr2 0`.
#guard run2 raceD σ_src 1 [[]] == .timeout
-- The two schedules disagree on `x`.
#guard (Res.toOption (run2 raceD σ_src 8 [[]])).map
        (fun tr => tr.map fun st => SvState.showSignal st "x") == some ["1"]
#guard (Res.toOption (run2 raceD σ_rev 8 [[]])).map
        (fun tr => tr.map fun st => SvState.showSignal st "x") == some ["0"]

-- `Runs2.functional` pins the trace from one kernel-checked run.
example (tr : List SvState) (h : Runs2 raceD σ_src [[]] tr) :
    tr = [[("clk", LVec.xVec 1), ("x", LVec.lit "1")]] :=
  Runs2.functional h ⟨8, by decide +kernel⟩

/-- The race is schedule-dependent: `raceD` is NOT `Deterministic2`. -/
private theorem raceD_not_deterministic : ¬ Deterministic2 raceD := fun h =>
  absurd (h σ_src σ_rev [[]] [[("clk", LVec.xVec 1), ("x", LVec.lit "1")]]
      [[("clk", LVec.xVec 1), ("x", LVec.lit "0")]]
      ⟨8, by decide +kernel⟩ ⟨8, by decide +kernel⟩)
    (by decide)

-- The positive instance, on a concrete process-free design.
example : Deterministic2 { name := "hold", decls := #[], processes := #[] } :=
  Deterministic2.of_processes_empty rfl

end LeanModels.Sv