Tasks / trusted base

LeanModels.Sv.Comb2Term

ls-sv semantics

Lines
672
file
LeanModels/Sv/Comb2Term.lean
sha256
774c73de2b5cafae942794f3638c346352eae2614c23a429111022b99fef7886
statements that rely on it
_example, add, aes_sbox, arbiter, clmul, fdivp, matmul, mul, sorter
download
LeanModels.Sv.Comb2Term.lean
LeanModels/Sv/Comb2Term.leanLeanModels.Sv.Comb2Term.lean
import LeanModels.Sv.Comb2Base

/-!
# Combinational settle terminates under acyclicity (`LeanModels.Sv` — Design2)

The TERMINATION direction of the combinational settle theorem (the
`Comb2Base`/`Comb2Sound`/`Comb2Term`/`Comb2` layer): on a comb design
(`CombDesign`) with an acyclicity certificate (`Acyclic`), `combSettle2`
returns `.ok` from every shaped state, under every schedule, at every fuel
from the explicit bound
`Design2.settleFuel d = d.combFuel + d.processes.size + 1` on. What it
returns is `Comb2Sound.lean`'s question; that it returns at all is this
file's.

## API index

* Main theorems: `combSettle2_terminates` (the headline statement, `∃ F`) and
  `combSettle2_terminates_of_le` (the same at the explicit bound
  `Design2.settleFuel`); their shape-carrying corollaries
  `combSettle2_terminates_shape` / `combSettle2_terminates_shape_of_le` (the
  returned state also has the comb shape — proved here from the settle
  sequence, without `Comb2Sound`).
* The stabilization theorem behind it, schedule- and fuel-free:
  `CombPassSeq` (an abstract run of passes: each order lists every process
  exactly once, each pass a successful `combPass2` at any fuel) and
  `CombPassSeq.stable` — under a rank certificate bounded by `R`, the state
  after `R` passes is a fixpoint (`s p = s R` for `p ≥ R`). Its inductive core
  is `CombPassSeq.combAgreeLow`.
* Rank certificates: `AcyclicBy d rank` (the body of `Acyclic`, with the rank
  named), `Acyclic.exists_rank_lt` (every certificate compresses to one
  bounded by the number of processes — the source of the rank-free bound).
* Rank-level agreement: `CombLowBit` (a bit driven only below rank `r`,
  vacuously true of undriven bits), `CombAgreeLow` (two states share all
  widths and all such bits; `CombAgreeLow.mono` weakens the rank),
  `evalPure2_eq_of_combAgreeLow` (a rank-`r` process cannot tell two such
  states apart).
* The settle as a pass sequence: `combSettleSeq` (states a settle visits,
  pass by pass), `combSettleSeq_shape`, `combSettleSeq_pass`,
  `CombPassSeq.of_combSettleSeq`, and `combSettle2_ok_of_stable` (a settle
  whose pass sequence is stable from pass `p + j` on returns a shaped state,
  given fuel for `j + 1` more passes).
* Pass algebra (namespace `Comb2Term`, general facts that belong next to
  `Comb2Base`'s pass lemmas): `combPass2_append`, `combPass2_det` (a
  successful pass result does not depend on the fuel), `combPass2_mid` (the
  state at which a given process runs inside a pass: per bit equal to the
  pass's start or its end; the process's window in the end state is what it
  wrote there), window/bit overlap facts `overlaps_of_bit`,
  `overlaps_whole_of_bit`, `overlaps_win_of_bit`, `driver_unique`,
  `agree_bit_of_extract`, `lt_of_writes`, and the filter-length facts
  `length_filter_le`/`length_filter_lt` used by rank compression.

## The argument

Let `s p` be the state after `p` passes. Passes run in arbitrary orders
(`σ.choose`), so a process may run before or after the processes it reads;
the induction never looks at the orders beyond "each process runs exactly
once per pass".

* **Every mid-pass state is a bit-wise mix of the pass's endpoints.** When
  process `j` runs inside pass `p`, the state `m` it sees agrees on every bit
  with `s p` (the bit's driver has not run yet in this pass, or the bit is
  undriven) or with `s (p+1)` (its driver has run, and nothing writes the bit
  again — `CombDesign.disjoint` + `Nodup`). And every window of `j` in
  `s (p+1)` holds exactly its slice of what `j` computed at `m`
  (`combPass2_mid`; a concatenated target has several windows, each with its
  `Win.placed` offset, and `CombDesign.selfDisjoint` keeps them apart).
* **Rank induction (`CombPassSeq.combAgreeLow`).** For every `r`, the states
  `s p` with `p ≥ r` all agree on every bit driven only below rank `r`
  (`CombAgreeLow`). `r = 0`: such bits are undriven, and no pass touches them
  (`combPass2_frame`). `r → r + 1`: a bit driven by `j` (rank `≤ r`) holds, in
  `s p` (`p ≥ r + 1`), what `j` wrote during pass `p - 1 ≥ r`, evaluated at a
  mid-pass state; every bit `j` reads is driven below `rank j ≤ r` (the
  certificate), and at mid-pass states of passes `≥ r` those bits are
  those of `s q`, `q ≥ r` — all equal by the induction hypothesis. So `j`
  computes the same value in every pass `≥ r`
  (`evalPure2_eq_of_combAgreeLow`, via `evalPure2_congr_reads`).
* **Fixpoint.** With every rank `< R`, every bit is driven below `R`, so
  `s R = s (R+1)` (`StateShape.eq_of_agree_bits`), and the settle's
  `st' == st` test fires (`LawfulBEq`) at the latest on pass `R`.
