import Spa.Analysis.Forward.Evaluation

namespace Spa

namespace Forward

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

def updateVariablesFromExpression (k : String) (e : Expr)
    (vs : VariableValues L prog) : VariableValues L prog :=
  FiniteMap.generalizedUpdate id (fun _ vs => E.eval e vs) [k] vs

lemma updateVariablesFromExpression_mono (k : String) (e : Expr) :
    Monotone (updateVariablesFromExpression (L := L) (prog := prog) k e) :=
  FiniteMap.generalizedUpdate_monotone monotone_id (fun _ => E.eval_mono e)

def evalBasicStmt (bs : BasicStmt)
    (vs : VariableValues L prog) : VariableValues L prog :=
  match bs with
  | .assign k e => updateVariablesFromExpression k e vs
  | .noop => vs

lemma evalBasicStmt_mono (bs : BasicStmt) :
    Monotone (evalBasicStmt (L := L) (prog := prog) bs) := by
  cases bs with
  | assign k e => exact updateVariablesFromExpression_mono k e
  | noop => exact monotone_id

def evalBasicStmtOpt (obs : Option BasicStmt)
    (vs : VariableValues L prog) : VariableValues L prog :=
  match obs with
  | none => vs
  | some bs => evalBasicStmt bs vs

lemma evalBasicStmtOpt_mono (obs : Option BasicStmt) :
    Monotone (evalBasicStmtOpt (L := L) (prog := prog) obs) := by
  cases obs <;> unfold evalBasicStmtOpt
  · exact monotone_id
  · apply evalBasicStmt_mono

instance ExprEvaluator.toStmtEvaluator : StmtEvaluator L prog :=
  ⟨evalBasicStmtOpt ∘ prog.code,
   by intro s; simp; exact (evalBasicStmtOpt_mono (prog.code s))⟩

instance ExprEvaluator.toStmtEvaluator_valid [LatticeInterpretation L]
    [ValidExprEvaluator L prog] : ValidStmtEvaluator L prog := by
  constructor
  simp [StmtEvaluator.eval, evalBasicStmtOpt]
  intro s vs ρ₁ ρ₂; generalize prog.code s = obs; intro hev hvs
  rcases hev with _ | @⟨_,bs,hev⟩ <;> try simpa
  rcases hev with _ | @⟨k, e, v, hev⟩ <;> try simpa
  intros k' l' hkl' v' hρ
  rcases hρ with _ | ⟨_,_,_,_,_,hne,hmem⟩ <;> simp [evalBasicStmt] at hkl'
  · have hl := FiniteMap.generalizedUpdate_mem_eq (f := id)
        (g := fun _ vs => E.eval e vs) (List.mem_singleton_self k) hkl'
    rewrite [hl]; simp
    exact ValidExprEvaluator.valid hev hvs
  · have hl := FiniteMap.generalizedUpdate_not_mem_backward
        (fun hmem => hne (List.mem_singleton.mp hmem)) hkl'
    apply hvs _ _ hl _ hmem

end Forward

end Spa
