/- Port of `Analysis/Forward/Lattices.agda`. The Agda module instantiates `Lattice.FiniteMap` twice (variables ↦ abstract values, states ↦ variable maps) and re-exports everything with ᵛ/ᵐ suffixes. In Lean the two instantiations are `abbrev`s and the FiniteMap API is used directly; the module parameters (the finite-height lattice `L`, the program) become section variables. Correspondence: VariableValues, StateVariables ↦ VariableValues, StateVariables isLatticeᵛ/isLatticeᵐ, ⊔ᵛ, ≼ᵛ … ↦ (the FiniteMap Lattice instances) fixedHeightᵛ ↦ varsFixedHeight ⊥ᵛ, ⊥ᵛ-contains-bottoms ↦ botV, FiniteMap.bot_contains_bots states-in-Map ↦ states_memKey variablesAt ↦ variablesAt variablesAt-∈ ↦ variablesAt_mem variablesAt-≈ ↦ (congruence, trivial with `=`) joinForKey, joinForKey-Mono ↦ joinForKey, joinForKey_mono joinAll, joinAll-Mono, joinAll-k∈ks-≡ ↦ joinAll, joinAll_mono, joinAll_mem_eq variablesAt-joinAll ↦ variablesAt_joinAll ⟦_⟧ᵛ ↦ interpV ⟦⊥ᵛ⟧ᵛ∅ ↦ interpV_botV_nil ⟦⟧ᵛ-respects-≈ᵛ ↦ (trivial with `=`) ⟦⟧ᵛ-⊔ᵛ-∨ ↦ interpV_sup ⟦⟧ᵛ-foldr ↦ interpV_foldr -/ import Spa.Language import Spa.Lattice.FiniteMap namespace Spa variable (L : Type) [Lattice L] (prog : Program) /-- Agda: `VariableValues`. -/ abbrev VariableValues : Type := FiniteMap String L prog.vars /-- Agda: `StateVariables`. -/ abbrev StateVariables : Type := FiniteMap prog.State (VariableValues L prog) prog.states variable {h : ℕ} /-- Agda: `fixedHeightᵛ`. -/ def varsFixedHeight (fhL : FixedHeight L h) : FixedHeight (VariableValues L prog) (prog.vars.length * h) := FiniteMap.fixedHeight fhL prog.vars /-- Agda: `⊥ᵛ`. -/ def botV (fhL : FixedHeight L h) : VariableValues L prog := (varsFixedHeight L prog fhL).bot /-- Agda: `fixedHeight` on `StateVariables` (assembled in `Forward.agda`'s fixpoint call; named here for reuse). -/ def statesFixedHeight (fhL : FixedHeight L h) : FixedHeight (StateVariables L prog) (prog.states.length * (prog.vars.length * h)) := FiniteMap.fixedHeight (varsFixedHeight L prog fhL) prog.states variable {L prog} omit [Lattice L] in /-- Agda: `states-in-Map`. -/ theorem states_memKey (s : prog.State) (sv : StateVariables L prog) : FiniteMap.MemKey s sv := FiniteMap.memKey_iff.mpr (prog.states_complete s) /-- Agda: `variablesAt`. -/ def variablesAt (s : prog.State) (sv : StateVariables L prog) : VariableValues L prog := (FiniteMap.locate (states_memKey s sv)).1 omit [Lattice L] in /-- Agda: `variablesAt-∈`. -/ theorem variablesAt_mem (s : prog.State) (sv : StateVariables L prog) : (s, variablesAt s sv) ∈ sv := (FiniteMap.locate (states_memKey s sv)).2 /-- Agda: `m₁≼m₂⇒m₁[k]ᵐ≼m₂[k]ᵐ`, specialized the way `Forward.agda` uses it. -/ theorem variablesAt_le {sv₁ sv₂ : StateVariables L prog} (hle : sv₁ ≤ sv₂) (s : prog.State) : variablesAt s sv₁ ≤ variablesAt s sv₂ := FiniteMap.le_of_mem_mem prog.states_nodup hle (variablesAt_mem s sv₁) (variablesAt_mem s sv₂) variable (fhL : FixedHeight L h) /-- Agda: `joinForKey`. -/ def joinForKey (k : prog.State) (sv : StateVariables L prog) : VariableValues L prog := (sv.valuesAt (prog.incoming k)).foldr (· ⊔ ·) (botV L prog fhL) /-- Agda: `joinForKey-Mono`. -/ theorem joinForKey_mono (k : prog.State) : Monotone (joinForKey fhL k) := by intro sv₁ sv₂ hle exact foldr_mono _ (FiniteMap.valuesAt_le hle (prog.incoming k)) (le_refl _) (fun b _ _ hab => sup_le_sup_right hab b) (fun a _ _ hab => sup_le_sup_left hab a) /-- Agda: `joinAll` (the "Exercise 4.26" generalized update with `f = id`). -/ def joinAll (sv : StateVariables L prog) : StateVariables L prog := FiniteMap.generalizedUpdate id (joinForKey fhL) prog.states sv /-- Agda: `joinAll-Mono`. -/ theorem joinAll_mono : Monotone (joinAll (prog := prog) fhL) := FiniteMap.generalizedUpdate_monotone monotone_id (joinForKey_mono fhL) /-- Agda: `joinAll-k∈ks-≡`. -/ theorem joinAll_mem_eq {s : prog.State} {vs : VariableValues L prog} {sv : StateVariables L prog} (h : (s, vs) ∈ joinAll fhL sv) : vs = joinForKey fhL s sv := FiniteMap.generalizedUpdate_mem_eq (prog.states_complete s) h /-- Agda: `variablesAt-joinAll`. -/ theorem variablesAt_joinAll (s : prog.State) (sv : StateVariables L prog) : variablesAt s (joinAll fhL sv) = joinForKey fhL s sv := joinAll_mem_eq fhL (variablesAt_mem s (joinAll fhL sv)) /-! ### Lifting an interpretation to variable maps -/ variable (I : LatticeInterpretation L) /-- Agda: `⟦_⟧ᵛ`. -/ def interpV (vs : VariableValues L prog) (ρ : Env) : Prop := ∀ (k : String) (l : L), (k, l) ∈ vs → ∀ (v : Value), Env.Mem (k, v) ρ → I.interp l v /-- Agda: `⟦⊥ᵛ⟧ᵛ∅`. -/ theorem interpV_botV_nil : interpV I (botV L prog fhL) [] := by intro k l _ v hmem cases hmem /-- Agda: `⟦⟧ᵛ-⊔ᵛ-∨`. -/ theorem interpV_sup {vs₁ vs₂ : VariableValues L prog} {ρ : Env} (h : interpV I vs₁ ρ ∨ interpV I vs₂ ρ) : interpV I (vs₁ ⊔ vs₂) ρ := by intro k l hmem v hv obtain ⟨l₁, l₂, rfl, h₁, h₂⟩ := FiniteMap.mem_sup hmem rcases h with h | h · exact I.interp_sup v (Or.inl (h _ _ h₁ _ hv)) · exact I.interp_sup v (Or.inr (h _ _ h₂ _ hv)) /-- Agda: `⟦⟧ᵛ-foldr`. -/ theorem interpV_foldr {vs : VariableValues L prog} {vss : List (VariableValues L prog)} {ρ : Env} (hvs : interpV I vs ρ) (hmem : vs ∈ vss) : interpV I (vss.foldr (· ⊔ ·) (botV L prog fhL)) ρ := by induction vss with | nil => cases hmem | cons vs' vss' ih => rcases List.mem_cons.mp hmem with rfl | hmem' · exact interpV_sup I (Or.inl hvs) · exact interpV_sup I (Or.inr (ih hmem')) end Spa