open import Relation.Binary hiding (Rel)
open import Relation.Binary.PropositionalEquality as PropEq using (_≡_; _≢_; refl; sym; cong; cong₂; trans; setoid)
open import Stoughton.Var

module Stoughton.SubstitutionLemmas (𝒞 : Set) {𝒱 : Set} (var : Enum 𝒱) where

  private 
    _≟_ = Enum._≟_ var

  open import Stoughton.Chi (Enum.encode var) (Enum.decode var) (Enum.inverse var)
  open import Stoughton.Syntax 𝒞 𝒱 _≟_
  open import Stoughton.Substitution 𝒞 var
  open import Stoughton.Alpha 𝒞 var

  open import Relation.Binary using (Decidable)
  open import Relation.Binary.PropositionalEquality using (_≡_; subst)

  open import Data.Empty
  open import Data.Nat hiding (_*_; _≟_)
  open import Data.Nat.Properties hiding (_≟_)
  open import Relation.Nullary
  open import Function renaming (_∘_ to _∘f_)
  open import Data.Product renaming (Σ to Σₓ)
  
  open PropEq.≡-Reasoning renaming (begin_ to begin≡_;_∎ to _◻)
  open import Data.List as List hiding (any) renaming (length to length') 
  open import Data.List.Properties
  open import Data.List.Relation.Unary.Any as Any hiding (map)
  open import Data.List.Membership.Propositional
  open import Data.List.Membership.Propositional.Properties

  ++-injʳ : {xs xs' ys ys' : List 𝒱} → xs ≡ xs' → xs ++ ys ≡ xs' ++ ys' → ys ≡ ys'
  ++-injʳ {[]} .{[]} {ys} {ys'} refl ys=ys' = ys=ys'
  ++-injʳ {x ∷ xs} .{x ∷ xs} {ys} {ys'} refl x:xs++ys=x:xs++ys' = ++-injʳ refl (∷-injectiveʳ x:xs++ys=x:xs++ys')

  fvλ∼ : ∀ {x x' A A' M M'} → λ[ x ∶ A ] M ∼α λ[ x' ∶ A' ] M' → fv M - x ≡ fv M' - x'
  fvλ∼ e@(∼λ A∼A' _ _ _) = ++-injʳ (M∼M'→fvM≡fvM' A∼A') (M∼M'→fvM≡fvM' e)

  fvΠ∼ : ∀ {x x' A A' M M'} → Π[ x ∶ A ] M ∼α Π[ x' ∶ A' ] M' → fv M - x ≡ fv M' - x'
  fvΠ∼ e@(∼Π A∼A' _ _ _) = ++-injʳ (M∼M'→fvM≡fvM' A∼A') (M∼M'→fvM≡fvM' e)

  compatSubAlpha : ∀ {M M' σ} → M ∼α M' → M ∙ σ ≡ M' ∙ σ
  compatSubAlpha ∼c              = refl
  compatSubAlpha ∼v              = refl
  compatSubAlpha (∼· M∼M' N∼N') = cong₂ _·_ (compatSubAlpha M∼M') (compatSubAlpha N∼N')
  compatSubAlpha {σ = σ} λxAM∼λx'A'M'@(∼λ {x} {x'} {z} {A} {A'} {M} {M'} A∼A' z#λxM z#λx'M' Mι≺+xz∼M'ι≺+x'z) rewrite fvλ∼ λxAM∼λx'A'M' =
    cong₂ (λ[_∶_]_ y) (compatSubAlpha A∼A') Mσx,y=M'σx'y
    where
    y : 𝒱
    y = X(σ , fv M' - x')
    Mσx,y=M'σx'y : M ∙ (σ ‚ x := v y) ≡ M' ∙ (σ ‚ x' := v y)
    Mσx,y=M'σx'y =
      begin≡
      M ∙ (σ ‚ x := v y)
      ≡⟨ composRenUpd {x} {z} {M} z#λxM ⟩
      (M ∙ (ι ‚ x := v z)) ∙ (σ ‚ z := v y)
      ≡⟨ cong₂ _∙_ Mι≺+xz∼M'ι≺+x'z refl ⟩
      (M' ∙ (ι ‚ x' := v z)) ∙ (σ ‚ z := v y)
      ≡⟨ sym (composRenUpd {x'} {z} {M'} z#λx'M') ⟩
      M' ∙ (σ ‚ x' := v y)
      ◻    
  compatSubAlpha {σ = σ} ΠxAM∼Πx'A'M'@(∼Π {x} {x'} {z} {A} {A'} {M} {M'} A∼A' z#λxM z#λx'M' Mι≺+xz∼M'ι≺+x'z) rewrite fvΠ∼ ΠxAM∼Πx'A'M' =
    cong₂ (Π[_∶_]_ y) (compatSubAlpha A∼A') Mσx,y=M'σx'y
    where
    y : 𝒱
    y = X(σ , fv M' - x')
    Mσx,y=M'σx'y : M ∙ (σ ‚ x := v y) ≡ M' ∙ (σ ‚ x' := v y)
    Mσx,y=M'σx'y =
      begin≡
      M ∙ (σ ‚ x := v y)
      ≡⟨ composRenUpd {x} {z} {M} z#λxM ⟩
      (M ∙ (ι ‚ x := v z)) ∙ (σ ‚ z := v y)
      ≡⟨ cong₂ _∙_ Mι≺+xz∼M'ι≺+x'z refl ⟩
      (M' ∙ (ι ‚ x' := v z)) ∙ (σ ‚ z := v y)
      ≡⟨ sym (composRenUpd {x'} {z} {M'} z#λx'M') ⟩
      M' ∙ (σ ‚ x' := v y)
      ◻       

  ∼σ : Symmetric _∼α_
  ∼σ {M} {N} M∼N 
    = iotaAlpha 
            (sym (compatSubAlpha M∼N))

  ∼τ : Transitive _∼α_
  ∼τ {M} {N} {P} M∼N N∼P 
    = iotaAlpha 
           (trans (compatSubAlpha M∼N) 
                  (compatSubAlpha N∼P))

  ≈-preorder∼ : Preorder _ _ _
  ≈-preorder∼ =  
      record { 
        Carrier = Λ;
        _≈_ = _≡_;
        _∼_ = _∼α_;
        isPreorder =  record {
          isEquivalence = Relation.Binary.Setoid.isEquivalence (setoid Λ) ;
          reflexive = λ { {M} {.M} refl → ∼ρ {M}};
          trans = ∼τ } }

  import Relation.Binary.Reasoning.Preorder as PreR

  lemma-σ⇂ : {M : List 𝒱}{σ σ' : Sub} → σ ∼α σ' ⇂ M → ((ι ⊙ σ) , M) ≅⇂ₗ ((ι ⊙ σ') , M)
  lemma-σ⇂ σ∼σ'⇂M  = ∼*ρ , (λ x xfreeM → compatSubAlpha (σ∼σ'⇂M  x xfreeM))

  subAlpha : ∀ {M σ σ'} → σ ∼α σ' ⇂ fv M → M ∙ σ ∼α M ∙ σ'
  subAlpha {M} {σ} {σ'} σ∼ασ'⇂M = iotaAlpha aux
    where
      aux : (M ∙ σ) ∙ ι ≡ (M ∙ σ') ∙ ι
      aux = begin≡
        (M ∙ σ) ∙ ι
        ≡⟨ subComp {M} {σ} {ι} ⟩
        M ∙ (ι ⊙ σ)
        ≡⟨  subEqRes {M} {ι ⊙ σ} {ι ⊙ σ'} (lemma-σ⇂ σ∼ασ'⇂M) ⟩
        M ∙ (ι ⊙ σ')
        ≡⟨ sym (subComp {M} {σ'} {ι}) ⟩
        (M ∙ σ') ∙ ι                                               ◻ 

  lemma-subst : ∀ {M M' σ σ'} → M ∼α M' → σ ∼α σ' ⇂ fv M → M ∙ σ ∼α M' ∙ σ'
  lemma-subst {M} {M'} {σ} {σ'} M∼M' σ∼σ'⇂M 
    =  begin
         M ∙ σ
         ∼⟨ subAlpha {M} σ∼σ'⇂M ⟩
         M ∙ σ'
         ≈⟨ compatSubAlpha M∼M'  ⟩
         M' ∙ σ'
       ∎
    where open PreR ≈-preorder∼ 

  lemma∙ι : ∀ {M} → M ∼α M ∙ ι
  lemma∙ι {M} = iotaAlpha ( begin≡
                                      M ∙ ι
                                    ≡⟨ subEqRes {M} {ι} {ι ⊙ ι} (∼*ρ , (λ _ _ → refl) ) ⟩
                                      M ∙ (ι ⊙ ι)
                                    ≡⟨  sym (subComp {M} {ι} {ι}) ⟩
                                      (M ∙ ι) ∙ ι
                                    ◻) 

  lemma∼λ : {M N A B : Λ}{x : 𝒱} → A ∼α B → M ∼α N → λ[ x ∶ A ] M ∼α λ[ x ∶ B ] N
  lemma∼λ {M} {N} {A} {B} {x} A∼B M∼N = ∼λ A∼B (∉- (fv M)) (∉- (fv N)) lemma∼ƛaux
    where
    lemma∼ƛaux : M ∙ ι ‚ x := v x ≡ N ∙ ι ‚ x := v x
    lemma∼ƛaux = compatSubAlpha {σ = ι ‚ x := v x} M∼N 

  lemma∼Π : {M N A B : Λ}{x : 𝒱} → A ∼α B → M ∼α N → Π[ x ∶ A ] M ∼α Π[ x ∶ B ] N
  lemma∼Π {M} {N} {A} {B} {x} A∼B M∼N = ∼Π A∼B (∉- (fv M)) (∉- (fv N)) lemma∼ƛaux
    where
    lemma∼ƛaux : M ∙ ι ‚ x := v x ≡ N ∙ ι ‚ x := v x
    lemma∼ƛaux = compatSubAlpha {σ = ι ‚ x := v x} M∼N 

  infix 1 _∼ασ_
  _∼ασ_ : Sub → Sub → Set
  σ ∼ασ σ' = (x : 𝒱) → σ x ∼α σ' x

  lemma∼ασ : {σ σ' : Sub}{M : List 𝒱} → σ ∼ασ σ' → σ ∼α σ' ⇂ M
  lemma∼ασ σ∼ασ' x x*M = σ∼ασ' x

  lemmaι∘σ : {σ : Sub} → ι ⊙ σ ∼ασ σ 
  lemmaι∘σ {σ} x = begin
                     σ x ∙ ι
                   ∼⟨ ∼σ (lemma∙ι) ⟩
                     σ x
                   ∎
                   where open PreR ≈-preorder∼ 

  lemma∼≺+ : {x : 𝒱}{N : Λ}{σ σ' : Sub} → σ ∼ασ σ' → σ ‚ x := N ∼ασ σ' ‚ x := N
  lemma∼≺+ {x} σ∼σ' y with x ≟ y
  ... | yes  _ = ∼ρ
  ... | no   _ = σ∼σ' y

  prop8 : {x y : 𝒱}{σ : Sub}{M N : Λ} → y #⇂ (σ , fv M - x) → (ι ‚ y := N ⊙ σ ‚ x := v y) ∼α σ ‚ x := N ⇂ fv M
  prop8 {x} {y} {σ} {M} {N} y#⇂λxM z z*M =
                  begin
                    (ι ‚ y := N ⊙ σ ‚ x := v y) z
                  ≈⟨ lemmaσ∘≺+ M N σ ι x y y#⇂λxM z z*M ⟩
                    ((ι ⊙ σ) ‚ x := N) z
                  ∼⟨ (lemma∼≺+ {x} {N} (lemmaι∘σ {σ})) z ⟩
                    (σ ‚ x := N) z
                  ∎
                  where open PreR ≈-preorder∼ 

  composRenUnary : ∀ {x y σ M N} → y #⇂ (σ , fv M - x) → (M ∙ (σ ‚ x := v y)) [ y := N ] ∼α M ∙ (σ ‚ x := N)
  composRenUnary {x} {y} {σ} {M} {N} y#⇂σ,λxM 
    =  begin
         (M ∙ σ ‚ x := v y) ∙ ι ‚ y := N
       ≈⟨ subComp {M} ⟩
         M ∙ (ι ‚ y := N ⊙ σ ‚ x := v y)
       ∼⟨ subAlpha {M} (prop8 {x} {M = M} y#⇂σ,λxM) ⟩
         M ∙ σ ‚ x := N
       ∎
       where open PreR ≈-preorder∼ 

  corollary4-2 : {x y : 𝒱}{M A : Λ}{σ : Sub} → y #⇂ (σ , fv M - x) → λ[ x ∶ A ] M ∙ σ ∼α λ[ y ∶ A ∙ σ ] (M ∙ σ ‚ x := v y)
  corollary4-2 {x} {y} {M} {A} {σ} y#⇂σ,λxM =
    begin
    λ[ x ∶ A ] M ∙ σ
    ≈⟨ refl ⟩
    λ[ z ∶ A ∙ σ ](M ∙ σ ‚ x := v z)
    ∼⟨ ∼λ ∼ρ w#ƛzM∙σ≺+x,z w#ƛyM∙σ≺+x,y e ⟩
    λ[ y ∶ A ∙ σ ](M ∙ σ ‚ x := v y)
    ∎
    where
    z w : 𝒱
    z = X (σ , fv M - x)
    w = X' ((fv (M ∙ σ ‚ x := v z) - z) ++ (fv (M ∙ σ ‚ x := v y)) - y)
    z#⇂σ,λxM : z #⇂ (σ , fv M - x)
    z#⇂σ,λxM = Xfresh σ (fv M - x)
    aux : ∀ u → u * M → (ι ‚ z := v w ⊙ σ ‚ x := v z) u ≡ (ι ‚ y := v w ⊙ σ ‚ x := v y) u
    aux u u*M with x ≟ u
    aux .x x*M | yes refl with z ≟ z
    aux .x x*M | yes refl | yes refl with y ≟ y
    aux .x x*M | yes refl | yes refl | yes refl = refl
    aux .x x*M | yes refl | yes refl | no y≢y = ⊥-elim (y≢y refl)
    aux .x x*M | yes refl | no z≢z = ⊥-elim (z≢z refl)
    aux u u*M  | no x≢u = trans aux1 (sym aux2)
      where
      aux1 : (σ u) [ z := v w ] ≡ σ u ∙ ι 
      aux1 = updFresh {z} {σ u} (z#⇂σ,λxM u (proj₂ delList (sym≢ x≢u , u*M)))
      aux2 : (σ u) [ y := v w ] ≡ σ u ∙ ι
      aux2 = updFresh {y} {σ u} (y#⇂σ,λxM u (proj₂ delList (sym≢ x≢u , u*M)))
    e : M ∙ σ ‚ x := v z ∙ ι ‚ z := v w ≡ M ∙ σ ‚ x := v y ∙ ι ‚ y := v w
    e = 
      begin≡ 
      M ∙ σ ‚ x := v z ∙ ι ‚ z := v w
      ≡⟨ subComp {M} {σ ‚ x := v z} {ι ‚ z := v w} ⟩
      M ∙ ι ‚ z := v w ⊙ σ ‚ x := v z
      ≡⟨ subEqRes {M} (∼*ρ , aux) ⟩
      M ∙ ι ‚ y := v w ⊙ σ ‚ x := v y      
      ≡⟨ sym (subComp {M} {σ ‚ x := v y} {ι ‚ y := v w}) ⟩
      M ∙ σ ‚ x := v y ∙ ι ‚ y := v w
      ◻
    w#ƛzM∙σ≺+x,z : w ∉ fv (M ∙ σ ‚ x := v z) - z
    w#ƛzM∙σ≺+x,z = c∉xs++ys→c∉xs  {w} {fv ((M ∙ σ ‚ x := v z)) - z} (Xpfresh (((fv (M ∙ σ ‚ x := v z)) - z) ++ (fv (M ∙ σ ‚ x := v y) - y)))
    w#ƛyM∙σ≺+x,y : w ∉ fv (M ∙ σ ‚ x := v y) - y
    w#ƛyM∙σ≺+x,y = c∉xs++ys→c∉ys  {w} {fv ((M ∙ σ ‚ x := v z)) - z} {fv ((M ∙ σ ‚ x := v y)) - y} (Xpfresh ((fv (M ∙ σ ‚ x := v z) - z) ++ (fv (M ∙ σ ‚ x := v y) - y)))
    open PreR ≈-preorder∼ 

  corollary4-2Π : {x y : 𝒱}{M A : Λ}{σ : Sub} → y #⇂ (σ , fv M - x) → Π[ x ∶ A ] M ∙ σ ∼α Π[ y ∶ A ∙ σ ] (M ∙ σ ‚ x := v y)
  corollary4-2Π {x} {y} {M} {A} {σ} y#⇂σ,ΠxM = 
    begin
    Π[ x ∶ A ] M ∙ σ
    ≈⟨ refl ⟩
    Π[ z ∶ A ∙ σ ](M ∙ σ ‚ x := v z)
    ∼⟨ ∼Π ∼ρ w#ƛzM∙σ≺+x,z w#ƛyM∙σ≺+x,y e ⟩
    Π[ y ∶ A ∙ σ ](M ∙ σ ‚ x := v y)
    ∎
    where
    z w : 𝒱
    z = X (σ , fv M - x)
    w = X' ((fv (M ∙ σ ‚ x := v z) - z) ++ (fv (M ∙ σ ‚ x := v y)) - y)
    z#⇂σ,ΠxM : z #⇂ (σ , fv M - x)
    z#⇂σ,ΠxM = Xfresh σ (fv M - x)
    aux : ∀ u → u * M → (ι ‚ z := v w ⊙ σ ‚ x := v z) u ≡ (ι ‚ y := v w ⊙ σ ‚ x := v y) u
    aux u u*M with x ≟ u
    aux .x x*M | yes refl with z ≟ z
    aux .x x*M | yes refl | yes refl with y ≟ y
    aux .x x*M | yes refl | yes refl | yes refl = refl
    aux .x x*M | yes refl | yes refl | no y≢y = ⊥-elim (y≢y refl)
    aux .x x*M | yes refl | no z≢z = ⊥-elim (z≢z refl)
    aux u u*M  | no x≢u = trans aux1 (sym aux2)
      where
      aux1 : (σ u) [ z := v w ] ≡ σ u ∙ ι 
      aux1 = updFresh {z} {σ u} (z#⇂σ,ΠxM u (proj₂ delList (sym≢ x≢u , u*M)))
      aux2 : (σ u) [ y := v w ] ≡ σ u ∙ ι
      aux2 = updFresh {y} {σ u} (y#⇂σ,ΠxM u (proj₂ delList (sym≢ x≢u , u*M)))
    e : M ∙ σ ‚ x := v z ∙ ι ‚ z := v w ≡ M ∙ σ ‚ x := v y ∙ ι ‚ y := v w
    e = 
      begin≡ 
      M ∙ σ ‚ x := v z ∙ ι ‚ z := v w
      ≡⟨ subComp {M} {σ ‚ x := v z} {ι ‚ z := v w} ⟩
      M ∙ ι ‚ z := v w ⊙ σ ‚ x := v z
      ≡⟨ subEqRes {M} (∼*ρ , aux) ⟩
      M ∙ ι ‚ y := v w ⊙ σ ‚ x := v y      
      ≡⟨ sym (subComp {M} {σ ‚ x := v y} {ι ‚ y := v w}) ⟩
      M ∙ σ ‚ x := v y ∙ ι ‚ y := v w
      ◻
    w#ƛzM∙σ≺+x,z : w ∉ fv (M ∙ σ ‚ x := v z) - z
    w#ƛzM∙σ≺+x,z = c∉xs++ys→c∉xs  {w} {fv ((M ∙ σ ‚ x := v z)) - z} (Xpfresh (((fv (M ∙ σ ‚ x := v z)) - z) ++ (fv (M ∙ σ ‚ x := v y) - y)))
    w#ƛyM∙σ≺+x,y : w ∉ fv (M ∙ σ ‚ x := v y) - y
    w#ƛyM∙σ≺+x,y = c∉xs++ys→c∉ys  {w} {fv ((M ∙ σ ‚ x := v z)) - z} {fv ((M ∙ σ ‚ x := v y)) - y} (Xpfresh ((fv (M ∙ σ ‚ x := v z) - z) ++ (fv (M ∙ σ ‚ x := v y) - y)))
    open PreR ≈-preorder∼ 
      
  corollary4-2' : {x y : 𝒱}{M A : Λ} → y ∉ fv M - x → λ[ x ∶ A ] M ∼α λ[ y ∶ A ] (M ∙ ι ‚ x := v y)
  corollary4-2' {x} {y} {M} {A} y#λxM
    =  begin
          λ[ x ∶ A ] M
        ∼⟨ lemma∙ι ⟩
          λ[ x ∶ A ] M ∙ ι
        ∼⟨ corollary4-2 {x} {y} {M} {A} (lemma#→ι#⇂ {y} {x} {fv M} y#λxM)  ⟩
          λ[ y ∶ A ∙ ι ](M ∙ ι ‚ x := v y)
        ∼⟨ lemma∼λ (∼σ lemma∙ι) ∼ρ ⟩
          λ[ y ∶ A ](M ∙ ι ‚ x := v y)
        ∎
        where open PreR ≈-preorder∼

  corollary4-2'Π : {x y : 𝒱}{M A : Λ} → y ∉ fv M - x → Π[ x ∶ A ] M ∼α Π[ y ∶ A ] (M ∙ ι ‚ x := v y)
  corollary4-2'Π {x} {y} {M} {A} y#ΠxM
    =  begin
          Π[ x ∶ A ] M
        ∼⟨ lemma∙ι ⟩
          Π[ x ∶ A ] M ∙ ι
        ∼⟨ corollary4-2Π {x} {y} {M} {A} (lemma#→ι#⇂ {y} {x} {fv M} y#ΠxM)  ⟩
          Π[ y ∶ A ∙ ι ](M ∙ ι ‚ x := v y)
        ∼⟨ lemma∼Π (∼σ lemma∙ι) ∼ρ ⟩
          Π[ y ∶ A ](M ∙ ι ‚ x := v y)
        ∎
        where open PreR ≈-preorder∼

  iotaIdem : ∀ {x y M} → (ι ‚ x := v y) ≅ ι ⊙ ι ‚ x := v y ⇂ fv M
  iotaIdem {x} {y} {M} = ∼*ρ , aux
    where
    aux : ∀ z → z ∈ fv M → (ι ‚ x := v y) z ≡ (ι ⊙ ι ‚ x := v y) z
    aux z _ with x ≟ z
    ... | yes _ = refl
    ... | no _  = refl

  eqSubSym : ∀ {σ σ' M} → σ ≅ σ' ⇂ fv M → σ' ≅ σ ⇂ fv M
  eqSubSym (_ , h) = ∼*ρ , λ x x∈fvM → sym (h x x∈fvM)

  lemma∼⇒≡ : ∀ {M M' x x' y} → M [ x := v y ] ∼α M' [ x' := v y ] → M [ x := v y ] ≡ M' [ x' := v y ]
  lemma∼⇒≡ {M} {M'} {x} {x'} {y} M[x=y]∼M'[x'=y] = begin≡
    M [ x := v y ]
    ≡⟨ subEqRes {M} (iotaIdem {x} {y} {M}) ⟩
    M ∙ ι ⊙ ι ‚ x := v y
    ≡⟨ sym (subComp {M}) ⟩
    M [ x := v y ] ∙ ι
    ≡⟨ compatSubAlpha M[x=y]∼M'[x'=y] ⟩
    M' [ x' := v y ] ∙ ι
    ≡⟨ subComp {M'} ⟩                
    M' ∙ ι ⊙ ι ‚ x' := v y
    ≡⟨ subEqRes {M'} (eqSubSym {M = M'} (iotaIdem {x'} {y} {M'}))  ⟩
    M' [ x' := v y ]
    ◻

  soundAlpha : ∀ {M N} → M ∼α N → M ∼αₛ N
  soundAlpha M∼N = iotaAlphaSt (compatSubAlpha M∼N)

  completeAlpha : ∀ {M N} → M ∼αₛ N → M ∼α N
  completeAlpha ∼c = ∼c
  completeAlpha ∼v = ∼v
  completeAlpha (∼· e f) = ∼· (completeAlpha e) (completeAlpha f)
  completeAlpha (∼λ {M} {M'} {x} {x'} {y} {A} {A'} e f g h) = ∼λ (completeAlpha e) f g (lemma∼⇒≡ {M} {M'} {x} {x'} {y} (completeAlpha h))
  completeAlpha (∼Π {M} {M'} {x} {x'} {y} {A} {A'} e f g h) = ∼Π (completeAlpha e) f g (lemma∼⇒≡ {M} {M'} {x} {x'} {y} (completeAlpha h))