import GolangLean.Core.Semantics

namespace GolangLean.Core

/-! # Determinism of TGC big-step.

  `BigStep h env e v₁ h₁ → BigStep h env e v₂ h₂ → v₁ = v₂ ∧ h₁ = h₂`

By induction on the first derivation, with case analysis on the second.
For each pair of constructors, either the term-shape forces them to agree
(so we apply the IHs to the sub-derivations) or, in the `ifte` case where
two rules share a term shape, an IH on the condition gives a contradictory
boolean. -/

theorem BigStep.deterministic
    {h : Heap} {env : Env} {e : Term} {v₁ v₂ : Value} {h₁ h₂ : Heap}
    (D₁ : BigStep h env e v₁ h₁) (D₂ : BigStep h env e v₂ h₂) :
    v₁ = v₂ ∧ h₁ = h₂ := by
  induction D₁ generalizing v₂ h₂ with
  | unitR =>
    cases D₂; exact ⟨rfl, rfl⟩
  | intLitR n =>
    cases D₂; exact ⟨rfl, rfl⟩
  | boolLitR b =>
    cases D₂; exact ⟨rfl, rfl⟩
  | varR hLook =>
    cases D₂ with
    | varR hLook' =>
      have heq := hLook.symm.trans hLook'
      exact ⟨Option.some.inj heq, rfl⟩
  | lamR x body =>
    cases D₂; exact ⟨rfl, rfl⟩
  | appR _ _ _ ih1 ih2 ihb =>
    cases D₂ with
    | appR D1' D2' Db' =>
      have ⟨hClos, hH1⟩ := ih1 D1'
      injection hClos with hx hbody henv
      subst hx; subst hbody; subst henv; subst hH1
      have ⟨hArg, hH2⟩ := ih2 D2'
      subst hArg; subst hH2
      exact ihb Db'
  | letInR _ _ ih1 ih2 =>
    cases D₂ with
    | letInR D1' D2' =>
      have ⟨hv1, hH1⟩ := ih1 D1'
      subst hv1; subst hH1
      exact ih2 D2'
  | ifTR _ _ ihc iht =>
    cases D₂ with
    | ifTR Dc' Dt' =>
      have ⟨_, hH1⟩ := ihc Dc'
      subst hH1
      exact iht Dt'
    | ifFR Dc' _ =>
      have ⟨hb, _⟩ := ihc Dc'
      injection hb with hb_eq
      exact Bool.noConfusion hb_eq
  | ifFR _ _ ihc ihf =>
    cases D₂ with
    | ifTR Dc' _ =>
      have ⟨hb, _⟩ := ihc Dc'
      injection hb with hb_eq
      exact Bool.noConfusion hb_eq
    | ifFR Dc' Df' =>
      have ⟨_, hH1⟩ := ihc Dc'
      subst hH1
      exact ihf Df'
  | binopR _ _ Hop ih1 ih2 =>
    cases D₂ with
    | binopR D1' D2' Hop' =>
      have ⟨hv1, hH1⟩ := ih1 D1'
      subst hv1; subst hH1
      have ⟨hv2, hH2⟩ := ih2 D2'
      subst hv2; subst hH2
      have heq := Hop.symm.trans Hop'
      exact ⟨Option.some.inj heq, rfl⟩
  | refMkR _ ih =>
    cases D₂ with
    | refMkR D' =>
      have ⟨hv, hH⟩ := ih D'
      subst hv; subst hH
      exact ⟨rfl, rfl⟩
  | derefR _ Hget ih =>
    cases D₂ with
    | derefR D' Hget' =>
      have ⟨hloc, hH⟩ := ih D'
      injection hloc with hloceq
      subst hloceq; subst hH
      have heq := Hget.symm.trans Hget'
      exact ⟨Option.some.inj heq, rfl⟩
  | assignR _ _ _ ih1 ih2 =>
    cases D₂ with
    | assignR D1' D2' _ =>
      have ⟨hloc, hH1⟩ := ih1 D1'
      injection hloc with hloceq
      subst hloceq; subst hH1
      have ⟨hv, hH2⟩ := ih2 D2'
      subst hv; subst hH2
      exact ⟨rfl, rfl⟩
  | seqR _ _ ih1 ih2 =>
    cases D₂ with
    | seqR D1' D2' =>
      have ⟨_, hH1⟩ := ih1 D1'
      subst hH1
      exact ih2 D2'

end GolangLean.Core
