/-
Port of `Analysis/Forward/Adapters.agda` (`ExprToStmtAdapter`).

Correspondence:
  updateVariablesFromExpression           ↦ updateVariablesFromExpression
  updateVariablesFromExpression-Mono      ↦ updateVariablesFromExpression_mono
  (the -k∈ks-≡ / -k∉ks-backward renames   ↦ used directly from FiniteMap)
  evalᵇ, evalᵇ-Monoʳ                      ↦ evalB, evalB_mono
  stmtEvaluator (instance)                ↦ ExprEvaluator.toStmtEvaluator
  evalᵇ-valid, validStmtEvaluator         ↦ ExprEvaluator.toStmtEvaluator_valid
                                            (the Agda `k ≟ˢ k'` case split is
                                            subsumed by `cases` on `Env.Mem`,
                                            whose `here` case forces `k' = k`)
-/
import Spa.Analysis.Forward.Evaluation

namespace Spa

variable {L : Type} [Lattice L] {prog : Program}

/-- Agda: `updateVariablesFromExpression` — set the single key `k` to the
value of `e` (the `GeneralizedUpdate` with `ks = [k]`). -/
def updateVariablesFromExpression (E : ExprEvaluator L prog) (k : String)
    (e : Expr) (vs : VariableValues L prog) : VariableValues L prog :=
  FiniteMap.generalizedUpdate id (fun _ vs => E.eval e vs) [k] vs

/-- Agda: `updateVariablesFromExpression-Mono`. -/
theorem updateVariablesFromExpression_mono (E : ExprEvaluator L prog)
    (k : String) (e : Expr) :
    Monotone (updateVariablesFromExpression E k e) :=
  FiniteMap.generalizedUpdate_monotone monotone_id (fun _ => E.eval_mono e)

/-- Agda: `evalᵇ`. -/
def evalB (E : ExprEvaluator L prog) (_ : prog.State) (bs : BasicStmt)
    (vs : VariableValues L prog) : VariableValues L prog :=
  match bs with
  | .assign k e => updateVariablesFromExpression E k e vs
  | .noop => vs

/-- Agda: `evalᵇ-Monoʳ`. -/
theorem evalB_mono (E : ExprEvaluator L prog) (s : prog.State) (bs : BasicStmt) :
    Monotone (evalB E s bs) := by
  cases bs with
  | assign k e => exact updateVariablesFromExpression_mono E k e
  | noop => exact monotone_id

/-- Agda: the `stmtEvaluator` instance of `ExprToStmtAdapter`. -/
def ExprEvaluator.toStmtEvaluator (E : ExprEvaluator L prog) :
    StmtEvaluator L prog :=
  ⟨evalB E, evalB_mono E⟩

/-- Agda: `evalᵇ-valid` / the `validStmtEvaluator` instance. -/
theorem ExprEvaluator.toStmtEvaluator_valid (E : ExprEvaluator L prog)
    {I : LatticeInterpretation L} (hE : IsValidExprEvaluator E I) :
    IsValidStmtEvaluator E.toStmtEvaluator I := by
  intro s vs ρ₁ ρ₂ bs hbs hvs
  cases hbs with
  | noop => exact hvs
  | assign k e v hev =>
    intro k' l hk'l v' hv'
    cases hv' with
    | here =>
      have hk'l₀ : (k, l) ∈ FiniteMap.generalizedUpdate (ks := prog.vars) id
          (fun _ vs => E.eval e vs) [k] vs := hk'l
      have hl := FiniteMap.generalizedUpdate_mem_eq (f := id)
        (g := fun _ vs => E.eval e vs) (List.mem_singleton_self k) hk'l₀
      rw [hl]
      exact hE hev hvs
    | there _ _ _ _ _ hne hmem' =>
      have hk'l₀ : (k', l) ∈ FiniteMap.generalizedUpdate (ks := prog.vars) id
          (fun _ vs => E.eval e vs) [k] vs := hk'l
      have hk'l' : (k', l) ∈ (id vs : VariableValues L prog) :=
        FiniteMap.generalizedUpdate_not_mem_backward
          (fun hmem => hne (List.mem_singleton.mp hmem)) hk'l₀
      exact hvs _ _ hk'l' _ hmem'

end Spa
