module

/-!
# Scope Cache: Specification and Verification

This file was mostly generated by Claude and verified by Lean. If the proofs
here break due to upstream changes and fixing them is a hassle, it is fine to
just delete this test file.

This file models the `scope_cache` data structure from `src/library/scope_cache.h`
and verifies that the optimized implementation refines the simple specification.

## Structure

- `Spec`: The specification — an eager entry-list model where stale entries are
  cleaned immediately on pop. No generation counters needed.
- `Imp`: The implementation — a lazy entry-list model with generation-based
  staleness detection (matching the C++ `scope_cache`).
- `Tests`: Exhaustive testing of both models on all bounded operation sequences.
- `Proofs`: Formal proof that Imp refines Spec via a simulation invariant.
-/

namespace ScopeCache

/-! ## Specification: Eager entry-list model

Each key independently maintains a list of entries. Each entry has:
- `result`: the cached value
- `scopeLevel`: the scope at which it was inserted
- `resultScope`: the outermost scope the result depends on

The entry covers scope levels `resultScope` through `scopeLevel`.
Entries are non-overlapping and sorted by `scopeLevel`.

On pop, entries whose `resultScope` exceeds the new scope are discarded.
This is the "eager" cleanup — no lazy staleness detection needed.
-/
namespace Spec

structure Entry where
  result : Nat
  scopeLevel : Nat
  resultScope : Nat
  deriving Repr, DecidableEq, BEq

structure State where
  entries : List Entry
  scopeN : Nat
  deriving Repr, DecidableEq, BEq

def empty : State := { entries := [], scopeN := 0 }

def push (s : State) : State :=
  { s with scopeN := s.scopeN + 1 }

def pop (s : State) : State :=
  let newScope := s.scopeN - 1
  { entries := (s.entries.filter fun e => e.resultScope ≤ newScope).map fun e =>
      if e.scopeLevel > newScope then { e with scopeLevel := newScope } else e
    scopeN := newScope }

def lookup (s : State) : Option (Nat × Nat) :=
  match s.entries.getLast? with
  | some top => if top.scopeLevel == s.scopeN then some (top.result, top.resultScope) else none
  | none => none