* **Rank compression.** Replacing `rank i` by the number of processes of
  strictly lower rank keeps the certificate (strict monotonicity of a
  filter's length) and bounds every rank by the process count, so `R` can be
  taken to be `d.processes.size` whatever certificate was given.
* **Fuel.** `combSettle2` spends one fuel unit per pass and hands the rest
  to the pass as expression fuel; `combPass2` succeeds at any expression fuel
  `≥ d.combFuel` (`combPass2_ok_of_combFuel`) and its result does not depend
  on the fuel (`combPass2_det`). `R + 1` passes at expression fuel
  `≥ d.combFuel` need total fuel `d.combFuel + R + 1`.

## Design laws

* **Fuel never appears in the stabilization theorem.** `CombPassSeq` takes
  each pass at its own existential fuel; fuel enters only in the settle loop
  (`combSettle2_ok_of_stable`) and the main theorems.
* **No known values.** Like `Comb2Base`, everything is 4-state and
  value-agnostic: no `BitVec`, no `Known2`.
* **Prefixed vocabulary.** The helper definitions live in the shared
  `LeanModels.Sv` namespace, so they carry the `comb`/`Comb` prefix
  (`combSettleSeq`, `CombLowBit`, `CombAgreeLow`) instead of generic names.

## Deviations from the original statement

* (2026-10-27, concatenation targets) `AcyclicBy` (like `Acyclic`) quantifies
  over the windows `wj ∈ tj.wins d` of a target, and `combPass2_mid` concludes
  for every placed window of the process (`Comb2Base.lean`, deviations); the
  main theorems keep their statements.
* None in the main theorem's statement. The originally planned bound
  "`R + 2` settle iterations with `R = 1 + max rank`" is replaced by `R + 1`
  iterations with `R = d.processes.size` (after rank compression), giving
  the explicit, certificate-independent `Design2.settleFuel`.

The smoke section at the end instantiates the theorem at `Comb2Base`'s
hand-built full adder (`Comb2Smoke.faDesign`).
-/

namespace LeanModels.Sv

/-! ## Pass algebra -/

namespace Comb2Term

/-- A pass over a concatenated order is the two passes in sequence. -/
theorem combPass2_append (d : Design2) (fuel : Nat) :
    ∀ (st : SvState) (l₁ l₂ : List Nat),
      combPass2 d fuel st (l₁ ++ l₂) =
        (combPass2 d fuel st l₁ >>= fun m => combPass2 d fuel m l₂)
  | _, [], _ => rfl
  | st, i :: l₁, l₂ => by
      cases hpi : d.processes[i]? with
      | none =>
          rw [List.cons_append, combPass2_cons_none hpi, combPass2_cons_none hpi]
          exact combPass2_append d fuel st l₁ l₂
      | some p =>
          rw [List.cons_append, combPass2_cons_some hpi, combPass2_cons_some hpi]
          cases runCombProcess2 fuel st p with
          | ok m => simp only [Res.ok_bind]; exact combPass2_append d fuel m l₁ l₂
          | timeout => rfl
          | unsupported msg => rfl

/-- **A pass's result does not depend on the fuel**: two successful passes
from the same shaped state over the same order (at any two fuels) agree. -/
theorem combPass2_det {d : Design2} (hd : CombDesign d) {f f' : Nat} :
    ∀ {is : List Nat} {st x y : SvState}, StateShape d st →
      combPass2 d f st is = .ok x → combPass2 d f' st is = .ok y → x = y
  | [], _, _, _, _, hx, hy => by cases hx; cases hy; rfl
  | _ :: _, _, _, _, hs, hx, hy => by
      rcases combPass2_cons_ok hd hs hx with ⟨hn, hx⟩ | ⟨t, e, v, hp, hv, hx⟩ <;>
      rcases combPass2_cons_ok hd hs hy with ⟨hn', hy⟩ | ⟨t', e', v', hp', hv', hy⟩
      · exact combPass2_det hd hs hx hy
      · rw [hn] at hp'; cases hp'
      · rw [hn'] at hp; cases hp
      · rw [hp] at hp'; cases hp'
        rw [hv] at hv'; cases hv'
        exact combPass2_det hd (hs.combWrite t v) hx hy

/-- Two windows that both contain one bit overlap. -/
theorem overlaps_of_bit {n : String} {b : Nat} {w₁ w₂ : Win}
    (h₁ : (ReadWin.win n b 1).overlaps w₁ = true) (h₂ : (ReadWin.win n b 1).overlaps w₂ = true) :
    Win.overlaps w₁ w₂ = true := by
  obtain ⟨m₁, lo₁, v₁⟩ := w₁
  obtain ⟨m₂, lo₂, v₂⟩ := w₂
  rw [ReadWin.overlaps_win_one] at h₁ h₂
  rw [Win.overlaps_eq_true]
  obtain ⟨rfl, _, _⟩ := h₁
  obtain ⟨rfl, _, _⟩ := h₂
  exact ⟨rfl, by omega, by omega⟩

/-- The whole signal overlaps every window that one of its 1-bit reads
overlaps. -/
theorem overlaps_whole_of_bit {n : String} {b : Nat} {wn : Win}
    (h : (ReadWin.win n b 1).overlaps wn = true) : (ReadWin.whole n).overlaps wn = true := by
  obtain ⟨m, lo, w⟩ := wn
  rw [ReadWin.overlaps_win_one] at h
  obtain ⟨rfl, _, _⟩ := h
  simp [ReadWin.overlaps]

/-- A bit of a window read that overlaps a write window makes the window read
overlap it. -/
theorem overlaps_win_of_bit {n : String} {lo w k : Nat} {wn : Win} (hk : k < w)
    (h : (ReadWin.win n (lo + k) 1).overlaps wn = true) :
    (ReadWin.win n lo w).overlaps wn = true := by
  obtain ⟨m, lo', w'⟩ := wn
  rw [ReadWin.overlaps_win_one] at h
  obtain ⟨rfl, _, _⟩ := h
  simp only [ReadWin.overlaps, Win.overlaps_eq_true, true_and]
  exact ⟨by omega, by omega⟩

/-- **One driver per bit**: two processes whose windows contain the same bit
are the same process. -/
theorem driver_unique {d : Design2} (hd : CombDesign d) {n : String} {b j k : Nat}
    {wj wk : Win} (hj : d.Writes j wj) (hk : d.Writes k wk)
    (h₁ : (ReadWin.win n b 1).overlaps wj = true) (h₂ : (ReadWin.win n b 1).overlaps wk = true) :
    j = k := by
  refine Classical.byContradiction fun hne => ?_
  have := hd.disjoint j k wj wk hne hj hk
  rw [overlaps_of_bit h₁ h₂] at this
  cases this

/-- Bit agreement from equal windows: a bit inside a window on which two
bindings of equal width agree. -/
theorem agree_bit_of_extract {st st' : SvState} {n : String} {lo w b : Nat} {u u' : LVec}
    (hu : SvState.lookup st n = some u) (hu' : SvState.lookup st' n = some u')
    (hw : u.width = u'.width) (he : u.extract lo w = u'.extract lo w)
    (hb₁ : lo ≤ b) (hb₂ : b < lo + w) : (ReadWin.win n b 1).agree st st' := by
  apply ReadWin.agree_win_of_lookup hu hu' hw
  have h₁ := LVec.extract_extract u (lo := lo) (w := w) (lo' := b - lo) (w' := 1) (by omega)
  have h₂ := LVec.extract_extract u' (lo := lo) (w := w) (lo' := b - lo) (w' := 1) (by omega)
  rw [Nat.add_sub_cancel' hb₁] at h₁ h₂
  rw [← h₁, ← h₂, he]

/-- A process index with a write window is in range. -/
theorem lt_of_writes {d : Design2} {j : Nat} {wn : Win} (h : d.Writes j wn) :
    j < d.processes.size := by
  obtain ⟨t, e, hp, -⟩ := h
  exact (Array.getElem?_eq_some_iff.mp hp).1

/-- **The state a process sees inside a pass.** In a successful pass over a
duplicate-free order from a shaped state `st` to `st'`, the process `j` (an
assign `t := e`, listed in the order) runs at a shaped state `m` with the
widths of `st`, which agrees on EVERY bit with `st` or with `st'`; and every
placed window of `t` holds, in `st'`, its slice of the value `j` computed at
`m` (resized to the target's total width). -/
theorem combPass2_mid {d : Design2} (hd : CombDesign d) {f : Nat} {is : List Nat}
    {st st' : SvState} (hs : StateShape d st) (hnd : is.Nodup)
    (h : combPass2 d f st is = .ok st') {j : Nat} (hj : j ∈ is) {t : Lhs2} {e : Expr2}
    (hp : d.processes[j]? = some (.assign t e)) :
    ∃ m v, StateShape d m ∧
      (∀ x, (SvState.lookup m x).map LVec.width = (SvState.lookup st x).map LVec.width) ∧
      (∀ x b, (ReadWin.win x b 1).agree m st ∨ (ReadWin.win x b 1).agree m st') ∧
      evalPure2 m e = .ok v ∧
      ∀ (n : String) (lo w off : Nat), ((n, lo, w), off) ∈ Win.placed (t.wins d) →
        ∃ u, SvState.lookup st' n = some u ∧
          u.extract lo w = (v.resizeU (Win.sumWidth (t.wins d))).extract off w := by
  obtain ⟨pre, post, rfl⟩ := List.append_of_mem hj
  rw [combPass2_append] at h
  obtain ⟨m, hm, hrest⟩ := Res.bind_eq_ok.mp h
  obtain ⟨hsm, hfpre⟩ := combPass2_frame hd hs hm
  obtain ⟨-, hnd₂, hdisj⟩ := List.nodup_append.mp hnd
  have hjpost : j ∉ post := (List.nodup_cons.mp hnd₂).1
  have hfrest := (combPass2_frame hd hsm hrest).2
  rcases combPass2_cons_ok hd hsm hrest with ⟨hn, -⟩ | ⟨t', e', v, hp', hv, hpost⟩
  · rw [hp] at hn; cases hn
  rw [hp] at hp'; cases hp'
  have hfpost := (combPass2_frame hd (hsm.combWrite t v) hpost).2
  refine ⟨m, v, hsm, fun x => (combPass2_width hd hs hm x).symm, fun x b => ?_, hv, ?_⟩
  · -- per bit: the bit's driver (if any) has run before `m` or not
    by_cases hdrv : ∃ k wk, d.Writes k wk ∧ (ReadWin.win x b 1).overlaps wk = true
    · obtain ⟨k, wk, hWk, hov⟩ := hdrv
      by_cases hk : k ∈ pre
      · right
        refine hfrest _ fun i hi wn hWn => Bool.eq_false_iff.mpr fun hov' => ?_
        have hik := driver_unique hd hWn hWk hov' hov
        exact hdisj k hk i hi hik.symm
      · left
        refine ReadWin.agree_symm
          (hfpre _ fun i hi wn hWn => Bool.eq_false_iff.mpr fun hov' => ?_)
        have hik := driver_unique hd hWn hWk hov' hov
        exact hk (hik ▸ hi)
    · left
      refine ReadWin.agree_symm (hfpre _ fun i _ wn hWn => Bool.eq_false_iff.mpr fun hov => ?_)
      exact hdrv ⟨i, wn, hWn, hov⟩
  · -- each window of `j`: written at `m`, then untouched through `post`
    intro n lo w off hmem
    obtain ⟨u0, hu0, hue0⟩ := hd.combWrite_readBack hsm hp v hmem
    have hW : d.Writes j (n, lo, w) := ⟨t, e, hp, Win.mem_of_mem_placed hmem⟩
    have hwin := hfpost (.win n lo w) fun k hk wn hWn => by
      have hkj : j ≠ k := fun h => hjpost (h ▸ hk)
      exact hd.disjoint j k _ wn hkj hW hWn
    simp only [ReadWin.agree, hu0, Option.map_some] at hwin
    cases hu : SvState.lookup st' n with
    | none => rw [hu] at hwin; cases hwin
    | some u =>
        rw [hu] at hwin
        simp only [Option.map_some, Option.some.injEq, Prod.mk.injEq] at hwin
        exact ⟨u, rfl, hwin.2.symm.trans hue0⟩

/-- Filtering by a weaker predicate keeps at least as many elements. -/
theorem length_filter_le {α : Type} {p q : α → Bool} :
    ∀ {l : List α}, (∀ x ∈ l, p x = true → q x = true) →
      (l.filter p).length ≤ (l.filter q).length
  | [], _ => Nat.le_refl _
  | a :: l, h => by
      have ih := length_filter_le (l := l) fun y hy => h y (List.mem_cons_of_mem _ hy)
      by_cases hpa : p a = true
      · simp [hpa, h a (List.mem_cons_self ..) hpa]; omega
      · by_cases hqa : q a = true <;> simp [hpa, hqa] <;> omega

/-- Filtering by a weaker predicate that admits one more element keeps
strictly more elements. -/
theorem length_filter_lt {α : Type} {p q : α → Bool} :
    ∀ {l : List α}, (∀ x ∈ l, p x = true → q x = true) →
      (∃ x ∈ l, q x = true ∧ p x = false) → (l.filter p).length < (l.filter q).length
  | [], _, ⟨_, hx, _⟩ => absurd hx List.not_mem_nil
  | a :: l, h, ⟨x, hx, hqx, hpx⟩ => by
      have hle := length_filter_le (l := l) fun y hy => h y (List.mem_cons_of_mem _ hy)
      rcases List.mem_cons.mp hx with rfl | hx
      · simp [hqx, hpx]; omega
      · have ih := length_filter_lt (l := l) (fun y hy => h y (List.mem_cons_of_mem _ hy))
          ⟨x, hx, hqx, hpx⟩
        by_cases hpa : p a = true
        · simp [hpa, h a (List.mem_cons_self ..) hpa]; omega
        · by_cases hqa : q a = true <;> simp [hpa, hqa] <;> omega

end Comb2Term

open Comb2Term

/-! ## Rank certificates -/

/-- The body of `Acyclic` with the rank named: every process reads only
windows written by strictly lower-ranked processes. `Acyclic d` is
definitionally `∃ rank, AcyclicBy d rank`. -/
def AcyclicBy (d : Design2) (rank : Nat → Nat) : Prop :=
  ∀ (i j : Nat) (ti : Lhs2) (ei : Expr2) (tj : Lhs2) (ej : Expr2) (wj : Win),
    d.processes[i]? = some (.assign ti ei) →
    d.processes[j]? = some (.assign tj ej) → wj ∈ tj.wins d →
    (∃ r ∈ ei.reads, r.overlaps wj = true) → rank j < rank i

/-- `Acyclic` is a named rank certificate. -/
theorem acyclic_iff_exists_acyclicBy {d : Design2} : Acyclic d ↔ ∃ rank, AcyclicBy d rank :=
  Iff.rfl

/-- **Rank compression.** Every acyclicity certificate can be replaced by one
whose ranks on process indices are below the number of processes: rank `i`
becomes the number of processes of strictly lower rank. -/
theorem Acyclic.exists_rank_lt {d : Design2} (ha : Acyclic d) :
    ∃ rank, AcyclicBy d rank ∧ ∀ i, i < d.processes.size → rank i < d.processes.size := by
  obtain ⟨rank, hr⟩ := ha
  refine ⟨fun i => ((List.range d.processes.size).filter fun j => decide (rank j < rank i)).length,
    ?_, ?_⟩
  · intro i j ti ei tj ej wj hi hj hwj hov
    have hlt := hr i j ti ei tj ej wj hi hj hwj hov
    have hjn : j < d.processes.size := (Array.getElem?_eq_some_iff.mp hj).1
    refine length_filter_lt (fun x _ hx => ?_) ⟨j, List.mem_range.mpr hjn, ?_, ?_⟩
    · simp only [decide_eq_true_eq] at hx ⊢; omega
    · simpa using hlt
    · simp
  · intro i hi
    calc _ < (List.range d.processes.size).length :=
          List.length_filter_lt_length_iff_exists.mpr ⟨i, List.mem_range.mpr hi, by simp⟩
      _ = d.processes.size := List.length_range ..

/-! ## Rank-level agreement -/

/-- Bit `b` of `n` is driven, if at all, only by processes of rank below `r`
(vacuously true of an undriven bit). -/
def CombLowBit (d : Design2) (rank : Nat → Nat) (r : Nat) (n : String) (b : Nat) : Prop :=
  ∀ j wn, d.Writes j wn → (ReadWin.win n b 1).overlaps wn = true → rank j < r

/-- Two states agree on every width and on every bit driven only below
rank `r`. -/
def CombAgreeLow (d : Design2) (rank : Nat → Nat) (r : Nat) (s s' : SvState) : Prop :=
  (∀ x, (SvState.lookup s x).map LVec.width = (SvState.lookup s' x).map LVec.width) ∧
  ∀ x b, CombLowBit d rank r x b → (ReadWin.win x b 1).agree s s'

/-- Agreement below rank `r` weakens to any lower rank. -/
theorem CombAgreeLow.mono {d : Design2} {rank : Nat → Nat} {r r' : Nat} {s s' : SvState}
    (h : CombAgreeLow d rank r s s') (hle : r' ≤ r) : CombAgreeLow d rank r' s s' :=
  ⟨h.1, fun x b hlow => h.2 x b fun j wn hW hov => Nat.lt_of_lt_of_le (hlow j wn hW hov) hle⟩

/-- **A process of rank `r` cannot tell apart two states that agree below
rank `r`**: its right-hand side reads only bits driven below its rank (the
certificate) or undriven bits. -/
theorem evalPure2_eq_of_combAgreeLow {d : Design2} {rank : Nat → Nat}
    (hr : AcyclicBy d rank) {i : Nat} {t : Lhs2} {e : Expr2}
    (hp : d.processes[i]? = some (.assign t e)) {s s' : SvState}
    (h : CombAgreeLow d rank (rank i) s s') : evalPure2 s e = evalPure2 s' e := by
  apply evalPure2_congr_reads
  intro rd hrd
  cases rd with
  | whole x =>
      show SvState.lookup s x = SvState.lookup s' x
      refine ReadWin.lookup_eq_of_agree_bits fun b => h.2 x b fun j wn hWj hov => ?_
      obtain ⟨tj, ej, hpj, hwj⟩ := hWj
      exact hr i j t e tj ej wn hp hpj hwj ⟨.whole x, hrd, overlaps_whole_of_bit hov⟩
  | win x lo w =>
      refine ReadWin.agree_win_of_bits (h.1 x) fun k hk => h.2 x (lo + k) fun j wn hWj hov => ?_
      obtain ⟨tj, ej, hpj, hwj⟩ := hWj
      exact hr i j t e tj ej wn hp hpj hwj ⟨.win x lo w, hrd, overlaps_win_of_bit hk hov⟩

/-! ## Abstract pass sequences and the stabilization theorem -/

/-- An abstract run of comb passes: states `s p` (the state after `p` passes)
and pass orders `os p`, each order listing every process exactly once, each
pass a successful `combPass2` at some fuel. The settle's own pass sequence is
one (`CombPassSeq.of_combSettleSeq`). -/
structure CombPassSeq (d : Design2) (s : Nat → SvState) (os : Nat → List Nat) : Prop where
  /-- The run starts from a shaped state. -/
  shape0 : StateShape d (s 0)
  /-- No order lists a process twice. -/
  nodup : ∀ p, (os p).Nodup
  /-- Every order lists every process. -/
  mem : ∀ p i, i < d.processes.size → i ∈ os p
  /-- Pass `p` maps `s p` to `s (p + 1)`. -/
  pass : ∀ p, ∃ f, combPass2 d f (s p) (os p) = .ok (s (p + 1))

namespace CombPassSeq

variable {d : Design2} {s : Nat → SvState} {os : Nat → List Nat}

/-- Every state of the run is shaped. -/
theorem shape (hd : CombDesign d) (hq : CombPassSeq d s os) : ∀ p, StateShape d (s p)
  | 0 => hq.shape0
  | p + 1 => by
      obtain ⟨f, hf⟩ := hq.pass p
      exact (combPass2_frame hd (hq.shape hd p) hf).1

/-- Widths never change along the run. -/
theorem width (hd : CombDesign d) (hq : CombPassSeq d s os) :
    ∀ p x, (SvState.lookup (s p) x).map LVec.width = (SvState.lookup (s 0) x).map LVec.width
  | 0, _ => rfl
  | p + 1, x => by
      obtain ⟨f, hf⟩ := hq.pass p
      rw [← combPass2_width hd (hq.shape hd p) hf x, hq.width hd p x]

/-- An undriven bit never changes along the run. -/
theorem undriven (hd : CombDesign d) (hq : CombPassSeq d s os) {x : String} {b : Nat}
    (hn : ¬∃ j wn, d.Writes j wn ∧ (ReadWin.win x b 1).overlaps wn = true) :
    ∀ p, (ReadWin.win x b 1).agree (s 0) (s p)
  | 0 => ReadWin.agree_refl _ _
  | p + 1 => by
      obtain ⟨f, hf⟩ := hq.pass p
      refine ReadWin.agree_trans (hq.undriven hd hn p)
        ((combPass2_frame hd (hq.shape hd p) hf).2 _ fun i _ wn hWn =>
          Bool.eq_false_iff.mpr fun hov => hn ⟨i, wn, hWn, hov⟩)

/-- **The inductive core.** All states after at least `r` passes agree on
every width and on every bit driven only below rank `r`. -/
theorem combAgreeLow (hd : CombDesign d) {rank : Nat → Nat} (hr : AcyclicBy d rank)
    (hq : CombPassSeq d s os) :
    ∀ r p p', r ≤ p → r ≤ p' → CombAgreeLow d rank r (s p) (s p') := by
  have hwid : ∀ p p' x, (SvState.lookup (s p) x).map LVec.width =
      (SvState.lookup (s p') x).map LVec.width := fun p p' x =>
    (hq.width hd p x).trans (hq.width hd p' x).symm
  intro r
  induction r with
  | zero =>
      intro p p' _ _
      refine ⟨hwid p p', fun x b hlow => ?_⟩
      have hn : ¬∃ j wn, d.Writes j wn ∧ (ReadWin.win x b 1).overlaps wn = true :=
        fun ⟨j, wn, hW, hov⟩ => Nat.not_lt_zero _ (hlow j wn hW hov)
      exact ReadWin.agree_trans (ReadWin.agree_symm (hq.undriven hd hn p)) (hq.undriven hd hn p')
  | succ r ih =>
      intro p p' hp hp'
      refine ⟨hwid p p', fun x b hlow => ?_⟩
      by_cases hdrv : ∃ j wn, d.Writes j wn ∧ (ReadWin.win x b 1).overlaps wn = true
      · obtain ⟨j, ⟨nj, loj, wj⟩, hWj, hov⟩ := hdrv
        have hrj : rank j < r + 1 := hlow j _ hWj hov
        obtain ⟨tj, ej, hpj, hwj⟩ := hWj
        have hjn : j < d.processes.size := (Array.getElem?_eq_some_iff.mp hpj).1
        obtain ⟨offj, hoff⟩ := Win.exists_mem_placed hwj
        rw [ReadWin.overlaps_win_one] at hov
        obtain ⟨rfl, hb₁, hb₂⟩ := hov
        obtain ⟨q, rfl⟩ : ∃ q, p = q + 1 := ⟨p - 1, by omega⟩
        obtain ⟨q', rfl⟩ : ∃ q', p' = q' + 1 := ⟨p' - 1, by omega⟩
        obtain ⟨f, hf⟩ := hq.pass q
        obtain ⟨f', hf'⟩ := hq.pass q'
        obtain ⟨m, v, -, hmw, hmb, hv, hU⟩ :=
          combPass2_mid hd (hq.shape hd q) (hq.nodup q) hf (hq.mem q j hjn) hpj
        obtain ⟨u, hu, hue⟩ := hU _ _ _ _ hoff
        obtain ⟨m', v', -, hmw', hmb', hv', hU'⟩ :=
          combPass2_mid hd (hq.shape hd q') (hq.nodup q') hf' (hq.mem q' j hjn) hpj
        obtain ⟨u', hu', hue'⟩ := hU' _ _ _ _ hoff
        -- the two mid-pass states agree below `rank j`
        have hmm : CombAgreeLow d rank (rank j) m m' := by
          refine ⟨fun y => (hmw y).trans ((hwid q q' y).trans (hmw' y).symm), fun y c hlow' => ?_⟩
          have hlowr : CombLowBit d rank r y c := fun i wn hW hov =>
            Nat.lt_of_lt_of_le (hlow' i wn hW hov) (Nat.le_of_lt_succ hrj)
          have hsame : ∀ a a', r ≤ a → r ≤ a' → (ReadWin.win y c 1).agree (s a) (s a') :=
            fun a a' ha ha' => (ih a a' ha ha').2 y c hlowr
          have hq₁ : r ≤ q := by omega
          have hq₂ : r ≤ q' := by omega
          rcases hmb y c with h₁ | h₁ <;> rcases hmb' y c with h₂ | h₂
          · exact ReadWin.agree_trans h₁
              (ReadWin.agree_trans (hsame q q' hq₁ hq₂) (ReadWin.agree_symm h₂))
          · exact ReadWin.agree_trans h₁
              (ReadWin.agree_trans (hsame q (q' + 1) hq₁ (by omega)) (ReadWin.agree_symm h₂))
          · exact ReadWin.agree_trans h₁
              (ReadWin.agree_trans (hsame (q + 1) q' (by omega) hq₂) (ReadWin.agree_symm h₂))
          · exact ReadWin.agree_trans h₁
              (ReadWin.agree_trans (hsame (q + 1) (q' + 1) (by omega) (by omega))
                (ReadWin.agree_symm h₂))
        have hvv : v = v' := by
          have := evalPure2_eq_of_combAgreeLow hr hpj hmm
          rw [hv, hv'] at this
          cases this; rfl
        subst hvv
        have huw : u.width = u'.width := by
          have := hwid (q + 1) (q' + 1) x
          rw [hu, hu'] at this
          simpa using this
        exact agree_bit_of_extract hu hu' huw (hue.trans hue'.symm) hb₁ hb₂
      · exact ReadWin.agree_trans (ReadWin.agree_symm (hq.undriven hd hdrv p))
          (hq.undriven hd hdrv p')

/-- **Stabilization.** Under a rank certificate bounded by `R` on the process
indices, the run is constant from pass `R` on. -/
theorem stable (hd : CombDesign d) {rank : Nat → Nat} (hr : AcyclicBy d rank) {R : Nat}
    (hR : ∀ i, i < d.processes.size → rank i < R) (hq : CombPassSeq d s os) :
    ∀ p, R ≤ p → s p = s R := by
  intro p hp
  refine StateShape.eq_of_agree_bits hd (hq.shape hd p) (hq.shape hd R) fun x b => ?_
  exact (hq.combAgreeLow hd hr R p R hp (Nat.le_refl R)).2 x b fun j _ hW _ =>
    hR j (lt_of_writes hW)

end CombPassSeq

/-! ## The settle as a pass sequence -/

/-- The states a settle from `st` (schedule counter `k`) visits, pass by
pass: pass `p` runs the order `σ.choose (k + p) d.combIndices` at expression
fuel `d.combFuel` (enough, and the result does not depend on it:
`Comb2Term.combPass2_det`). -/
def combSettleSeq (d : Design2) (σ : ScheduleOracle) (k : Nat) (st : SvState) :
    Nat → SvState
  | 0 => st
  | p + 1 =>
      match combPass2 d d.combFuel (combSettleSeq d σ k st p)
          (σ.choose (k + p) d.combIndices) with
      | .ok s => s
      | _ => combSettleSeq d σ k st p

private theorem combSettleSeq_succ_of_ok {d : Design2} {σ : ScheduleOracle} {k : Nat}
    {st : SvState} {p : Nat} {x : SvState}
    (h : combPass2 d d.combFuel (combSettleSeq d σ k st p)
      (σ.choose (k + p) d.combIndices) = .ok x) :
    combSettleSeq d σ k st (p + 1) = x := by
  simp only [combSettleSeq, h]

/-- The settle sequence of a shaped state is shaped, and each of its steps is
a successful pass. -/
theorem combSettleSeq_spec {d : Design2} (hd : CombDesign d) (σ : ScheduleOracle) (k : Nat)
    {st : SvState} (hs : StateShape d st) :
    ∀ p, StateShape d (combSettleSeq d σ k st p) ∧
      combPass2 d d.combFuel (combSettleSeq d σ k st p) (σ.choose (k + p) d.combIndices) =
        .ok (combSettleSeq d σ k st (p + 1))
  | p => by
      have hsp : StateShape d (combSettleSeq d σ k st p) := by
        cases p with
        | zero => exact hs
        | succ p =>
            obtain ⟨hs', hpass⟩ := combSettleSeq_spec hd σ k hs p
            exact (combPass2_frame hd hs' hpass).1
      obtain ⟨x, hx⟩ := combPass2_ok_of_combFuel hd (Nat.le_refl _)
        (σ.choose (k + p) d.combIndices) hsp
      exact ⟨hsp, by rw [combSettleSeq_succ_of_ok hx]; exact hx⟩

/-- Every state of the settle sequence is shaped. -/
theorem combSettleSeq_shape {d : Design2} (hd : CombDesign d) (σ : ScheduleOracle) (k : Nat)
    {st : SvState} (hs : StateShape d st) (p : Nat) :
    StateShape d (combSettleSeq d σ k st p) :=
  (combSettleSeq_spec hd σ k hs p).1

/-- Pass `p` of a settle (at ANY fuel at which it succeeds) produces the next
state of the settle sequence. -/
theorem combSettleSeq_pass {d : Design2} (hd : CombDesign d) (σ : ScheduleOracle) (k : Nat)
    {st : SvState} (hs : StateShape d st) {p f : Nat} {x : SvState}
    (h : combPass2 d f (combSettleSeq d σ k st p) (σ.choose (k + p) d.combIndices) = .ok x) :
    x = combSettleSeq d σ k st (p + 1) :=
  combPass2_det hd (combSettleSeq_shape hd σ k hs p) h (combSettleSeq_spec hd σ k hs p).2

/-- The settle sequence is an abstract pass sequence. -/
theorem CombPassSeq.of_combSettleSeq {d : Design2} (hd : CombDesign d) (σ : ScheduleOracle)
    (k : Nat) {st : SvState} (hs : StateShape d st) :
    CombPassSeq d (combSettleSeq d σ k st) (fun p => σ.choose (k + p) d.combIndices) where
  shape0 := hs
  nodup p := hd.nodup_choose σ (k + p)
  mem p i hi := (hd.mem_choose σ (k + p) i).mpr hi
  pass p := ⟨d.combFuel, (combSettleSeq_spec hd σ k hs p).2⟩

/-- **The settle loop.** If the settle sequence is stable at pass `p + j`
(`s (p+j) = s (p+j+1)`), a settle entered at pass `p` with fuel for `j + 1`
more passes above `d.combFuel` returns — a state of the settle sequence, so
a shaped one (`combSettleSeq_shape`). -/
theorem combSettle2_ok_of_stable {d : Design2} (hd : CombDesign d) {σ : ScheduleOracle}
    {k : Nat} {st : SvState} (hs : StateShape d st) :
    ∀ (j p fuel : Nat),
      combSettleSeq d σ k st (p + j) = combSettleSeq d σ k st (p + j + 1) →
      d.combFuel + j + 1 ≤ fuel →
      ∃ S k', combSettle2 d σ fuel (combSettleSeq d σ k st p) (k + p) = .ok (S, k') ∧
        StateShape d S
  | _, _, 0, _, hf => absurd hf (by omega)
  | j, p, f + 1, hstab, hf => by
      obtain ⟨x, hx⟩ := combPass2_ok_of_combFuel hd (by omega : d.combFuel ≤ f)
        (σ.choose (k + p) d.combIndices) (combSettleSeq_shape hd σ k hs p)
      have hxe := combSettleSeq_pass hd σ k hs hx
      subst hxe
      simp only [combSettle2, hx, Res.ok_bind]
      by_cases heq : (combSettleSeq d σ k st (p + 1) == combSettleSeq d σ k st p) = true
      · rw [if_pos heq]
        exact ⟨combSettleSeq d σ k st (p + 1), _, rfl, combSettleSeq_shape hd σ k hs (p + 1)⟩
      · rw [if_neg heq]
        cases j with
        | zero =>
            exfalso; apply heq
            rw [Nat.add_zero] at hstab
            rw [← hstab]
            exact beq_self_eq_true _
        | succ j =>
            have h := combSettle2_ok_of_stable hd hs j (p + 1) f
              (by rw [show p + 1 + j = p + (j + 1) by omega]; exact hstab) (by omega)
            rwa [show k + (p + 1) = k + p + 1 by omega] at h

/-! ## Termination -/

/-- **Explicit settle fuel** of a comb design: expression fuel for every
right-hand side (`Design2.combFuel`) plus one settle iteration per process
plus the final, confirming pass. -/
def Design2.settleFuel (d : Design2) : Nat := d.combFuel + d.processes.size + 1

/-- **Settle terminates in a shaped state, explicit bound.** On a comb design
with an acyclicity certificate, `combSettle2` returns `.ok` from every shaped
state, under every schedule, at every fuel from `Design2.settleFuel d` on, and
the returned state has the comb shape (it is a state of the settle sequence). -/
theorem combSettle2_terminates_shape_of_le {d : Design2} (hd : CombDesign d)
    (ha : Acyclic d) {fuel : Nat} (hf : d.settleFuel ≤ fuel) (σ : ScheduleOracle)
    (st : SvState) (k : Nat) (hs : StateShape d st) :
    ∃ S k', combSettle2 d σ fuel st k = .ok (S, k') ∧ StateShape d S := by
  obtain ⟨rank, hr, hlt⟩ := ha.exists_rank_lt
  have hstab := (CombPassSeq.of_combSettleSeq hd σ k hs).stable hd hr hlt
    (d.processes.size + 1) (Nat.le_succ _)
  have h := combSettle2_ok_of_stable hd hs d.processes.size 0 fuel
    (by rw [Nat.zero_add, hstab]) (by unfold Design2.settleFuel at hf; omega)
  simpa [combSettleSeq] using h

/-- **Settle terminates, explicit bound.** On a comb design with an
acyclicity certificate, `combSettle2` returns `.ok` from every shaped state,
under every schedule, at every fuel from `Design2.settleFuel d` on. -/
theorem combSettle2_terminates_of_le {d : Design2} (hd : CombDesign d) (ha : Acyclic d)
    {fuel : Nat} (hf : d.settleFuel ≤ fuel) (σ : ScheduleOracle) (st : SvState) (k : Nat)
    (hs : StateShape d st) : ∃ S k', combSettle2 d σ fuel st k = .ok (S, k') := by
  obtain ⟨S, k', h, -⟩ := combSettle2_terminates_shape_of_le hd ha hf σ st k hs
  exact ⟨S, k', h⟩

/-- **Settle terminates** (the headline statement): on a comb design with an
acyclicity certificate there is a fuel bound above which `combSettle2`
returns `.ok` from every shaped state, under every schedule and schedule
counter. The bound is `Design2.settleFuel d` (`combSettle2_terminates_of_le`). -/
theorem combSettle2_terminates {d : Design2} (hd : CombDesign d) (ha : Acyclic d) :
    ∃ F, ∀ fuel, F ≤ fuel → ∀ σ st k, StateShape d st →
      ∃ S k', combSettle2 d σ fuel st k = .ok (S, k') :=
  ⟨d.settleFuel, fun _ hf σ st k hs => combSettle2_terminates_of_le hd ha hf σ st k hs⟩

/-- **Settle terminates in a shaped state** (`combSettle2_terminates` with
the shape of the result): same hypotheses, and the returned state also has
the comb shape. The bound is `Design2.settleFuel d`
(`combSettle2_terminates_shape_of_le`). -/
theorem combSettle2_terminates_shape {d : Design2} (hd : CombDesign d) (ha : Acyclic d) :
    ∃ F, ∀ fuel, F ≤ fuel → ∀ σ st k, StateShape d st →
      ∃ S k', combSettle2 d σ fuel st k = .ok (S, k') ∧ StateShape d S :=
  ⟨d.settleFuel, fun _ hf σ st k hs =>
    combSettle2_terminates_shape_of_le hd ha hf σ st k hs⟩

/-! ## Smoke test: the full adder settles -/

namespace Comb2Smoke

/-- The full adder's explicit settle fuel. -/
example : faDesign.settleFuel = faDesign.combFuel + 5 := rfl

/-- The full adder's settle from its startup state returns under every
schedule, at the explicit bound. -/
example (σ : ScheduleOracle) (k : Nat) :
    ∃ S k', combSettle2 faDesign σ faDesign.settleFuel (initState2 faDesign) k = .ok (S, k') :=
  combSettle2_terminates_of_le fa_comb fa_acyclic (Nat.le_refl _) σ _ k
    (StateShape.of_initState2 fa_comb)

#guard (combSettle2 faDesign σ_rev faDesign.settleFuel (initState2 faDesign) 0).toOption.isSome

end Comb2Smoke

end LeanModels.Sv