import Stoughton.Syntax as Term
open import Stoughton.Var

module Beta (𝒞 : Set) {𝒱} (var : Enum 𝒱) where

  open import Level
  open import Data.Product
  open import Data.Nat hiding (_*_; _≟_)
  open import Relation.Nullary
  import Relation.Binary.Reasoning.Preorder as PreR
  open import Relation.Binary.PropositionalEquality.Core
  open import Data.List renaming (map to mapL)
  open import Data.List.Properties
  open import Data.List.Membership.Propositional
  open import Data.List.Membership.Propositional.Properties
  open import Data.Empty
  open import Relation.Nullary
  open import Data.List.Relation.Unary.Any
  
  private
    _≟_ = Enum._≟_ var
  
  open Term 𝒞 𝒱 _≟_
  open import Stoughton.Substitution 𝒞 var
  open import Stoughton.SubstitutionLemmas 𝒞 var
  open import Stoughton.Alpha 𝒞 var
  open import Definitions 𝒞 var
  open import Stoughton.Chi (Enum.encode var) (Enum.decode var) (Enum.inverse var)

  infix 3 _▹β_
  data _▹β_ : Λ → Λ → Set where
    β : ∀ {x M N A} → (λ[ x ∶ A ] M) · N ▹β M [ x := N ]

  β⁻¹preserves* : AntiPreserves* _▹β_
  β⁻¹preserves* {x} .{(λ[ y ∶ _ ] M) · N} x*M[y/N] (β {y} {M} {N}) with lemmafreeσ→ₗ {x} {M} x*M[y/N]
  ...  | z , z*M , x*z[y/N] with y ≟ z
  β⁻¹preserves* {x} .{(λ[ y ∶ A ] M) · N} x*M[y/N] (β {y} {M} {N} {A}) | .y , y*M , x*N           | yes refl = ∈-++⁺ʳ (fv (λ[ y ∶ A ] M)) x*N
  β⁻¹preserves* {x} .{(λ[ y ∶ A ] M) · N} x*M[y/N] (β {y} {M} {N} {A})     | .x , x*M , here refl | no y≢z = ∈-++⁺ˡ (∈-++⁺ʳ (fv A) (lemma∈-≢ x*M y≢z)) 

  βpreserves# : Preserves# _▹β_
  βpreserves# = antipres*⇒pres# {_▹β_} β⁻¹preserves*
  
  compat∙β : Compat∙ _▹β_
  compat∙β .{(λ[ x ∶ A ] M) · N} {_} {σ} (β {x} {M} {N} {A}) = (M ∙ σ ‚ x := v y) ∙ ι ‚ y := (N ∙ σ) , β , aux
    where
    open PreR ≈-preorder∼
    y : 𝒱
    y = X (σ , fv M - x)
    aux : (M ∙ σ ‚ x := v y) ∙ ι ‚ y := (N ∙ σ) ∼α (M ∙ ι ‚ x := N) ∙ σ
    aux = begin
      (M ∙ σ ‚ x := v y) ∙ ι ‚ y := (N ∙ σ)
      ∼⟨ composRenUnary {x} {y} {σ} {M} (Xfresh σ (fv M - x)) ⟩
      M ∙ σ ‚ x := (N ∙ σ)
      ≈⟨ subDistribUpd {M} {N} {σ} {x} ⟩
      (M ∙ ι ‚ x := N) ∙ σ
      ∎

  commutβα : CommAlpha _▹β_
  commutβα .{(λ[ x ∶ A ] M) · N} .{(λ[ x′ ∶ A′ ] M′) · N′} (∼· {_} {_} {N} .{N′} (∼λ {x} .{x′} {y} {A} .{A′} {M} .{M′} A∼A′ y#λxM y#λx′M′ M[y/x]∼M′[y/x′]) N∼N′) (β {x′} {M′} {N′} {A′}) =
    M ∙ ι ‚ x := N , β , aux
    where
    open PreR ≈-preorder∼
    aux : M ∙ ι ‚ x := N ∼α M′ ∙ ι ‚ x′ := N′
    aux = begin
      M ∙ ι ‚ x := N
      ≈⟨ composRenUpd {x} {y} {M} y#λxM ⟩
      (M ∙ ι ‚ x := v y) ∙ ι ‚ y := N
      ∼⟨ lemma-subst (≡⇒∼ M[y/x]∼M′[y/x′]) (lemma≺+∼α⇂ {y} lemmaι∼α⇂ N∼N′) ⟩
      (M′ ∙ ι ‚ x′ := v y) ∙ ι ‚ y := N′
      ≈⟨ sym (composRenUpd {x′} {y} {M′} y#λx′M′) ⟩
      M′ ∙ ι ‚ x′ := N′
      ∎