def insert (s : State) (value : Nat) (resultScope : Nat) : State × Nat :=
  let shared := match s.entries.getLast? with
    | some top => if top.resultScope == resultScope then top.result else value
    | none => value
  let entries' := s.entries.filter fun e => e.scopeLevel < resultScope
  let newEntry : Entry := { result := shared, scopeLevel := s.scopeN, resultScope }
  ({ s with entries := entries' ++ [newEntry] }, shared)

end Spec

/-! ## Implementation: Lazy entry-list model with generation counters

Models the C++ `scope_cache` for a single key. Entries are NOT cleaned on pop;
instead, they are lazily validated via generation counters when accessed (rewind).
-/
namespace Imp

abbrev GenList := List Nat

structure Entry where
  result : Nat
  scopeLevel : Nat
  scopeGens : GenList  -- snapshot of gen list at insert time
  resultScope : Nat
  deriving Repr, DecidableEq, BEq, Inhabited

structure State where
  entries : List Entry
  scopeN : Nat
  genCounter : Nat
  currentGens : GenList  -- head = gen for scopeN
  deriving Repr

def empty : State :=
  { entries := [], scopeN := 0, genCounter := 0, currentGens := [0] }

def push (s : State) : State :=
  { s with
    scopeN := s.scopeN + 1
    genCounter := s.genCounter + 1
    currentGens := (s.genCounter + 1) :: s.currentGens }

def pop (s : State) : State :=
  { s with
    scopeN := s.scopeN - 1
    currentGens := s.currentGens.tail }

/-- Search from `level` down to `resultScope` for a level where entry and current gens match.
    Returns the matching level and the entry's remaining gen list at that level. -/
def findValidLevel (eGens cGens : GenList) (level resultScope : Nat) :
    Option (Nat × GenList) :=
  match eGens.head?, cGens.head? with
  | some eg, some cg =>
    if eg == cg then some (level, eGens)
    else if h : level ≤ resultScope then none
    else findValidLevel eGens.tail cGens.tail (level - 1) resultScope
  | _, _ => none
termination_by level - resultScope

/-- Rewind: clean up the entry stack from the back. -/
def rewind (entries : List Entry) (scopeN : Nat) (currentGens : GenList) : List Entry :=
  go entries.reverse []
where
  go : List Entry → List Entry → List Entry
  | [], acc => acc
  | top :: rest, acc =>
    if top.resultScope > scopeN then
      go rest acc
    else
      let degradedLevel := min top.scopeLevel scopeN
      let degradedGens := top.scopeGens.drop (top.scopeLevel - degradedLevel)
      let currentGensAligned := currentGens.drop (scopeN - degradedLevel)
      match findValidLevel degradedGens currentGensAligned degradedLevel top.resultScope with
      | some (lvl, eGens) =>
        rest.reverse ++ [{ top with scopeLevel := lvl, scopeGens := eGens }] ++ acc
      | none => go rest acc

def lookup (s : State) : Option (Nat × Nat) :=
  let entries' := rewind s.entries s.scopeN s.currentGens
  match entries'.getLast? with
  | some top => if top.scopeLevel == s.scopeN then some (top.result, top.resultScope) else none
  | none => none

def insert (s : State) (value : Nat) (resultScope : Nat) : State × Nat :=
  let entries' := rewind s.entries s.scopeN s.currentGens
  let shared := match entries'.getLast? with
    | some top => if top.resultScope == resultScope then top.result else value
    | none => value
  let entries'' := entries'.filter fun e => e.scopeLevel < resultScope
  let newEntry : Entry := {
    result := shared, scopeLevel := s.scopeN, scopeGens := s.currentGens, resultScope
  }
  ({ s with entries := entries'' ++ [newEntry] }, shared)

/-- Erase generation info from an Imp entry to get a Spec entry. -/
def Entry.toSpec (e : Entry) : Spec.Entry :=
  { result := e.result, scopeLevel := e.scopeLevel, resultScope := e.resultScope }

end Imp

/-! ## Tests -/
namespace Tests

-- Basic Spec tests
#eval do
  let s := Spec.empty
  assert! Spec.lookup s == none
  let (s, _) := Spec.insert s 100 0
  assert! Spec.lookup s == some (100, 0)
  let s := Spec.push s
  assert! Spec.lookup s == none  -- entry at scope 0, not at scope 1
  let (s, v) := Spec.insert s 200 0
  assert! v == 100  -- sharing
  assert! Spec.lookup s == some (100, 0)
  let s := Spec.pop s
  assert! Spec.lookup s == some (100, 0)
  return ()

-- Basic Imp tests
#eval do
  let s := Imp.empty
  assert! Imp.lookup s == none
  let (s, _) := Imp.insert s 100 0
  assert! Imp.lookup s == some (100, 0)
  let s := Imp.push s
  assert! Imp.lookup s == none
  let (s, v) := Imp.insert s 200 0
  assert! v == 100  -- sharing
  assert! Imp.lookup s == some (100, 0)
  let s := Imp.pop s
  assert! Imp.lookup s == some (100, 0)
  return ()

-- Re-entry test
#eval do
  let s := Imp.empty
  let s := Imp.push s
  let (s, _) := Imp.insert s 100 1
  assert! Imp.lookup s == some (100, 1)
  let s := Imp.pop s
  let s := Imp.push s  -- re-enter scope 1
  assert! Imp.lookup s == none  -- stale!
  return ()

-- Degradation test
#eval do
  let s := Imp.empty
  let s := Imp.push s
  let s := Imp.push s
  let s := Imp.push s  -- scope 3
  let (s, _) := Imp.insert s 100 1
  assert! Imp.lookup s == some (100, 1)
  let s := Imp.pop s  -- scope 2
  assert! Imp.lookup s == some (100, 1)
  let s := Imp.pop s  -- scope 1
  assert! Imp.lookup s == some (100, 1)
  return ()

-- WalkDown test: all scopes re-entered, walkDown must iterate to scope 0
#eval do
  let s := Imp.empty
  let s := Imp.push s    -- scope 1
  let s := Imp.push s    -- scope 2
  let s := Imp.push s    -- scope 3
  let (s, _) := Imp.insert s 100 0
  let s := Imp.pop s     -- scope 2
  let s := Imp.pop s     -- scope 1
  let s := Imp.pop s     -- scope 0
  let s := Imp.push s    -- scope 1 (re-enter)
  let s := Imp.push s    -- scope 2 (re-enter)
  let s := Imp.push s    -- scope 3 (re-enter)
  -- Entry should survive at level 0 (only scope 0 unchanged)
  let (s, v) := Imp.insert s 200 0
  assert! v == 100  -- sharing: rewind found entry at level 0
  assert! Imp.lookup s == some (100, 0)
  return ()

/-! ### Exhaustive verification

Verify that Spec and Imp produce identical observable results for ALL
operation sequences up to a bounded depth and length. -/

partial def verifySpecImp (maxOps maxScope : Nat) : IO Unit :=
  go maxOps Spec.empty Imp.empty 0 []
where
  go (fuel : Nat) (spec : Spec.State) (imp : Imp.State) (depth : Nat)
      (trace : List String) : IO Unit := do
    if fuel == 0 then return
    -- push
    if depth < maxScope then
      let spec' := Spec.push spec
      let imp' := Imp.push imp
      if Spec.lookup spec' != Imp.lookup imp' then
        throw <| IO.userError s!"push lookup mismatch after {trace}"
      go (fuel - 1) spec' imp' (depth + 1) (trace ++ ["push"])
    -- pop
    if depth > 0 then
      let spec' := Spec.pop spec
      let imp' := Imp.pop imp
      if Spec.lookup spec' != Imp.lookup imp' then
        throw <| IO.userError s!"pop lookup mismatch after {trace}"
      go (fuel - 1) spec' imp' (depth - 1) (trace ++ ["pop"])
    -- inserts
    for v in [0, 1, 2] do
      for rs in List.range (depth + 1) do
        let (spec', sv) := Spec.insert spec v rs
        let (imp', iv) := Imp.insert imp v rs
        if sv != iv then
          throw <| IO.userError s!"insert sharing mismatch after {trace}: insert {v} {rs}"
        if Spec.lookup spec' != Imp.lookup imp' then
          throw <| IO.userError s!"insert lookup mismatch after {trace}: insert {v} {rs}"
        go (fuel - 1) spec' imp' depth (trace ++ [s!"ins({v},{rs})"])

#eval do
  verifySpecImp 4 3
  return ()

end Tests

/-! ## Formal Proofs

We prove that for any sequence of operations starting from empty,
Spec and Imp produce identical lookup results and insert-sharing values.

### Approach

We define a simulation invariant `SimInv` relating Spec and Imp states:
the Imp entries, after rewinding, match the Spec entries (modulo generation info).
We prove:
1. `SimInv` holds for empty states
2. Each operation preserves `SimInv`
3. `SimInv` implies lookup equivalence and insert-sharing equivalence

Key rewind properties are stated as separate lemmas.
-/
namespace Proofs

/-! ### Rewind helper lemmas

These lemmas capture the essential properties of the `rewind` function needed
for the simulation proof. They express how rewind behaves under push, pop,
and when applied to freshly-inserted entries. -/

/-- After Imp.insert, the entries are `filtered ++ [newEntry]` where newEntry
    has current scope and gens. Rewind at the same scope is identity because
    rewind processes from the back and immediately finds the valid last entry. -/
theorem rewind_after_insert (filtered : List Imp.Entry) (newEntry : Imp.Entry)
    (scopeN : Nat) (gens : Imp.GenList)
    (hSL : newEntry.scopeLevel = scopeN)
    (hGens : newEntry.scopeGens = gens)
    (hRS : newEntry.resultScope ≤ scopeN)
    (hGensPos : gens.length > 0) :
    Imp.rewind (filtered ++ [newEntry]) scopeN gens = filtered ++ [newEntry] := by
  subst hSL; subst hGens
  simp only [Imp.rewind]
  rw [List.reverse_append, List.reverse_cons, List.reverse_nil, List.nil_append,
    List.singleton_append]
  rw [Imp.rewind.go]
  -- resultScope ≤ scopeLevel, so not discarded
  simp only [show (newEntry.resultScope > newEntry.scopeLevel) = False from
    eq_false (Nat.not_lt.mpr hRS), ↓reduceIte]
  -- degradedLevel = scopeLevel, drops are zero
  simp only [Nat.min_self, Nat.sub_self, List.drop_zero]
  -- findValidLevel with same eGens and cGens: head? values are equal
  obtain ⟨g, gl, hgl⟩ := List.exists_cons_of_length_pos hGensPos
  conv => lhs; rw [hgl, Imp.findValidLevel]; simp
  rw [← hgl]

/-- When the first gen check fails due to a fresh gen g, findValidLevel at (n+1)
    with g::cGens reduces to findValidLevel at n with eGens.tail and cGens. -/
theorem findValidLevel_skip_fresh (eGens cGens : Imp.GenList) (n g resultScope : Nat)
    (hFreshHead : ∀ k < eGens.length, eGens[k]! < g) (hRS : resultScope ≤ n) :
    Imp.findValidLevel eGens (g :: cGens) (n + 1) resultScope =
    Imp.findValidLevel eGens.tail cGens n resultScope := by
  cases eGens with
  | nil =>
    -- eGens empty → both sides none
    simp [Imp.findValidLevel]
  | cons v vs =>
    -- eGens = v :: vs, head? = some v, tail = vs
    have hvg : v < g := by have := hFreshHead 0 (by simp); simpa using this
    rw [Imp.findValidLevel]
    simp [show (v == g) = false from by simp [Nat.ne_of_lt hvg],
          show ¬(n + 1 ≤ resultScope) from Nat.not_le.mpr (Nat.lt_succ_of_le hRS)]

/-- Push rewind equivalence: rewind at (n+1, g::gens) equals rewind at (n, gens)
    when g is fresh (strictly greater than all gens in all entries). -/
theorem rewind_push_equiv (entries : List Imp.Entry) (n g : Nat) (gens : Imp.GenList)
    (hFresh : ∀ e ∈ entries, ∀ k < e.scopeGens.length, e.scopeGens[k]! < g)
    (hRSSL : ∀ e ∈ entries, e.resultScope ≤ e.scopeLevel) :
    Imp.rewind entries (n + 1) (g :: gens) = Imp.rewind entries n gens := by
  simp only [Imp.rewind]
  suffices ∀ revEntries acc,
      (∀ e ∈ revEntries, ∀ k < e.scopeGens.length, e.scopeGens[k]! < g) →
      (∀ e ∈ revEntries, e.resultScope ≤ e.scopeLevel) →
      Imp.rewind.go (n + 1) (g :: gens) revEntries acc =
      Imp.rewind.go n gens revEntries acc by
    exact this entries.reverse [] (fun e he => hFresh e (List.mem_reverse.mp he))
      (fun e he => hRSSL e (List.mem_reverse.mp he))
  intro revEntries acc hFreshRev hRSRev
  induction revEntries generalizing acc with
  | nil => simp [Imp.rewind.go]
  | cons top rest ih =>
    have hTF := hFreshRev top (List.mem_cons_self ..)
    have hTR := hRSRev top (List.mem_cons_self ..)
    have hRF := fun e he => hFreshRev e (List.mem_cons_of_mem _ he)
    have hRR := fun e he => hRSRev e (List.mem_cons_of_mem _ he)
    simp only [Imp.rewind.go]
    -- Helper: degraded gens freshness (all gens in dropped list are still < g)
    have hDGFresh : ∀ d, ∀ k < (top.scopeGens.drop d).length,
        (top.scopeGens.drop d)[k]! < g := by
      intro d k hk
      simp only [List.length_drop] at hk
      have hkd : k + d < top.scopeGens.length := Nat.add_lt_of_lt_sub hk
      have h1 := hTF (k + d) hkd
      rw [show top.scopeGens[k + d]! = (top.scopeGens.drop d)[k]! from by
        simp [List.getElem!_eq_getElem?_getD, List.getElem?_drop, Nat.add_comm]] at h1
      exact h1
    -- Main case split: first handle LHS (scope n+1), then RHS (scope n)
    by_cases hGN1 : top.resultScope > n + 1
    · -- resultScope > n+1: both discard (also > n)
      have hGN : top.resultScope > n := Nat.lt_of_le_of_lt (Nat.le_succ n) hGN1
      simp only [hGN1, hGN, ↓reduceIte]; exact ih acc hRF hRR
    · simp only [show ¬(top.resultScope > n + 1) from hGN1, ↓reduceIte]
      by_cases hGN : top.resultScope > n
      · -- resultScope = n+1: discarded at (n), findValidLevel returns none at (n+1)
        simp only [hGN, ↓reduceIte]
        have hEq : top.resultScope = n + 1 := Nat.le_antisymm (Nat.not_lt.mp hGN1) hGN
        have hSL : top.scopeLevel ≥ n + 1 := hEq ▸ hTR
        simp only [show min top.scopeLevel (n + 1) = n + 1 from Nat.min_eq_right hSL,
                    Nat.sub_self, List.drop_zero]
        rw [hEq]; cases hDG : top.scopeGens.drop (top.scopeLevel - (n + 1)) with
        | nil => simp [Imp.findValidLevel]; exact ih acc hRF hRR
        | cons v vs =>
          rw [Imp.findValidLevel]; simp only [List.head?_cons]
          have hvg : v < g := by
            have := hDGFresh (top.scopeLevel - (n + 1)) 0 (by simp [hDG])
            simpa [hDG] using this
          simp [show (v == g) = false from by simp [Nat.ne_of_lt hvg]]
          exact ih acc hRF hRR
      · -- resultScope ≤ n: both not discarded
        simp only [show ¬(top.resultScope > n) from hGN, ↓reduceIte]
        have hRSN : top.resultScope ≤ n := Nat.not_lt.mp hGN
        by_cases hSL : top.scopeLevel ≤ n
        · -- scopeLevel ≤ n: identical processing
          simp only [show min top.scopeLevel (n + 1) = top.scopeLevel from
                        Nat.min_eq_left (Nat.le_succ_of_le hSL),
                      show min top.scopeLevel n = top.scopeLevel from Nat.min_eq_left hSL,
                      Nat.sub_self, List.drop_zero]
          have hDrop : List.drop (n + 1 - top.scopeLevel) (g :: gens) =
                       List.drop (n - top.scopeLevel) gens := by
            rw [show n + 1 - top.scopeLevel = (n - top.scopeLevel) + 1 from by grind]; rfl
          rw [hDrop]
          -- Both sides now match on the same findValidLevel call
          split
          · rfl  -- some case: identical
          · exact ih acc hRF hRR  -- none case: induction hypothesis
        · -- scopeLevel > n: use findValidLevel_skip_fresh
          have hSLgt : top.scopeLevel > n := Nat.not_le.mp hSL
          simp only [show min top.scopeLevel (n + 1) = n + 1 from
                        Nat.min_eq_right (Nat.succ_le_of_lt hSLgt),
                      show min top.scopeLevel n = n from Nat.min_eq_right (Nat.le_of_lt hSLgt),
                      Nat.sub_self, List.drop_zero]
          have hTailEq : (List.drop (top.scopeLevel - (n + 1)) top.scopeGens).tail =
                         List.drop (top.scopeLevel - n) top.scopeGens := by
            rw [List.tail_drop]; congr 1; grind
          rw [findValidLevel_skip_fresh _ _ _ _ _ (hDGFresh _) hRSN, hTailEq]
          split
          · rfl
          · exact ih acc hRF hRR

/-- findValidLevel returns gens that are a suffix (via drop) of the input eGens. -/
theorem findValidLevel_gens_suffix {eGens cGens : Imp.GenList} {level resultScope lvl : Nat}
    {gs : Imp.GenList}
    (h : Imp.findValidLevel eGens cGens level resultScope = some (lvl, gs)) :
    ∃ d, gs = eGens.drop d := by
  induction eGens generalizing level cGens with
  | nil => simp [Imp.findValidLevel] at h
  | cons v vs ih =>
    cases cGens with
    | nil => simp [Imp.findValidLevel] at h
    | cons w ws =>
      rw [Imp.findValidLevel] at h
      simp only [List.head?_cons] at h
      split at h
      · simp at h; exact ⟨0, by simp [h.2]⟩
      · split at h
        · simp at h
        · obtain ⟨d, hd⟩ := ih h
          exact ⟨d + 1, by rw [hd]; rfl⟩

/-- findValidLevel returns a level ≤ the input level. -/
theorem findValidLevel_lvl_le {eGens cGens : Imp.GenList} {level resultScope lvl : Nat}
    {gs : Imp.GenList}
    (h : Imp.findValidLevel eGens cGens level resultScope = some (lvl, gs)) :
    lvl ≤ level := by
  induction eGens generalizing level cGens with
  | nil => simp [Imp.findValidLevel] at h
  | cons v vs ih =>
    cases cGens with
    | nil => simp [Imp.findValidLevel] at h
    | cons w ws =>
      rw [Imp.findValidLevel] at h
      simp only [List.head?_cons] at h
      split at h
      · simp at h; exact h.1 ▸ Nat.le_refl _
      · split at h
        · simp at h
        · exact Nat.le_trans (ih h) (Nat.sub_le level 1)

/-- findValidLevel returns a level ≥ resultScope. -/
theorem findValidLevel_rs_le {eGens cGens : Imp.GenList} {level resultScope lvl : Nat}
    {gs : Imp.GenList}
    (hLR : resultScope ≤ level)
    (h : Imp.findValidLevel eGens cGens level resultScope = some (lvl, gs)) :
    resultScope ≤ lvl := by
  induction eGens generalizing level cGens with
  | nil => simp [Imp.findValidLevel] at h
  | cons v vs ih =>
    cases cGens with
    | nil => simp [Imp.findValidLevel] at h
    | cons w ws =>
      rw [Imp.findValidLevel] at h
      simp only [List.head?_cons] at h
      split at h
      · simp at h; exact h.1 ▸ hLR
      · split at h
        · simp at h
        · exact ih (by grind) h

/-- When findValidLevel matches at the starting level, it returns the input gens unchanged. -/
theorem findValidLevel_match_at_level {eGens cGens : Imp.GenList} {level rs : Nat}
    {gs : Imp.GenList}
    (h : Imp.findValidLevel eGens cGens level rs = some (level, gs)) :
    gs = eGens := by
  cases eGens with
  | nil => simp [Imp.findValidLevel] at h
  | cons v vs =>
    cases cGens with
    | nil => simp [Imp.findValidLevel] at h
    | cons w ws =>
      rw [Imp.findValidLevel] at h
      simp only [List.head?_cons] at h
      split at h
      · simp at h; exact h.symm
      · split at h
        · simp at h
        · -- Recursive case: fvl(vs, ws, level-1, rs) returns (level, gs).
          -- But findValidLevel_lvl_le gives level ≤ level - 1, contradicting level ≥ 1.
          rename_i _ hlrs
          have := findValidLevel_lvl_le h
          omega

/-- The output gens of findValidLevel is a drop-suffix of the input eGens. -/
theorem findValidLevel_output_eq_input_drop
    {eGens cGens : Imp.GenList} {level rs lvl : Nat} {gs : Imp.GenList}
    (h : Imp.findValidLevel eGens cGens level rs = some (lvl, gs)) :
    gs = eGens.drop (level - lvl) := by
  induction eGens generalizing cGens level with
  | nil => simp [Imp.findValidLevel] at h
  | cons v vs ih =>
    cases cGens with
    | nil => simp [Imp.findValidLevel] at h
    | cons w ws =>
      rw [Imp.findValidLevel] at h
      simp only [List.head?_cons] at h
      split at h
      · simp at h; obtain ⟨hlvl, hgs⟩ := h
        subst hlvl; subst hgs; simp
      · split at h
        · simp at h
        · have hrec := ih h
          have hlvl_lt : lvl < level := by
            have := findValidLevel_lvl_le h; omega
          rw [hrec, show level - lvl = (level - 1 - lvl) + 1 from by omega,
            List.drop_succ_cons]

/-- When findValidLevel succeeds and gensSuffix holds between the original entry gens
    and the current gens list, the output gens are aligned at the correct offset. -/
theorem findValidLevel_aligned
    {eGens cGens : Imp.GenList} {level rs lvl : Nat} {gs : Imp.GenList}
    (hfvl : Imp.findValidLevel eGens cGens level rs = some (lvl, gs))
    {origGens gens : Imp.GenList} {d offset : Nat}
    (hE : eGens = origGens.drop d) (hC : cGens = gens.drop offset)
    (hGensSuffix : ∀ i < origGens.length, ∀ j < gens.length,
      origGens[i]! = gens[j]! → origGens.drop i = gens.drop j) :
    gs = gens.drop (offset + (level - lvl)) := by
  induction eGens generalizing cGens level d offset with
  | nil => simp [Imp.findValidLevel] at hfvl
  | cons v vs ih =>
    cases cGens with
    | nil => simp [Imp.findValidLevel] at hfvl
    | cons w ws =>
      rw [Imp.findValidLevel] at hfvl
      simp only [List.head?_cons] at hfvl
      split at hfvl
      · -- Match: v == w
        rename_i hvw
        simp at hfvl
        obtain ⟨hlvl, hgs⟩ := hfvl
        subst hlvl; subst hgs; simp
        -- From hE: v :: vs = origGens.drop d, so origGens[d]! = v
        have hd : d < origGens.length := by
          have : (origGens.drop d).length > 0 := by rw [← hE]; simp
          simp [List.length_drop] at this; omega
        have ho : offset < gens.length := by
          have : (gens.drop offset).length > 0 := by rw [← hC]; simp
          simp [List.length_drop] at this; omega
        have hv : origGens[d]! = v := by
          have : (origGens.drop d)[0]! = v := by rw [← hE]; rfl
          rwa [List.getElem!_eq_getElem?_getD, List.getElem?_drop, Nat.add_zero,
            ← List.getElem!_eq_getElem?_getD] at this
        have hw : gens[offset]! = w := by
          have : (gens.drop offset)[0]! = w := by rw [← hC]; rfl
          rwa [List.getElem!_eq_getElem?_getD, List.getElem?_drop, Nat.add_zero,
            ← List.getElem!_eq_getElem?_getD] at this
        have heq : origGens[d]! = gens[offset]! := by
          rw [hv, hw]; exact beq_iff_eq.mp hvw
        rw [← hGensSuffix d hd offset ho heq, ← hE]
      · split at hfvl
        · simp at hfvl
        · -- Recurse: fvl vs ws (level - 1) rs
          rename_i hne hlrs
          have hE' : vs = origGens.drop (d + 1) := by
            have h := congrArg List.tail hE
            simp only [List.tail_cons, List.tail_drop] at h
            exact h
          have hC' : ws = gens.drop (offset + 1) := by
            have h := congrArg List.tail hC
            simp only [List.tail_cons, List.tail_drop] at h
            exact h
          have hlvlle := findValidLevel_lvl_le hfvl
          have hih := ih hfvl hE' hC'
          rw [hih]; congr 1
          omega

/-- Every entry in rewind output either comes from the input unchanged or has its
    scopeGens as a suffix (via drop) of some input entry's scopeGens. -/
theorem rewind_mem {entries : List Imp.Entry} {n : Nat} {gens : Imp.GenList}
    {e : Imp.Entry} (he : e ∈ Imp.rewind entries n gens) :
    ∃ e' ∈ entries, e.result = e'.result ∧ e.resultScope = e'.resultScope ∧
      (∃ d, e.scopeGens = e'.scopeGens.drop d) := by
  simp only [Imp.rewind] at he
  suffices ∀ revEntries acc,
      (∀ e ∈ revEntries, e ∈ entries) →
      (∀ e ∈ acc, ∃ e' ∈ entries, e.result = e'.result ∧ e.resultScope = e'.resultScope ∧
        ∃ d, e.scopeGens = e'.scopeGens.drop d) →
      e ∈ Imp.rewind.go n gens revEntries acc →
      ∃ e' ∈ entries, e.result = e'.result ∧ e.resultScope = e'.resultScope ∧
        ∃ d, e.scopeGens = e'.scopeGens.drop d by
    exact this entries.reverse []
      (fun e he => List.mem_reverse.mp he) (fun _ h => nomatch h) he
  intro revEntries acc hRev hAcc
  induction revEntries generalizing acc with
  | nil =>
    simp [Imp.rewind.go]; intro hmem; exact hAcc e hmem
  | cons top rest ih =>
    intro he'
    have hTopMem := hRev top (List.mem_cons_self ..)
    have hRestMem := fun e he => hRev e (List.mem_cons_of_mem _ he)
    simp only [Imp.rewind.go] at he'
    split at he'
    · exact ih acc hRestMem hAcc he'
    · split at he'
      · -- Found valid: rest.reverse ++ [modified_top] ++ acc
        rename_i _ lvl eGens hfvl
        rcases List.mem_append.mp he' with h1 | h2
        · rcases List.mem_append.mp h1 with hr | hm
          · exact ⟨e, hRev e (List.mem_cons_of_mem _ (List.mem_reverse.mp hr)),
              rfl, rfl, ⟨0, by simp⟩⟩
          · simp only [List.mem_cons, List.mem_nil_iff, or_false] at hm
            subst hm
            obtain ⟨d, hd⟩ := findValidLevel_gens_suffix hfvl
            exact ⟨top, hTopMem, rfl, rfl,
              ⟨top.scopeLevel - min top.scopeLevel n + d, by rw [hd, List.drop_drop]⟩⟩
        · exact hAcc e h2
      · exact ih acc hRestMem hAcc he'

/-- Entries in rewind output have their gen values bounded by the input entries' gen values. -/
theorem rewind_genBound {entries : List Imp.Entry} {n : Nat} {gens : Imp.GenList}
    {bound : Nat}
    (hBound : ∀ e ∈ entries, ∀ k < e.scopeGens.length, e.scopeGens[k]! ≤ bound)
    {e : Imp.Entry} (he : e ∈ Imp.rewind entries n gens) :
    ∀ k < e.scopeGens.length, e.scopeGens[k]! ≤ bound := by
  obtain ⟨e', he', _, _, ⟨d, hd⟩⟩ := rewind_mem he
  intro k hk; rw [hd] at hk ⊢
  have hkd : k + d < e'.scopeGens.length := by
    simp [List.length_drop] at hk; grind
  have h1 := hBound e' he' (k + d) hkd
  rwa [show e'.scopeGens[k + d]! = (e'.scopeGens.drop d)[k]! from by
    simp [List.getElem!_eq_getElem?_getD, List.getElem?_drop, Nat.add_comm]] at h1

/-- Entries in rewind output have resultScope ≤ scopeLevel. -/
theorem rewind_rsLeSl {entries : List Imp.Entry} {n : Nat} {gens : Imp.GenList}
    (hRSSL : ∀ e ∈ entries, e.resultScope ≤ e.scopeLevel)
    {e : Imp.Entry} (he : e ∈ Imp.rewind entries n gens) :
    e.resultScope ≤ e.scopeLevel := by
  simp only [Imp.rewind] at he
  suffices ∀ revEntries acc,
      (∀ e ∈ revEntries, e.resultScope ≤ e.scopeLevel) →
      (∀ e ∈ acc, e.resultScope ≤ e.scopeLevel) →
      e ∈ Imp.rewind.go n gens revEntries acc → e.resultScope ≤ e.scopeLevel by
    exact this entries.reverse [] (fun e he => hRSSL e (List.mem_reverse.mp he))
      (fun _ h => nomatch h) he
  intro revEntries acc hRev hAcc
  induction revEntries generalizing acc with
  | nil => simp [Imp.rewind.go]; intro hmem; exact hAcc e hmem
  | cons top rest ih =>
    intro he'
    have hRestRev := fun e he => hRev e (List.mem_cons_of_mem _ he)
    simp only [Imp.rewind.go] at he'
    split at he'
    · exact ih acc hRestRev hAcc he'
    · split at he'
      · rename_i _ lvl eGens hfvl
        rcases List.mem_append.mp he' with h1 | h2
        · rcases List.mem_append.mp h1 with hr | hm
          · exact hRev _ (List.mem_cons_of_mem _ (List.mem_reverse.mp hr))
          · simp only [List.mem_cons, List.mem_nil_iff, or_false] at hm
            subst hm; simp only
            exact findValidLevel_rs_le (by simp [Nat.min_def]; split <;> grind) hfvl
        · exact hAcc e h2
      · exact ih acc hRestRev hAcc he'

/-- Entries are well-ordered: each entry's scopeLevel < every later entry's resultScope.
    This holds for entries produced by Imp.insert (which filters scopeLevel < rs). -/
def Imp.EntryOrdered (entries : List Imp.Entry) : Prop :=
  entries.Pairwise (fun a b => a.scopeLevel < b.resultScope)

/-- Rewind preserves entry ordering. -/
theorem rewind_entryOrdered {entries : List Imp.Entry} {n : Nat} {gens : Imp.GenList}
    (hOrd : Imp.EntryOrdered entries) :
    Imp.EntryOrdered (Imp.rewind entries n gens) := by
  simp only [Imp.rewind, Imp.EntryOrdered]
  suffices ∀ revEntries acc,
      (revEntries.reverse ++ acc).Pairwise (fun a b => a.scopeLevel < b.resultScope) →
      acc.Pairwise (fun a b => a.scopeLevel < b.resultScope) →
      (Imp.rewind.go n gens revEntries acc).Pairwise
        (fun a b => a.scopeLevel < b.resultScope) by
    exact this entries.reverse []
      (by simpa using hOrd) List.Pairwise.nil
  intro revEntries acc hAll hAcc
  induction revEntries generalizing acc with
  | nil => simpa [Imp.rewind.go]
  | cons top rest ih =>
    simp only [Imp.rewind.go]
    -- Extract ordering facts from hAll
    rw [List.reverse_cons, List.append_assoc, List.singleton_append] at hAll
    have hP := List.pairwise_append.mp hAll
    have hOrdPre := hP.1
    have hOrdTA := hP.2.1
    have hCross := hP.2.2
    have hTopAcc := List.pairwise_cons.mp hOrdTA
    split
    · -- top.resultScope > n: skip top
      apply ih acc
      · rw [List.pairwise_append]
        exact ⟨hOrdPre, hTopAcc.2,
          fun a ha b hb => hCross a ha b (List.mem_cons_of_mem _ hb)⟩
      · exact hAcc
    · -- top.resultScope ≤ n
      split
      · -- findValidLevel returns some (lvl, eGens)
        rename_i _ lvl eGens hfvl
        -- Result is rest.reverse ++ [modified_top] ++ acc
        -- modified_top has same resultScope, scopeLevel ≤ top.scopeLevel
        have hLvlLe : lvl ≤ min top.scopeLevel n :=
          findValidLevel_lvl_le hfvl
        have hLvlLeSL : lvl ≤ top.scopeLevel :=
          Nat.le_trans hLvlLe (Nat.min_le_left ..)
        rw [List.append_assoc, List.pairwise_append]
        refine ⟨hOrdPre, ?_, ?_⟩
        · -- Pairwise for [modified_top] ++ acc
          rw [List.singleton_append, List.pairwise_cons]
          exact ⟨fun b hb => Nat.lt_of_le_of_lt hLvlLeSL (hTopAcc.1 b hb), hTopAcc.2⟩
        · -- ∀ a ∈ rest.reverse, ∀ b ∈ [modified_top] ++ acc, a.sl < b.rs
          intro a ha b hb
          rw [List.singleton_append, List.mem_cons] at hb
          cases hb with
          | inl heq =>
            subst heq; simp only
            exact hCross a ha top (List.mem_cons_self ..)
          | inr hb' => exact hCross a ha b (List.mem_cons_of_mem _ hb')
      · -- findValidLevel returns none: skip top
        apply ih acc
        · rw [List.pairwise_append]
          exact ⟨hOrdPre, hTopAcc.2,
            fun a ha b hb => hCross a ha b (List.mem_cons_of_mem _ hb)⟩
        · exact hAcc


/-- When all entries in revEntries are trivially valid at the given scope
    (findValidLevel matches immediately at their scopeLevel), rewind.go is identity. -/
theorem rewind_go_all_valid (revEntries acc : List Imp.Entry)
    (scope : Nat) (gens : Imp.GenList)
    (hRS : ∀ e ∈ revEntries, e.resultScope ≤ scope)
    (hSL : ∀ e ∈ revEntries, e.scopeLevel ≤ scope)
    (hRSSL : ∀ e ∈ revEntries, e.resultScope ≤ e.scopeLevel)
    (hValid : ∀ e ∈ revEntries,
      Imp.findValidLevel e.scopeGens (gens.drop (scope - e.scopeLevel))
        e.scopeLevel e.resultScope = some (e.scopeLevel, e.scopeGens)) :
    Imp.rewind.go scope gens revEntries acc = revEntries.reverse ++ acc := by
  induction revEntries generalizing acc with
  | nil => simp [Imp.rewind.go]
  | cons top rest ih =>
    simp only [Imp.rewind.go]
    have hTopRS := hRS top (List.mem_cons_self ..)
    have hTopSL := hSL top (List.mem_cons_self ..)
    have hTopRSSL := hRSSL top (List.mem_cons_self ..)
    have hTopValid := hValid top (List.mem_cons_self ..)
    simp only [show ¬(top.resultScope > scope) from Nat.not_lt.mpr hTopRS, ↓reduceIte]
    simp only [show min top.scopeLevel scope = top.scopeLevel from Nat.min_eq_left hTopSL,
               Nat.sub_self, List.drop_zero] at hTopValid ⊢
    rw [hTopValid]
    simp [List.reverse_cons]

/-! ### Simulation invariant -/

/-- The simulation invariant relates Spec and Imp states. -/
structure SimInv (spec : Spec.State) (imp : Imp.State) : Prop where
  scopeEq : spec.scopeN = imp.scopeN
  entriesEq : spec.entries =
    (Imp.rewind imp.entries imp.scopeN imp.currentGens).map Imp.Entry.toSpec
  gensLen : imp.currentGens.length = imp.scopeN + 1
  -- All generation values in entries and currentGens are ≤ genCounter
  genBound : ∀ e ∈ imp.entries, ∀ k < e.scopeGens.length, e.scopeGens[k]! ≤ imp.genCounter
  curGenBound : ∀ k < imp.currentGens.length, imp.currentGens[k]! ≤ imp.genCounter
  -- Entries have resultScope ≤ scopeLevel (from insert semantics)
  rsLeSl : ∀ e ∈ imp.entries, e.resultScope ≤ e.scopeLevel
  -- Entries are well-ordered: each entry's scopeLevel < next entry's resultScope
  entryOrdered : Imp.EntryOrdered imp.entries
  -- Current gens are all distinct (each push creates a unique gen value)
  gensNodup : imp.currentGens.Nodup
  -- Gen values act as unique scope identifiers: matching gens imply matching suffixes
  gensSuffix : ∀ e ∈ imp.entries, ∀ i < e.scopeGens.length,
    ∀ j < imp.currentGens.length, e.scopeGens[i]! = imp.currentGens[j]! →
    e.scopeGens.drop i = imp.currentGens.drop j

/-- SimInv holds for empty states. -/
theorem simInv_empty : SimInv Spec.empty Imp.empty where
  scopeEq := by simp [Spec.empty, Imp.empty]
  entriesEq := by simp [Spec.empty, Imp.empty, Imp.rewind, Imp.rewind.go]
  gensLen := by simp [Imp.empty]
  genBound := by simp [Imp.empty]
  curGenBound := by simp [Imp.empty]
  rsLeSl := by simp [Imp.empty]
  entryOrdered := List.Pairwise.nil
  gensNodup := by simp [Imp.empty, List.Nodup]
  gensSuffix := by simp [Imp.empty]

/-! ### Observational equivalence from SimInv -/

/-- Lookup equivalence given SimInv. -/
theorem lookup_equiv (h : SimInv spec imp) :
    Spec.lookup spec = Imp.lookup imp := by
  simp only [Spec.lookup, Imp.lookup]
  rw [h.entriesEq, h.scopeEq]
  cases hrw : (Imp.rewind imp.entries imp.scopeN imp.currentGens).getLast? with
  | none => simp [List.getLast?_map, hrw]
  | some e => simp [List.getLast?_map, hrw, Imp.Entry.toSpec]

/-- Insert-sharing equivalence given SimInv. -/
theorem insert_sharing_equiv (h : SimInv spec imp) :
    (Spec.insert spec v rs).2 = (Imp.insert imp v rs).2 := by
  simp only [Spec.insert, Imp.insert]
  rw [h.entriesEq]
  cases hrw : (Imp.rewind imp.entries imp.scopeN imp.currentGens).getLast? with
  | none => simp [List.getLast?_map, hrw]
  | some e => simp [List.getLast?_map, hrw, Imp.Entry.toSpec]

/-! ### SimInv preservation -/

/-- Push preserves SimInv. -/
theorem push_simInv (h : SimInv spec imp) :
    SimInv (Spec.push spec) (Imp.push imp) where
  scopeEq := by simp [Spec.push, Imp.push, h.scopeEq]
  entriesEq := by
    simp only [Spec.push, Imp.push]
    have hFresh : ∀ e ∈ imp.entries,
        ∀ k < e.scopeGens.length, e.scopeGens[k]! < imp.genCounter + 1 := by
      intro e he k hk
      exact Nat.lt_succ_of_le (h.genBound e he k hk)
    rw [rewind_push_equiv imp.entries imp.scopeN
      (imp.genCounter + 1) imp.currentGens hFresh h.rsLeSl]
    exact h.entriesEq
  gensLen := by simp [Imp.push, h.gensLen]
  genBound := by
    intro e he k hk
    simp only [Imp.push] at he
    exact Nat.le_succ_of_le (h.genBound e he k hk)
  curGenBound := by
    intro k hk
    simp only [Imp.push] at hk ⊢
    cases k with
    | zero => simp
    | succ k =>
      simp only [List.getElem!_cons_succ]
      exact Nat.le_succ_of_le (h.curGenBound k (by
        simp only [List.length_cons] at hk; exact Nat.lt_of_succ_lt_succ hk))
  rsLeSl := by
    intro e he; simp only [Imp.push] at he; exact h.rsLeSl e he
  entryOrdered := by simp only [Imp.push]; exact h.entryOrdered
  gensNodup := by
    simp only [Imp.push, List.nodup_cons]
    refine ⟨?_, h.gensNodup⟩
    intro hmem
    have ⟨k, hk, heq⟩ := List.mem_iff_getElem.mp hmem
    have hle := h.curGenBound k hk
    simp only [List.getElem!_eq_getElem?_getD, List.getElem?_eq_getElem hk,
      Option.getD_some] at hle
    -- hle : currentGens[k] ≤ genCounter, heq : currentGens[k] = genCounter + 1
    grind
  gensSuffix := by
    intro e he i hi j hj heq
    simp only [Imp.push] at he hj heq ⊢
    cases j with
    | zero =>
      -- gens[0] is the fresh gen (genCounter + 1), can't match any entry gen
      simp only [List.getElem!_cons_zero] at heq
      exact absurd heq.symm (Nat.ne_of_gt (Nat.lt_succ_of_le (h.genBound e he i hi)))
    | succ j =>
      simp only [List.getElem!_cons_succ] at heq
      exact h.gensSuffix e he i hi j (by
        simp only [List.length_cons] at hj; exact Nat.lt_of_succ_lt_succ hj) heq

/-- Key lemma: an entry with scopeGens aligned to gens (via suffix property) is
    trivially valid at its scopeLevel. -/
theorem fvl_self_match (e : Imp.Entry) (gens : Imp.GenList) (scope : Nat)
    (_hSL : e.scopeLevel ≤ scope)
    (hSuffix : e.scopeGens = gens.drop (scope - e.scopeLevel))
    (hLen : e.scopeGens.length > 0) :
    Imp.findValidLevel e.scopeGens (gens.drop (scope - e.scopeLevel))
      e.scopeLevel e.resultScope = some (e.scopeLevel, e.scopeGens) := by
  obtain ⟨g, gs, hgs⟩ := List.exists_cons_of_length_pos hLen
  have hgs' : gens.drop (scope - e.scopeLevel) = g :: gs := hSuffix ▸ hgs
  rw [hgs, hgs', Imp.findValidLevel]
  simp

/-- For entries with sl ≤ n-1, the fvl calls at scope n and scope n-1 are identical
    because gens.drop(n - sl) = gens.tail.drop(n-1 - sl). -/
theorem fvl_drop_shift (gens : Imp.GenList) (n sl : Nat) (hSL : sl ≤ n - 1) (hn : n > 0) :
    gens.drop (n - sl) = gens.tail.drop (n - 1 - sl) := by
  have h1 : n - sl = (n - 1 - sl) + 1 := by grind
  rw [h1, List.drop_tail]

/-- When fvl succeeds at scope n with level = n (first gens match),
    and dg = gens (from gensSuffix), fvl at scope n-1 matches at n-1. -/
theorem fvl_self_tail (gens : Imp.GenList) (n rs : Nat) (hn : n > 0)
    (_hRS : rs ≤ n - 1) (hGensLen : gens.length = n + 1) :
    Imp.findValidLevel gens.tail gens.tail (n - 1) rs = some (n - 1, gens.tail) := by
  have hTailLen : gens.tail.length = n := by simp [List.length_tail, hGensLen]
  obtain ⟨g, gs, hgs⟩ := List.exists_cons_of_length_pos (by rw [hTailLen]; exact hn : gens.tail.length > 0)
  rw [hgs, Imp.findValidLevel]; simp

/-- When fvl at scope n succeeds for an entry, fvl at scope n-1 produces
    the degraded version. Takes fvl success as a premise. -/
theorem fvl_pop_entry (e : Imp.Entry) (n : Nat) (gens : Imp.GenList)
    (hn : n > 0) (hRS : e.resultScope ≤ n - 1)
    (hGensSuffix : ∀ i < e.scopeGens.length, ∀ j < gens.length,
      e.scopeGens[i]! = gens[j]! → e.scopeGens.drop i = gens.drop j)
    (hGensLen : gens.length = n + 1)
    (lvl : Nat) (eGens : Imp.GenList)
    (hfvl : Imp.findValidLevel (e.scopeGens.drop (e.scopeLevel - min e.scopeLevel n))
        (gens.drop (n - min e.scopeLevel n)) (min e.scopeLevel n) e.resultScope =
        some (lvl, eGens)) :
    Imp.findValidLevel (e.scopeGens.drop (e.scopeLevel - min e.scopeLevel (n - 1)))
        (gens.tail.drop ((n - 1) - min e.scopeLevel (n - 1)))
        (min e.scopeLevel (n - 1)) e.resultScope =
        some (min lvl (n - 1),
              if lvl ≤ n - 1 then eGens
              else eGens.tail) := by
  by_cases hSL : e.scopeLevel ≤ n - 1
  · -- Case: sl ≤ n-1. Both fvl calls are identical.
    have hSLn : e.scopeLevel ≤ n := Nat.le_trans hSL (Nat.pred_le n)
    rw [Nat.min_eq_left hSLn, Nat.sub_self, List.drop_zero] at hfvl
    rw [Nat.min_eq_left hSL, Nat.sub_self, List.drop_zero,
        ← fvl_drop_shift gens n e.scopeLevel hSL hn]
    have hlvl_le : lvl ≤ n - 1 := Nat.le_trans (findValidLevel_lvl_le hfvl) hSL
    simp [Nat.min_eq_left hlvl_le, hlvl_le, hfvl]
  · -- Case: sl ≥ n. Unfold one step of fvl at scope n.
    have hSLge : e.scopeLevel ≥ n := by omega
    rw [Nat.min_eq_right hSLge, show n - n = 0 from Nat.sub_self n,
        List.drop_zero] at hfvl
    rw [Nat.min_eq_right (by omega : e.scopeLevel ≥ n - 1),
        show (n - 1) - (n - 1) = 0 from Nat.sub_self (n - 1), List.drop_zero]
    -- Relate the two degraded gens lists
    have hdrop_shift : e.scopeGens.drop (e.scopeLevel - (n - 1)) =
        (e.scopeGens.drop (e.scopeLevel - n)).tail := by
      rw [List.tail_drop]; congr 1; omega
    rw [hdrop_shift]
    -- dg := e.scopeGens.drop(sl - n) must be non-empty (fvl succeeded on it)
    obtain ⟨v, vs, hvvs⟩ : ∃ v vs, e.scopeGens.drop (e.scopeLevel - n) = v :: vs := by
      cases hdg : e.scopeGens.drop (e.scopeLevel - n) with
      | nil => simp [hdg, Imp.findValidLevel] at hfvl
      | cons v vs => exact ⟨v, vs, rfl⟩
    -- gens must be non-empty
    obtain ⟨w, ws, hwws⟩ : ∃ w ws, gens = w :: ws := by
      cases gens with
      | nil => simp at hGensLen
      | cons w ws => exact ⟨w, ws, rfl⟩
    rw [hvvs, hwws] at hfvl ⊢
    simp only [List.tail_cons]
    -- Unfold fvl at scope n: fvl(v :: vs, w :: ws, n, rs)
    rw [Imp.findValidLevel] at hfvl
    simp only [List.head?_cons] at hfvl
    split at hfvl
    · -- Sub-case: v == w (match at level n)
      rename_i heq
      simp at hfvl
      obtain ⟨hlvl_eq, hout_eq⟩ := hfvl
      subst hlvl_eq; subst hout_eq
      -- lvl = n, eGens = v :: vs
      simp only [show ¬(n ≤ n - 1) from by omega, ↓reduceIte,
                  show min n (n - 1) = n - 1 from Nat.min_eq_right (Nat.pred_le n)]
      -- By gensSuffix: e.scopeGens.drop(sl-n) = gens, so v :: vs = w :: ws
      have hveq : v = w := by simpa using heq
      have hi : e.scopeLevel - n < e.scopeGens.length := by
        have : (e.scopeGens.drop (e.scopeLevel - n)).length > 0 := by rw [hvvs]; simp
        simp [List.length_drop] at this; omega
      have hval : e.scopeGens[e.scopeLevel - n]! = gens[0]! := by
        have h1 : (e.scopeGens.drop (e.scopeLevel - n))[0]! = v := by
          rw [hvvs]; simp
        rw [show (e.scopeGens.drop (e.scopeLevel - n))[0]! = e.scopeGens[e.scopeLevel - n]! from by
          simp [List.getElem!_eq_getElem?_getD, List.getElem?_drop]] at h1
        have h2 : gens[0]! = w := by rw [hwws]; simp
        rw [h1, h2, hveq]
      have hdg_gens : e.scopeGens.drop (e.scopeLevel - n) = gens :=
        hGensSuffix (e.scopeLevel - n) hi 0 (by rw [hGensLen]; omega) hval
      -- Goal: fvl vs ws (n-1) rs = some(n-1, (v :: vs).tail)
      -- Since drop(sl-n) = gens, and drop(sl-n) = v :: vs, and gens = w :: ws:
      -- v :: vs = w :: ws, so vs = ws and v = w
      -- Also (v :: vs).tail = vs = ws = gens.tail
      have hvs_ws : vs = ws := by
        have h := hdg_gens; rw [hvvs, hwws] at h; exact List.cons.inj h |>.2
      simp only [List.tail_cons, hvs_ws]
      -- Goal: fvl ws ws (n-1) rs = some(n-1, ws)
      have hws_eq : ws = gens.tail := by rw [hwws]; rfl
      rw [hws_eq]
      exact fvl_self_tail gens n e.resultScope hn hRS hGensLen
    · -- Sub-case: v ≠ w
      rename_i hne
      split at hfvl
      · -- n ≤ rs: impossible since rs ≤ n-1
        omega
      · -- n > rs: fvl recurses to fvl(vs, ws, n-1, rs) = some(lvl, eGens)
        -- This IS the scope n-1 call, and lvl ≤ n-1
        simp only [List.tail_cons] at hfvl
        have hlvl_le : lvl ≤ n - 1 := findValidLevel_lvl_le hfvl
        simp [Nat.min_eq_left hlvl_le, hlvl_le, hfvl]

/-- If fvl at scope n returns none, fvl at scope n-1 also returns none. -/
theorem fvl_pop_none (e : Imp.Entry) (n : Nat) (gens : Imp.GenList)
    (hn : n > 0) (hRS : e.resultScope ≤ n - 1)
    (hfvl : Imp.findValidLevel (e.scopeGens.drop (e.scopeLevel - min e.scopeLevel n))
        (gens.drop (n - min e.scopeLevel n)) (min e.scopeLevel n) e.resultScope = none) :
    Imp.findValidLevel (e.scopeGens.drop (e.scopeLevel - min e.scopeLevel (n - 1)))
        (gens.tail.drop ((n - 1) - min e.scopeLevel (n - 1)))
        (min e.scopeLevel (n - 1)) e.resultScope = none := by
  by_cases hSL : e.scopeLevel ≤ n - 1
  · -- sl ≤ n-1: both calls are identical
    have hSLn : e.scopeLevel ≤ n := Nat.le_trans hSL (Nat.pred_le n)
    rw [Nat.min_eq_left hSLn, Nat.sub_self, List.drop_zero] at hfvl
    rw [Nat.min_eq_left hSL, Nat.sub_self, List.drop_zero,
        ← fvl_drop_shift gens n e.scopeLevel hSL hn]
    exact hfvl
  · -- sl ≥ n: unfold one step
    have hSLge : e.scopeLevel ≥ n := by omega
    rw [Nat.min_eq_right hSLge, show n - n = 0 from Nat.sub_self n,
        List.drop_zero] at hfvl
    rw [Nat.min_eq_right (by omega : e.scopeLevel ≥ n - 1),
        show (n - 1) - (n - 1) = 0 from Nat.sub_self (n - 1), List.drop_zero]
    have hdrop_shift : e.scopeGens.drop (e.scopeLevel - (n - 1)) =
        (e.scopeGens.drop (e.scopeLevel - n)).tail := by
      rw [List.tail_drop]; congr 1; omega
    rw [hdrop_shift]
    cases hdg : e.scopeGens.drop (e.scopeLevel - n) with
    | nil => simp [Imp.findValidLevel]
    | cons v vs =>
      simp only [List.tail_cons]
      -- Goal: fvl(vs, gens.tail, n-1, rs) = none
      -- hfvl: fvl(dg, gens, n, rs) = none where dg = v :: vs
      rw [hdg] at hfvl
      cases hg : gens with
      | nil =>
        -- fvl with empty cGens always returns none
        simp only [List.tail_nil]
        cases vs with
        | nil => rw [Imp.findValidLevel]; rfl
        | cons a as => rw [Imp.findValidLevel]; simp
      | cons w ws =>
        simp only [List.tail_cons]
        rw [hg] at hfvl; rw [Imp.findValidLevel] at hfvl
        simp only [List.head?_cons] at hfvl
        split at hfvl
        · -- v == w: fvl returns some, contradicts hfvl = none
          exact absurd hfvl (by simp)
        · split at hfvl
          · -- n ≤ rs: impossible since rs ≤ n-1 and n > 0
            omega
          · -- Recursive: fvl(vs, ws, n-1, rs) = none
            simp only [List.tail_cons] at hfvl
            exact hfvl

/-- When all entries have rs ≤ k and sl ≤ k, filter+degrade is the identity. -/
private theorem filter_degrade_spec_id (l : List Spec.Entry) (k : Nat)
    (hRS : ∀ e ∈ l, e.resultScope ≤ k)
    (hSL : ∀ e ∈ l, e.scopeLevel ≤ k) :
    List.map (fun e => if e.scopeLevel > k then
        { result := e.result, scopeLevel := k, resultScope := e.resultScope } else e)
      (l.filter (fun e => decide (e.resultScope ≤ k))) = l := by
  induction l with
  | nil => simp
  | cons hd tl ih =>
    simp only [List.filter_cons,
      show decide (hd.resultScope ≤ k) = true from decide_eq_true_eq.mpr (hRS hd (List.mem_cons_self ..)),
      ↓reduceIte, List.map_cons,
      show ¬(hd.scopeLevel > k) from Nat.not_lt.mpr (hSL hd (List.mem_cons_self ..)),
      ↓reduceIte]
    exact congrArg (hd :: ·) (ih (fun e he => hRS e (List.mem_cons_of_mem _ he))
                                  (fun e he => hSL e (List.mem_cons_of_mem _ he)))

/-- Entries' gen lists are mutually consistent: earlier entries' gens are drop-suffixes
    of later entries' gens. This is preserved trivially by push/pop (which don't touch entries)
    and established by insert (where all surviving entries are aligned with currentGens). -/
private def GensConsistent (entries : List Imp.Entry) : Prop :=
  entries.Pairwise (fun a b => a.scopeGens = b.scopeGens.drop (b.scopeLevel - a.scopeLevel))

/-- Pop rewind equivalence: rewind at (n-1, gens.tail) relates to
    filter+degrade of rewind at (n, gens). -/
theorem rewind_pop_equiv (entries : List Imp.Entry) (n : Nat) (gens : Imp.GenList)
    (hn : n > 0)
    (hGensLen : gens.length = n + 1)
    (hRSSL : ∀ e ∈ entries, e.resultScope ≤ e.scopeLevel)
    (hOrd : Imp.EntryOrdered entries)
    (hGensSuffix : ∀ e ∈ entries, ∀ i < e.scopeGens.length,
      ∀ j < gens.length, e.scopeGens[i]! = gens[j]! →
      e.scopeGens.drop i = gens.drop j)
    (hGC : GensConsistent entries) :
    (Imp.rewind entries (n - 1) gens.tail).map Imp.Entry.toSpec =
    List.map
      (fun e => if e.scopeLevel > n - 1 then
        { e with scopeLevel := n - 1 : Spec.Entry } else e)
      ((Imp.rewind entries n gens).map Imp.Entry.toSpec |>.filter
        fun e => decide (e.resultScope ≤ n - 1)) := by
  simp only [Imp.rewind]
  -- GensConsistent is a Pairwise property on entries. In the suffices, we track
  -- GensConsistent for revEntries.reverse (sub-list of entries) plus EntryOrdered.
  -- In the early-stop case, we derive alignment for rest entries from GensConsistent
  -- (inter-entry gens relationship) + gensSuffix (entry-to-currentGens relationship).
  suffices ∀ revEntries acc1 acc2,
      (∀ e ∈ revEntries, e ∈ entries) →
      (∀ e ∈ revEntries, e.resultScope ≤ e.scopeLevel) →
      (revEntries.reverse ++ acc1).Pairwise (fun a b => a.scopeLevel < b.resultScope) →
      (∀ e ∈ revEntries, ∀ i < e.scopeGens.length,
        ∀ j < gens.length, e.scopeGens[i]! = gens[j]! →
        e.scopeGens.drop i = gens.drop j) →
      revEntries.reverse.Pairwise
        (fun a b => a.scopeGens = b.scopeGens.drop (b.scopeLevel - a.scopeLevel)) →
      acc2.map Imp.Entry.toSpec =
        List.map (fun e => if e.scopeLevel > n - 1 then
          { e with scopeLevel := n - 1 : Spec.Entry } else e)
        (acc1.map Imp.Entry.toSpec |>.filter fun e => decide (e.resultScope ≤ n - 1)) →
      (Imp.rewind.go (n - 1) gens.tail revEntries acc2).map Imp.Entry.toSpec =
        List.map (fun e => if e.scopeLevel > n - 1 then
          { e with scopeLevel := n - 1 : Spec.Entry } else e)
        ((Imp.rewind.go n gens revEntries acc1).map Imp.Entry.toSpec |>.filter
          fun e => decide (e.resultScope ≤ n - 1)) by
    exact this entries.reverse [] []
      (fun e he => List.mem_reverse.mp he)
      (fun e he => hRSSL e (List.mem_reverse.mp he))
      (by simpa using hOrd)
      (fun e he => hGensSuffix e (List.mem_reverse.mp he))
      (by simpa using hGC)
      (by simp)
  intro revEntries acc1 acc2 hMem hRSSLrev hOrdRev hGSrev hGCrev hAccEq
  induction revEntries generalizing acc1 acc2 with
  | nil => simp [Imp.rewind.go]; exact hAccEq
  | cons top rest ih =>
    have hTopMem := hMem top (List.mem_cons_self ..)
    have hTopRSSL := hRSSLrev top (List.mem_cons_self ..)
    have hRestMem := fun e he => hMem e (List.mem_cons_of_mem _ he)
    have hRestRSSL := fun e he => hRSSLrev e (List.mem_cons_of_mem _ he)
    have hRestGS := fun e he => hGSrev e (List.mem_cons_of_mem _ he)
    have hTopGS := hGSrev top (List.mem_cons_self ..)
    -- GensConsistent for rest.reverse: sub-list of (top :: rest).reverse = rest.reverse ++ [top]
    rw [List.reverse_cons] at hGCrev
    have hRestGC := (List.pairwise_append.mp hGCrev).1
    -- Cross from GensConsistent: ∀ e ∈ rest, e.gens = top.gens.drop(top.sl - e.sl)
    have hGCcross := (List.pairwise_append.mp hGCrev).2.2
    -- Extract ordering
    rw [List.reverse_cons, List.append_assoc, List.singleton_append] at hOrdRev
    have hP := List.pairwise_append.mp hOrdRev
    have hOrdPre := hP.1
    have hOrdTA := hP.2.1
    have hCross := hP.2.2
    have hTopAcc := List.pairwise_cons.mp hOrdTA
    simp only [Imp.rewind.go]
    -- Case 1: top.resultScope > n
    by_cases hGN : top.resultScope > n
    · -- Both scopes skip (rs > n implies rs > n-1)
      have hGN1 : top.resultScope > n - 1 := by omega
      simp only [hGN, hGN1, ↓reduceIte]
      exact ih acc1 acc2 hRestMem hRestRSSL
        (by rw [List.pairwise_append]; exact ⟨hOrdPre, hTopAcc.2,
          fun a ha b hb => hCross a ha b (List.mem_cons_of_mem _ hb)⟩)
        hRestGS hRestGC hAccEq
    · simp only [show ¬(top.resultScope > n) from hGN, ↓reduceIte]
      -- Case 2: top.resultScope = n (rs > n-1 but rs ≤ n)
      by_cases hGN1 : top.resultScope > n - 1
      · -- Scope n processes, scope n-1 skips
        simp only [hGN1, ↓reduceIte]
        -- At scope n, fvl is called
        split
        · -- fvl at scope n succeeds: result has rs = n which gets filtered
          rename_i lvl eGens hfvl_n
          -- The entry has rs > n-1, so it gets filtered out
          have hRSn : top.resultScope = n := by omega
          -- rest.reverse ++ [modified_top] ++ acc1
          -- After map toSpec and filter (rs ≤ n-1): modified_top has rs = n, filtered out
          -- Prefix entries (rest.reverse) have scopeLevel < top.resultScope = n
          -- so scopeLevel ≤ n-1, so their toSpec entries don't get capped
          -- modified_top has rs = n, so it gets filtered out on RHS.
          -- All entries in rest have sl < top.rs = n (by EntryOrdered/hCross),
          -- and rs ≤ sl < n (by hRSSL). Use IH.
          -- Key: need acc equivalence for the new accumulators.
          -- New acc1' = modified_top :: acc1.
          -- modified_top.toSpec.resultScope = n > n-1, so filtered out.
          -- modified_top entry
          have hNewAccEq : acc2.map Imp.Entry.toSpec =
              List.map (fun e => if e.scopeLevel > n - 1 then
                { e with scopeLevel := n - 1 : Spec.Entry } else e)
              (({ result := top.result, scopeLevel := lvl,
                  scopeGens := eGens, resultScope := top.resultScope } ::
                acc1).map Imp.Entry.toSpec |>.filter
                fun e => decide (e.resultScope ≤ n - 1)) := by
            simp only [List.map_cons, List.filter_cons, Imp.Entry.toSpec, hRSn]
            have : ¬(n ≤ n - 1) := by omega
            simp only [this, decide_false]
            exact hAccEq
          -- Ordering: entries in rest have sl < top.rs = n.
          -- mt.resultScope = n, and lvl ≤ n (from fvl_lvl_le). So sl_rest < n = mt.rs.
          -- Ordering: entries in rest have sl < top.rs = n = mt.rs
          have hOrdNew : (rest.reverse ++
              { result := top.result, scopeLevel := lvl,
                scopeGens := eGens, resultScope := top.resultScope } :: acc1).Pairwise
              (fun a b => a.scopeLevel < b.resultScope) := by
            rw [List.pairwise_append]; refine ⟨hOrdPre, ?_, ?_⟩
            · have hlvl : lvl ≤ top.scopeLevel :=
                Nat.le_trans (findValidLevel_lvl_le hfvl_n) (Nat.min_le_left ..)
              exact List.pairwise_cons.mpr
                ⟨fun a ha => by
                  exact Nat.lt_of_le_of_lt hlvl (hTopAcc.1 a ha),
                 hTopAcc.2⟩
            · intro a ha b hb
              rcases List.mem_cons.mp hb with rfl | hb'
              · simp only; exact hRSn ▸ hCross a ha top (List.mem_cons_self ..)
              · exact hCross a ha b (List.mem_cons_of_mem _ hb')
          -- The IH gives rewind.go(n, gens, rest, mt::acc1) on the RHS.
          -- The goal has rest.reverse ++ [mt] ++ acc1 (early stop result).
          -- Show they're equal via rewind_go_all_valid.
          -- Derive alignment for rest entries from GensConsistent + top's fvl + gensSuffix
          -- Step 1: top.gens.drop(top.sl - n) = gens
          have hTopSLgeN : n ≤ top.scopeLevel := by
            have := hTopRSSL; rw [hRSn] at this; exact this
          have hMinEq : min top.scopeLevel n = n := Nat.min_eq_right hTopSLgeN
          -- fvl at (n, n) can only match at level n
          have hLvlN : lvl = n := by
            have h1 := findValidLevel_lvl_le hfvl_n
            have h2 := findValidLevel_rs_le
              (show top.resultScope ≤ min top.scopeLevel n by
                simp [hRSn, hMinEq]) hfvl_n
            rw [hMinEq] at h1; rw [hRSn] at h2; omega
          -- top.gens.drop(top.sl - n) = gens
          -- eGens = top.gens.drop(top.sl - n) since fvl matched at level n
          have hfvl_n' : Imp.findValidLevel
              (top.scopeGens.drop (top.scopeLevel - n))
              (gens.drop (n - n)) n top.resultScope = some (n, eGens) := by
            rwa [hMinEq, hLvlN] at hfvl_n
          have hEGens : eGens = top.scopeGens.drop (top.scopeLevel - n) :=
            findValidLevel_match_at_level hfvl_n'
          -- eGens = gens (from findValidLevel_aligned)
          have hEGensGens : eGens = gens := by
            have := findValidLevel_aligned hfvl_n rfl rfl hTopGS
            rw [hMinEq, Nat.sub_self, hLvlN, Nat.sub_self, Nat.zero_add, List.drop_zero] at this
            exact this
          -- So top.gens.drop(top.sl - n) = gens
          have hTopAligned : top.scopeGens.drop (top.scopeLevel - n) = gens := by
            rw [← hEGensGens, hEGens]
          -- Step 2: derive alignment for each rest entry
          have hRestValid : ∀ e ∈ rest,
              Imp.findValidLevel e.scopeGens (gens.drop (n - e.scopeLevel))
                e.scopeLevel e.resultScope = some (e.scopeLevel, e.scopeGens) := by
            intro e he
            have heSL : e.scopeLevel ≤ n := by
              have := hCross e (List.mem_reverse.mpr he) top (List.mem_cons_self ..)
              have := hRSSLrev e (List.mem_cons_of_mem _ he)
              omega
            -- From GensConsistent: e.gens = top.gens.drop(top.sl - e.sl)
            have heGC := hGCcross e (List.mem_reverse.mpr he) top (List.mem_cons_self ..)
            -- Derive e.gens = gens.drop(n - e.sl) using List.drop_drop
            have heAligned : e.scopeGens = gens.drop (n - e.scopeLevel) := by
              rw [heGC, show top.scopeLevel - e.scopeLevel =
                (top.scopeLevel - n) + (n - e.scopeLevel) from by omega,
                ← List.drop_drop, hTopAligned]
            exact fvl_self_match e gens n heSL heAligned
              (by rw [heAligned, List.length_drop, hGensLen]; omega)
          -- Use IH + rewind_go_all_valid to close the goal
          have hRestRS : ∀ e ∈ rest, e.resultScope ≤ n :=
            fun e he => by
              have := hCross e (List.mem_reverse.mpr he) top (List.mem_cons_self ..)
              have := hRestRSSL e he; omega
          have hRestSL : ∀ e ∈ rest, e.scopeLevel ≤ n :=
            fun e he => by
              have := hCross e (List.mem_reverse.mpr he) top (List.mem_cons_self ..)
              omega
          have hRGAV := rewind_go_all_valid rest
            ({ result := top.result, scopeLevel := lvl,
               scopeGens := eGens, resultScope := top.resultScope } :: acc1)
            n gens hRestRS hRestSL hRestRSSL hRestValid
          have hIH := ih
            ({ result := top.result, scopeLevel := lvl,
               scopeGens := eGens, resultScope := top.resultScope } :: acc1)
            acc2 hRestMem hRestRSSL hOrdNew hRestGS hRestGC hNewAccEq
          rw [hRGAV] at hIH
          simp only [List.append_assoc, List.singleton_append] at hIH ⊢
          exact hIH
        · -- fvl at scope n fails: both skip
          exact ih acc1 acc2 hRestMem hRestRSSL
            (by rw [List.pairwise_append]; exact ⟨hOrdPre, hTopAcc.2,
              fun a ha b hb => hCross a ha b (List.mem_cons_of_mem _ hb)⟩)
            hRestGS hRestGC hAccEq
      · -- Case 3: top.resultScope ≤ n-1 (both scopes process)
        have hRS : top.resultScope ≤ n - 1 := by omega
        simp only [show ¬(top.resultScope > n - 1) from hGN1, ↓reduceIte]
        -- `split` splits the first match (LHS = scope n-1 fvl)
        split
        · -- scope n-1 fvl succeeds
          rename_i lvl1 eGens1 hfvl_n1
          -- Now split on scope n fvl (RHS)
          split
          · -- scope n fvl also succeeds
            rename_i lvl eGens hfvl_n
            -- Relate (lvl1, eGens1) to (lvl, eGens) via fvl_pop_entry
            have hRel := fvl_pop_entry top n gens hn hRS hTopGS hGensLen lvl eGens hfvl_n
            rw [hRel] at hfvl_n1
            simp only [Option.some.injEq, Prod.mk.injEq] at hfvl_n1
            obtain ⟨hLvl1, hEGens1⟩ := hfvl_n1
            subst hLvl1; subst hEGens1
            -- Simplify filter/map on empty list and singleton
            simp only [List.map_append, List.map_cons, List.map_nil,
                       List.filter_append, List.filter_cons,
                       Imp.Entry.toSpec, List.filter_nil]
            -- rest entries: filter+degrade is identity (sl < top.rs ≤ n-1)
            have hRestRS : ∀ e ∈ rest.reverse.map Imp.Entry.toSpec,
                e.resultScope ≤ n - 1 := by
              intro e he
              obtain ⟨e', he', rfl⟩ := List.mem_map.mp he
              have := hCross e' he' top (List.mem_cons_self ..)
              have := hRSSLrev e' (List.mem_cons_of_mem _ (List.mem_reverse.mp he'))
              simp [Imp.Entry.toSpec]; omega
            have hRestSL : ∀ e ∈ rest.reverse.map Imp.Entry.toSpec,
                e.scopeLevel ≤ n - 1 := by
              intro e he
              obtain ⟨e', he', rfl⟩ := List.mem_map.mp he
              have := hCross e' he' top (List.mem_cons_self ..)
              simp [Imp.Entry.toSpec]; omega
            rw [filter_degrade_spec_id _ (n - 1) hRestRS hRestSL,
                show decide (top.resultScope ≤ n - 1) = true from
                  decide_eq_true_eq.mpr hRS]
            simp only [↓reduceIte, List.map_cons, List.map_nil]
            congr 1
            · congr 1
              -- [{sl = min lvl (n-1)}] = [degrade {sl = lvl}]
              by_cases hlvl : lvl > n - 1
              · simp [hlvl, show min lvl (n - 1) = n - 1 from Nat.min_eq_right (by omega)]
              · simp [hlvl, show min lvl (n - 1) = lvl from Nat.min_eq_left (by omega)]
          · -- scope n fails but scope n-1 succeeds: impossible
            rename_i hfvl_n_none
            exact absurd hfvl_n1 (by rw [fvl_pop_none top n gens hn hRS hfvl_n_none]; simp)
        · -- scope n-1 fvl fails
          rename_i hfvl_n1_none
          -- Now split on scope n fvl (RHS)
          split
          · -- scope n succeeds but scope n-1 fails: impossible
            rename_i lvl eGens hfvl_n
            exfalso
            rw [fvl_pop_entry top n gens hn hRS hTopGS hGensLen lvl eGens hfvl_n]
              at hfvl_n1_none
            exact absurd hfvl_n1_none (by simp)
          · -- Both fail: both skip, use IH
            exact ih acc1 acc2 hRestMem hRestRSSL
              (by rw [List.pairwise_append]; exact ⟨hOrdPre, hTopAcc.2,
                fun a ha b hb => hCross a ha b (List.mem_cons_of_mem _ hb)⟩)
              hRestGS hRestGC hAccEq

/-- Pop preserves SimInv. -/
theorem pop_simInv (h : SimInv spec imp) (hpos : spec.scopeN > 0)
    (hgc : GensConsistent imp.entries) :
    SimInv (Spec.pop spec) (Imp.pop imp) where
  scopeEq := by simp [Spec.pop, Imp.pop, h.scopeEq]
  entriesEq := by
    simp only [Spec.pop, Imp.pop]
    rw [h.entriesEq, h.scopeEq]
    exact (rewind_pop_equiv imp.entries imp.scopeN imp.currentGens
      (h.scopeEq ▸ hpos) h.gensLen h.rsLeSl h.entryOrdered h.gensSuffix hgc).symm
  gensLen := by
    simp only [Imp.pop, List.length_tail, h.gensLen]
    have : imp.scopeN ≥ 1 := h.scopeEq ▸ hpos
    rw [Nat.succ_sub_one, Nat.sub_add_cancel this]
  genBound := by
    intro e he k hk
    simp only [Imp.pop] at he
    exact h.genBound e he k hk
  curGenBound := by
    intro k hk
    simp only [Imp.pop] at hk ⊢
    have : k + 1 < imp.currentGens.length := by
      rw [List.length_tail] at hk; grind
    rw [show imp.currentGens.tail[k]! = imp.currentGens[k + 1]! from by
      simp [List.getElem!_eq_getElem?_getD, List.getElem?_tail]]
    exact h.curGenBound (k + 1) this
  rsLeSl := by
    intro e he; simp only [Imp.pop] at he; exact h.rsLeSl e he
  entryOrdered := by simp only [Imp.pop]; exact h.entryOrdered
  gensNodup := by
    simp only [Imp.pop]
    exact h.gensNodup.sublist (List.tail_sublist _)
  gensSuffix := by
    intro e he i hi j hj heq
    simp only [Imp.pop] at he hj heq ⊢
    have hj1 : j + 1 < imp.currentGens.length := by
      rw [List.length_tail] at hj; grind
    rw [show imp.currentGens.tail[j]! = imp.currentGens[j + 1]! from by
      simp [List.getElem!_eq_getElem?_getD, List.getElem?_tail]] at heq
    rw [show imp.currentGens.tail.drop j = imp.currentGens.drop (j + 1) from by
      rw [List.drop_tail]]
    exact h.gensSuffix e he i hi (j + 1) hj1 heq

private theorem filter_map_toSpec (p : Nat → Bool) (entries : List Imp.Entry) :
    (entries.map Imp.Entry.toSpec).filter (fun e => p e.scopeLevel) =
    (entries.filter (fun e => p e.scopeLevel)).map Imp.Entry.toSpec := by
  induction entries with
  | nil => simp
  | cons hd tl ih =>
    simp only [List.map_cons, List.filter_cons, Imp.Entry.toSpec]
    split <;> simp_all [Imp.Entry.toSpec]

/-- Insert preserves SimInv. -/
theorem insert_simInv (h : SimInv spec imp) (hrs : rs ≤ spec.scopeN) :
    SimInv (Spec.insert spec v rs).1 (Imp.insert imp v rs).1 where
  scopeEq := by simp [Spec.insert, Imp.insert, h.scopeEq]
  entriesEq := by
    simp only [Spec.insert, Imp.insert]
    rw [h.entriesEq, h.scopeEq]
    have hGensPos : imp.currentGens.length > 0 := by
      rw [h.gensLen]; exact Nat.zero_lt_succ _
    rw [rewind_after_insert _ _ imp.scopeN imp.currentGens rfl rfl
      (h.scopeEq ▸ hrs) hGensPos]
    rw [List.map_append, List.map_cons, List.map_nil]
    congr 1
    · exact filter_map_toSpec (· < rs) _
    · simp only [List.cons_eq_cons, and_true, Imp.Entry.toSpec]
      congr 1
      cases hL : (Imp.rewind imp.entries imp.scopeN imp.currentGens).getLast? with
      | none => simp [List.getLast?_map, hL]
      | some e => simp [List.getLast?_map, hL, Imp.Entry.toSpec]
  gensLen := by simp [Imp.insert, h.gensLen]
  genBound := by
    intro e he k hk
    simp only [Imp.insert, List.mem_append, List.mem_cons, List.mem_nil_iff, or_false] at he
    rcases he with hmem | heq
    · -- From filtered rewind entries — these come from imp.entries via rewind
      exact rewind_genBound h.genBound (List.mem_filter.mp hmem).1 k hk
    · -- The new entry has scopeGens = currentGens
      subst heq
      exact h.curGenBound k hk
  curGenBound := by
    intro k hk
    simp only [Imp.insert] at hk ⊢
    exact h.curGenBound k hk
  rsLeSl := by
    intro e he
    simp only [Imp.insert, List.mem_append, List.mem_cons, List.mem_nil_iff, or_false] at he
    rcases he with hmem | heq
    · exact rewind_rsLeSl h.rsLeSl (List.mem_filter.mp hmem).1
    · subst heq; exact h.scopeEq ▸ hrs
  entryOrdered := by
    simp only [Imp.insert, Imp.EntryOrdered]
    apply List.pairwise_append.mpr
    refine ⟨(rewind_entryOrdered h.entryOrdered).filter _, ?_, ?_⟩
    · -- [newEntry].Pairwise R — trivially true for singleton
      exact List.pairwise_cons.mpr ⟨fun b hb => (List.not_mem_nil hb).elim, List.Pairwise.nil⟩
    · -- ∀ a ∈ filtered, ∀ b ∈ [newEntry], R a b
      intro a ha b hb
      simp only [List.mem_singleton] at hb
      subst hb; simp only
      exact of_decide_eq_true (List.mem_filter.mp ha).2
  gensNodup := by simp only [Imp.insert]; exact h.gensNodup
  gensSuffix := by
    intro e he i hi j hj heq
    simp only [Imp.insert, List.mem_append, List.mem_cons, List.mem_nil_iff, or_false] at he
    simp only [Imp.insert] at hj heq ⊢
    rcases he with hmem | heq_e
    · -- Entry from filtered rewind: its gens come from some original entry
      have he_rw := (List.mem_filter.mp hmem).1
      obtain ⟨e', he', _, _, ⟨d, hd⟩⟩ := rewind_mem he_rw
      -- Convert to facts about e'.scopeGens
      have hi' : i + d < e'.scopeGens.length := by
        rw [hd] at hi; simp only [List.length_drop] at hi; grind
      have heq' : e'.scopeGens[i + d]! = imp.currentGens[j]! := by
        rw [hd] at heq
        simp only [List.getElem!_eq_getElem?_getD, List.getElem?_drop] at heq
        rw [Nat.add_comm] at heq
        simp only [List.getElem!_eq_getElem?_getD]
        exact heq
      have hsuf := h.gensSuffix e' he' (i + d) hi' j hj heq'
      -- hsuf : e'.scopeGens.drop (i + d) = imp.currentGens.drop j
      -- Goal: e.scopeGens.drop i = imp.currentGens.drop j
      rw [hd, List.drop_drop, Nat.add_comm]
      exact hsuf
    · -- The new entry: scopeGens = imp.currentGens
      subst heq_e; simp only at hi heq ⊢
      -- heq : currentGens[i]! = currentGens[j]!
      -- Use Nodup to conclude i = j, then drop i = drop j
      have heqi : imp.currentGens[i]? = imp.currentGens[j]? := by
        simp only [List.getElem!_eq_getElem?_getD,
          List.getElem?_eq_getElem hi, List.getElem?_eq_getElem hj,
          Option.getD_some] at heq
        simp only [List.getElem?_eq_getElem hi, List.getElem?_eq_getElem hj]
        exact congrArg some heq
      have hij := (List.getElem?_inj hi h.gensNodup).mp heqi
      subst hij; rfl

/-! ### Reachable states and main theorems -/

/-- States reachable from empty via matching operations. -/
inductive Reachable : Spec.State → Imp.State → Prop where
  | empty : Reachable Spec.empty Imp.empty
  | push : Reachable s i → Reachable (Spec.push s) (Imp.push i)
  | pop : Reachable s i → s.scopeN > 0 →
          Reachable (Spec.pop s) (Imp.pop i)
  | insert : Reachable s i → rs ≤ s.scopeN →
             Reachable (Spec.insert s v rs).1 (Imp.insert i v rs).1

private theorem push_gensConsistent (hgc : GensConsistent imp.entries) :
    GensConsistent (Imp.push imp).entries := by
  simp only [Imp.push]; exact hgc

private theorem pop_gensConsistent (hgc : GensConsistent imp.entries) :
    GensConsistent (Imp.pop imp).entries := by
  simp only [Imp.pop]; exact hgc

/-- When all entries are aligned with a common gens list and ordered, GensConsistent holds. -/
private theorem aligned_to_gensConsistent
    {entries : List Imp.Entry} {g : Imp.GenList} {N : Nat}
    (hAlign : ∀ e ∈ entries, e.scopeGens = g.drop (N - e.scopeLevel))
    (hOrd : entries.Pairwise (fun a b => a.scopeLevel < b.resultScope))
    (hRSSL : ∀ e ∈ entries, e.resultScope ≤ e.scopeLevel)
    (hSLle : ∀ e ∈ entries, e.scopeLevel ≤ N) :
    GensConsistent entries := by
  unfold GensConsistent
  induction entries with
  | nil => exact List.Pairwise.nil
  | cons hd tl ih =>
    apply List.pairwise_cons.mpr
    constructor
    · intro b hb
      rw [hAlign hd (List.mem_cons_self ..), hAlign b (List.mem_cons_of_mem _ hb),
        List.drop_drop]; congr 1
      have := (List.pairwise_cons.mp hOrd).1 b hb
      have := hRSSL b (List.mem_cons_of_mem _ hb)
      have := hSLle b (List.mem_cons_of_mem _ hb)
      omega
    · exact ih (fun e he => hAlign e (List.mem_cons_of_mem _ he))
        (List.pairwise_cons.mp hOrd).2
        (fun e he => hRSSL e (List.mem_cons_of_mem _ he))
        (fun e he => hSLle e (List.mem_cons_of_mem _ he))

/-- After rewind, all surviving entries have gens aligned with currentGens. -/
private theorem rewind_gens_aligned
    (hGC : GensConsistent entries)
    (hGS : ∀ e ∈ entries, ∀ i < e.scopeGens.length, ∀ j < gens.length,
      e.scopeGens[i]! = gens[j]! → e.scopeGens.drop i = gens.drop j)
    (hRSSL : ∀ e ∈ entries, e.resultScope ≤ e.scopeLevel)
    (hOrd : Imp.EntryOrdered entries) :
    ∀ e ∈ Imp.rewind entries n gens, e.scopeGens = gens.drop (n - e.scopeLevel) := by
  simp only [Imp.rewind]
  suffices ∀ revEntries acc,
      (∀ e ∈ revEntries, e ∈ entries) →
      revEntries.reverse.Pairwise
        (fun a b => a.scopeGens = b.scopeGens.drop (b.scopeLevel - a.scopeLevel)) →
      revEntries.reverse.Pairwise (fun a b => a.scopeLevel < b.resultScope) →
      (∀ e ∈ acc, e.scopeGens = gens.drop (n - e.scopeLevel)) →
      ∀ e ∈ Imp.rewind.go n gens revEntries acc,
        e.scopeGens = gens.drop (n - e.scopeLevel) by
    exact this entries.reverse []
      (fun e he => List.mem_reverse.mp he)
      (by simpa using hGC)
      (by simpa using hOrd)
      (by simp)
  intro revEntries acc hMem hGCrev hOrdRev hAccAligned
  induction revEntries generalizing acc with
  | nil => simp [Imp.rewind.go]; exact hAccAligned
  | cons top rest ih =>
    have hTopMem := hMem top (List.mem_cons_self ..)
    have hRestMem := fun e he => hMem e (List.mem_cons_of_mem _ he)
    -- Decompose Pairwise on rest.reverse ++ [top]
    rw [List.reverse_cons] at hGCrev hOrdRev
    have hGCparts := List.pairwise_append.mp hGCrev
    have hRestGC := hGCparts.1
    have hGCcross := hGCparts.2.2
    have hOrdParts := List.pairwise_append.mp hOrdRev
    have hRestOrd := hOrdParts.1
    have hOrdCross := hOrdParts.2.2
    simp only [Imp.rewind.go]
    by_cases hGN : top.resultScope > n
    · -- rs > n: discard
      simp only [hGN, ↓reduceIte]
      exact ih acc hRestMem hRestGC hRestOrd hAccAligned
    · simp only [show ¬(top.resultScope > n) from hGN, ↓reduceIte]
      split
      · -- fvl succeeds: early stop, result = rest.reverse ++ [modified_top] ++ acc
        rename_i lvl eGens hfvl
        -- Establish key facts about the fvl result before intro
        have hTopRSSL := hRSSL top hTopMem
        have hRSleMin : top.resultScope ≤ min top.scopeLevel n :=
          Nat.le_min.mpr ⟨hTopRSSL, Nat.not_lt.mp hGN⟩
        have hRSle := findValidLevel_rs_le hRSleMin hfvl
        have hEGensDrop := findValidLevel_output_eq_input_drop hfvl
        have hFVLle := findValidLevel_lvl_le hfvl
        have hEGensTop : eGens = top.scopeGens.drop (top.scopeLevel - lvl) := by
          rw [hEGensDrop, List.drop_drop]; congr 1; omega
        have hEGensGens := findValidLevel_aligned hfvl rfl rfl (hGS top hTopMem)
        have hEGensGens' : eGens = gens.drop (n - lvl) := by
          rw [hEGensGens]; congr 1; omega
        have hTopDrop : top.scopeGens.drop (top.scopeLevel - lvl) =
            gens.drop (n - lvl) := by
          rw [← hEGensGens', hEGensTop]
        intro e he
        -- result = (rest.reverse ++ [modified_top]) ++ acc
        rcases List.mem_append.mp he with hLeft | hAcc
        · rcases List.mem_append.mp hLeft with hRest | hSingle
          · -- e ∈ rest.reverse: derive alignment from GensConsistent + fvl result
            have heGC := hGCcross e hRest top (List.mem_singleton.mpr rfl)
            have heOrd := hOrdCross e hRest top (List.mem_singleton.mpr rfl)
            -- e.gens = gens.drop(n - e.sl) via List.drop_drop
            rw [heGC, show top.scopeLevel - e.scopeLevel =
              (top.scopeLevel - lvl) + (lvl - e.scopeLevel) from by omega,
              ← List.drop_drop, hTopDrop, List.drop_drop]
            congr 1; omega
          · -- e ∈ [modified_top]: from findValidLevel_aligned
            rw [List.mem_singleton.mp hSingle]
            simp only [show (n - min top.scopeLevel n) + (min top.scopeLevel n - lvl) =
              n - lvl from by omega] at hEGensGens
            exact hEGensGens
        · -- e ∈ acc: from hypothesis
          exact hAccAligned e hAcc
      · -- fvl fails: continue with rest
        exact ih acc hRestMem hRestGC hRestOrd hAccAligned

private theorem insert_gensConsistent (h : SimInv spec imp)
    (hgc : GensConsistent imp.entries) (hrs : rs ≤ spec.scopeN) :
    GensConsistent (Imp.insert imp v rs).1.entries := by
  have hRA := rewind_gens_aligned (n := imp.scopeN) (gens := imp.currentGens)
    hgc h.gensSuffix h.rsLeSl h.entryOrdered
  -- After insert, entries = filter(sl < rs, rewind) ++ [newEntry]
  -- All are aligned with currentGens and GensConsistent follows from aligned_to_gensConsistent
  change GensConsistent (Imp.insert imp v rs).1.entries
  unfold Imp.insert
  simp only
  apply aligned_to_gensConsistent (N := imp.scopeN) (g := imp.currentGens)
  · -- alignment: all entries have gens = currentGens.drop(scopeN - sl)
    intro e he
    rcases List.mem_append.mp he with hmem | hSingle
    · exact hRA e (List.mem_filter.mp hmem).1
    · rw [List.mem_singleton.mp hSingle]; simp [Nat.sub_self]
  · -- ordering: Pairwise (fun a b => a.sl < b.rs)
    apply List.pairwise_append.mpr
    refine ⟨(rewind_entryOrdered h.entryOrdered).filter _, ?_, ?_⟩
    · exact List.pairwise_cons.mpr ⟨fun b hb => by simp at hb, List.Pairwise.nil⟩
    · intro a ha b hb
      rw [List.mem_singleton.mp hb]
      exact of_decide_eq_true (List.mem_filter.mp ha).2
  · -- rsLeSl
    intro e he
    rcases List.mem_append.mp he with hmem | hSingle
    · exact rewind_rsLeSl h.rsLeSl (List.mem_filter.mp hmem).1
    · rw [List.mem_singleton.mp hSingle]; exact h.scopeEq ▸ hrs
  · -- sl ≤ scopeN
    intro e he
    rcases List.mem_append.mp he with hmem | hSingle
    · have := of_decide_eq_true (List.mem_filter.mp hmem).2
      have := h.scopeEq ▸ hrs; omega
    · rw [List.mem_singleton.mp hSingle]; simp

/-- All reachable states satisfy the simulation invariant and gens consistency. -/
private theorem reachable_simInv_gc : Reachable s i → SimInv s i ∧ GensConsistent i.entries := by
  intro h
  induction h with
  | empty => exact ⟨simInv_empty, List.Pairwise.nil⟩
  | push _ ih =>
    exact ⟨push_simInv ih.1, push_gensConsistent ih.2⟩
  | pop _ hpos ih =>
    exact ⟨pop_simInv ih.1 hpos ih.2, pop_gensConsistent ih.2⟩
  | insert _ hrs ih =>
    exact ⟨insert_simInv ih.1 hrs, insert_gensConsistent ih.1 ih.2 hrs⟩

/-- All reachable states satisfy the simulation invariant. -/
theorem reachable_simInv : Reachable s i → SimInv s i :=
  fun h => (reachable_simInv_gc h).1

/-- Main theorem: lookup is equivalent for all reachable states. -/
theorem reachable_lookup_equiv (h : Reachable s i) :
    Spec.lookup s = Imp.lookup i :=
  lookup_equiv (reachable_simInv h)

/-- Main theorem: insert sharing is equivalent for all reachable states. -/
theorem reachable_insert_equiv (h : Reachable s i) :
    (Spec.insert s v rs).2 = (Imp.insert i v rs).2 :=
  insert_sharing_equiv (reachable_simInv h)

end Proofs

end ScopeCache
