mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-17 18:34:06 +00:00
This PR replaces the default `instantiateMVars` implementation with a two-pass variant that fuses fvar substitution into the traversal, avoiding separate `replace_fvars` calls for delayed-assigned MVars and preserving sharing. The old single-pass implementation is removed entirely. The previous implementation had quadratic complexity when instantiating expressions with long chains of nested delayed-assigned MVars. Such chains arise naturally from repeated `intro`/`apply` tactic sequences, where each step creates a new delayed assignment wrapping the previous one. The new two-pass approach resolves the entire chain in a single traversal with a fused fvar substitution, reducing this to linear complexity. ### Terminology (used in this PR and in the source) * **Direct MVar**: an MVar that is not delayed-assigned. * **Pending MVar**: the direct MVar stored in a `DelayedMetavarAssignment`. * **Assigned MVar**: a direct MVar with an assignment, or a delayed-assigned MVar with an assigned pending MVar. * **MVar DAG**: the directed acyclic graph of MVars reachable from the expression. * **Resolvable MVar**: an MVar where all MVars reachable from it (including itself) are assigned. * **Updateable MVar**: an assigned direct MVar, or a delayed-assigned MVar that is resolvable but not reachable from any other resolvable delayed-assigned MVar. In the MVar DAG, the updateable delayed-assigned MVars form a cut (the **updateable-MVar cut**) with only assigned MVars behind it and no resolvable delayed-assigned MVars before it. ### Two-pass architecture **Pass 1** (`instantiate_direct_fn`): Traverses all MVars and expressions reachable from the initial expression and instantiates all updateable direct MVars (updating their assignment with the result), instantiates all level MVars, and determines if there are any updateable delayed-assigned MVars. **Pass 2** (`instantiate_delayed_fn`): Only run if pass 1 found updateable delayed-assigned MVars. Has an **outer** and an **inner** mode, depending on whether it has crossed the updateable-MVar cut. In outer mode (empty fvar substitution), all MVars are either unassigned direct MVars (left alone), non-updateable delayed-assigned MVars (pending MVar traversed in outer mode and updated with the result), or updateable delayed-assigned MVars. When a delayed-assigned MVar is encountered, its MVar DAG is explored (via `is_resolvable_pending`) to determine if it is resolvable (and thus updateable). Results are cached across invocations. If it is updateable, the substitution is initialized from its arguments and traversal continues with the value of its pending MVar in inner mode. In inner mode (non-empty substitution), all encountered delayed-assigned MVars are, by construction, resolvable but not updateable. The substitution is carried along and extended as we cross such MVars. Pending MVars of these delayed-assigned MVars are NOT updated with the result (as the result is valid only for this substitution, not in general). Applying the substitution in one go, rather than instantiating each delayed-assigned MVar on its own from inside out, avoids the quadratic overhead of that approach when there are long chains of delayed-assigned MVars. **Write-back behavior**: Pass 2 writes back the normalized pending MVar values of delayed-assigned MVars above the updateable-MVar cut (the non-resolvable ones whose children may have been resolved). This is exactly the right set: these MVars are visited in outer mode, so their normalized values are suitable for storing in the mctx. MVars below the cut are visited in inner mode, so their intermediate values cannot be written back. ### Pass 2 scope-tracked caching A `scope_cache` data structure ensures that sharing is preserved even across different delayed-assigned MVars (and hence with different substitutions), when possible. Each `visit_delayed` call pushes a new scope with fresh fvar bindings. The cache correctly handles cross-scope reuse, fvar shadowing, and late-binding via generation counters and scope-level tracking. The `scope_cache` has been formally verified: `tests/elab/scopeCacheProofs.lean` contains a complete Lean proof that the lazy generation-based implementation refines the eager specification, covering all operations (push, pop, lookup, insert) including the rewind lazy cleanup with scope re-entry and degradation. The key correctness invariant is inter-entry gen list consistency (GensConsistent), which, unlike per-entry alignment with `currentGens`, survives pop+push cycles. ### Behavioral differences from original `instantiateMVars` The implementation matches the original single-pass `instantiateMVars` behavior with one cosmetic difference: the new implementation substitutes fvars inline during traversal rather than constructing intermediate beta-redexes, producing more beta-reduced terms in some edge cases. This changes the pretty-printed output for two elab tests (`1179b`, `depElim1`) but all terms remain definitionally equal. ### Tests Correctness and performance tests for the new implementation were added in #12808. ### Files - `src/library/instantiate_mvars.cpp` — C++ implementation of both passes (replaces `src/kernel/instantiate_mvars.cpp`) - `src/library/scope_cache.h` — scope-aware cache data structure - `src/Lean/MetavarContext.lean` — exported accessors for `DelayedMetavarAssignment` fields - `tests/elab/scopeCacheProofs.lean` — formal verification of `scope_cache` correctness - `tests/elab/1179b.lean.out.expected`, `tests/elab/depElim1.lean.out.expected` — updated expected output Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1691 lines
76 KiB
Lean4
1691 lines
76 KiB
Lean4
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
|