open import Data.Empty
open import Data.Product
open import Relation.Binary.Construct.Closure.ReflexiveTransitive hiding (_⋆)
open import Relation.Binary.Construct.Closure.Equivalence as Eq
open import Relation.Binary.Construct.Union
open import Level
open import Relation.Binary
open import Relation.Nullary
open import Relation.Binary.Construct.Composition
import Relation.Binary.Reasoning.Preorder as PreR
open import Relation.Binary.PropositionalEquality as PropEq hiding (trans)
open import Data.List
open import Data.List.Membership.Propositional
open import Data.Sum
open import Data.List.Relation.Unary.Any 

open import Stoughton.Var

module ParallelReduction.Properties (𝒞 : Set) {𝒱 : Set} (var : Enum 𝒱) where

  open import Stoughton.Syntax 𝒞 𝒱 (Enum._≟_ var)  
  open import Stoughton.Alpha 𝒞 var
  open import Stoughton.Chi (Enum.encode var) (Enum.decode var) (Enum.inverse var)
  open import Stoughton.Substitution 𝒞 var      
  open import Stoughton.SubstitutionLemmas 𝒞 var
  open import Beta 𝒞 var  
  open import BetaReduction 𝒞 var
  open import BetaConversion 𝒞 var  
  open import Relation 𝒞 var
  open import ParallelReduction 𝒞 var

  private
    _≟_ = Enum._≟_ var
    
  infixl 3 _⇉ₛ_
  _⇉ₛ_ : Sub → Sub → Set
  σ ⇉ₛ σ' = ∀ x → σ x ⇉ σ' x

  lemma⇉s : {σ σ' : Sub}(x y : 𝒱) → σ ⇉ₛ σ' → σ ‚ x := v y ⇉ₛ σ' ‚ x := v y
  lemma⇉s {σ} {σ'} x y σ⇉σ' z with x ≟ z 
  ... | yes  _ = ⇉v
  ... | no   _ = σ⇉σ' z

  lemma⇉# : {x : 𝒱}{M N : Λ} → x # M → M ⇉ N → x # N
  lemma⇉# x#M M⇉N = lemma→α*# x#M (manyStepBetaContainsPar M⇉N) 

  lemma⇉* : {x : 𝒱}{M N : Λ} → x * N → M ⇉ N → x * M
  lemma⇉* x*N M⇉N = lemma→α** x*N (manyStepBetaContainsPar M⇉N) 

  lemma⇉#⇂ : {x y : 𝒱}{M M' : Λ}{σ σ' : Sub} → M ⇉ M' → σ ⇉ₛ σ' → y #⇂ (σ , fv M - x) → y #⇂ (σ' , fv M' - x)
  lemma⇉#⇂ {x} {y} {M} {M'} {σ} {σ'} M⇉M' σ⇉σ' y#σ⇂fvM-x z z∈fvM'-x = y#σ'z
    where
    z≢x : z ≢ x
    z≢x = ∈-→≢ {xs = fv M'} z∈fvM'-x
    z∈fvM' : z ∈ fv M' 
    z∈fvM' = ∈-→∈ z∈fvM'-x
    z∈fvM : z ∈ fv M
    z∈fvM = lemma⇉* z∈fvM' M⇉M' 
    z∈fvM-x : z ∈ fv M - x
    z∈fvM-x = lemma∈-≢ z∈fvM (sym≢ z≢x)
    y#σz : y # σ z
    y#σz = y#σ⇂fvM-x z z∈fvM-x
    y#σ'z : y # σ' z
    y#σ'z = lemma⇉# y#σz (σ⇉σ' z)

  compatParSub : ∀ {M M' σ σ'} → M ⇉ M' → σ ⇉ₛ σ' → ∃ λ N → M ∙ σ ⇉ N × N ∼α M' ∙ σ'
  compatParSub {σ' = σ'} (⇉v {x}) σ⇉σ' = σ' x , σ⇉σ' x , ∼ρ
  compatParSub (⇉c {k}) _ = c k , ⇉c , ∼c
  compatParSub {λ[ .x ∶ .A ] M} {λ[ .x ∶ .A' ] M'} {σ} {σ'} (⇉λ {x} {A} {A'} A⇉A' M⇉M') σ⇉σ'
    with compatParSub A⇉A' σ⇉σ' | compatParSub M⇉M' (lemma⇉s x (X (σ , fv M - x)) σ⇉σ')
  ... | B , Aσ⇉B , B∼A'σ' | N , Mσ,x=y⇉N , N∼M'σ',x=y =
    λ[ y ∶ B ] N , ⇉λ {y} Aσ⇉B Mσ,x=y⇉N , ∼σ (∼τ λ[x:A']M'σ'∼λ[y:A'σ']M'σ',x=y λ[y:A'σ']M'σ',x=y∼λ[y:B]N)
    where
    y : 𝒱 
    y = X (σ , fv M - x)
    y#⇂σ,ƛxM : y #⇂ (σ , fv M - x)
    y#⇂σ,ƛxM = Xfresh σ (fv M - x)
    y#⇂σ',ƛxM' : y #⇂ (σ' , fv M' - x)
    y#⇂σ',ƛxM' = lemma⇉#⇂ {x} {y} {M} M⇉M' σ⇉σ' y#⇂σ,ƛxM
    λ[x:A']M'σ'∼λ[y:A'σ']M'σ',x=y : (λ[ x ∶ A' ] M') ∙ σ' ∼α λ[ y ∶ A' ∙ σ' ] (M' ∙ σ' ‚ x := v y)
    λ[x:A']M'σ'∼λ[y:A'σ']M'σ',x=y = corollary4-2  {x} {y} {M'} {A'} y#⇂σ',ƛxM'
    λ[y:A'σ']M'σ',x=y∼λ[y:B]N : λ[ y ∶ A' ∙ σ' ] (M' ∙ σ' ‚ x := v y) ∼α λ[ y ∶ B ] N
    λ[y:A'σ']M'σ',x=y∼λ[y:B]N = lemma∼λ (∼σ B∼A'σ') (∼σ N∼M'σ',x=y)      
  compatParSub {Π[ .x ∶ .A ] M} {Π[ .x ∶ .A' ] M'} {σ} {σ'} (⇉Π {x} {A} {A'} A⇉A' M⇉M') σ⇉σ'
    with compatParSub A⇉A' σ⇉σ' | compatParSub M⇉M' (lemma⇉s x (X (σ , fv M - x)) σ⇉σ')
  ... | B , Aσ⇉B , B∼A'σ' | N , Mσ,x=y⇉N , N∼M'σ',x=y =
    Π[ y ∶ B ] N , ⇉Π {y} Aσ⇉B Mσ,x=y⇉N , ∼σ (∼τ Π[x:A']M'σ'∼Π[y:A'σ']M'σ',x=y Π[y:A'σ']M'σ',x=y∼Π[y:B]N)
    where
    y : 𝒱 
    y = X (σ , fv M - x)
    y#⇂σ,ƛxM : y #⇂ (σ , fv M - x)
    y#⇂σ,ƛxM = Xfresh σ (fv M - x)
    y#⇂σ',ƛxM' : y #⇂ (σ' , fv M' - x)
    y#⇂σ',ƛxM' = lemma⇉#⇂ {x} {y} {M} M⇉M' σ⇉σ' y#⇂σ,ƛxM
    Π[x:A']M'σ'∼Π[y:A'σ']M'σ',x=y : (Π[ x ∶ A' ] M') ∙ σ' ∼α Π[ y ∶ A' ∙ σ' ] (M' ∙ σ' ‚ x := v y)
    Π[x:A']M'σ'∼Π[y:A'σ']M'σ',x=y = corollary4-2Π  {x} {y} {M'} {A'} y#⇂σ',ƛxM'
    Π[y:A'σ']M'σ',x=y∼Π[y:B]N : Π[ y ∶ A' ∙ σ' ] (M' ∙ σ' ‚ x := v y) ∼α Π[ y ∶ B ] N
    Π[y:A'σ']M'σ',x=y∼Π[y:B]N = lemma∼Π (∼σ B∼A'σ') (∼σ N∼M'σ',x=y)     
  compatParSub (⇉· M⇉M' N⇉N') σ⇉σ' with compatParSub M⇉M' σ⇉σ' | compatParSub N⇉N' σ⇉σ'
  ... | P' , M⇉P , P∼M'σ' | Q' , N⇉P , P∼N'σ' = (P' · Q') , ⇉· M⇉P N⇉P , ∼· P∼M'σ' P∼N'σ'
  compatParSub {.((λ[ x ∶ A ] M) · N)} {.(M' ∙ ι ‚ x := N')} {σ} {σ'} (⇉β {x} {A} {M} {M'} {N} {N'} M⇉M' N⇉N') σ⇉σ'
    with compatParSub M⇉M' (lemma⇉s x (X (σ , fv M - x)) σ⇉σ') | compatParSub N⇉N' σ⇉σ'
  ... | P , Mσ,x=y⇉P , P∼M'σ,x=y | Q , Nσ⇉Q , Q∼N'σ = P [ y := Q ] , ⇉β {y} {M' = P} {N' = Q} Mσ,x=y⇉P Nσ⇉Q , ∼τ lemma∼2 lemma∼
    where    
    y : 𝒱
    y = X (σ , fv M - x)
    y#⇂σ,ƛxM : y #⇂ (σ , fv M - x)
    y#⇂σ,ƛxM = Xfresh σ (fv M - x)
    open PreR ≈-preorder∼
    lemma∼ : (M' ∙ σ' ‚ x := v y) ∙ (ι ‚ y := (N' ∙ σ')) ∼α (M' ∙ ι ‚ x := N') ∙ σ'
    lemma∼ = 
      begin
      (M' ∙ σ' ‚ x := v y) ∙ (ι ‚ y := (N' ∙ σ'))
      ∼⟨ composRenUnary {x} {y} {σ'} {M'} (lemma⇉#⇂ M⇉M' σ⇉σ' y#⇂σ,ƛxM) ⟩
      (M' ∙ (σ' ‚ x := (N' ∙ σ')))
      ≈⟨ subDistribUpd {M'} {N'} {σ'} {x} ⟩
      (M' ∙ ι ‚ x := N') ∙ σ'
      ∎        
    lemma∼2 : P ∙ (ι ‚ y := Q) ∼α (M' ∙ σ' ‚ x := v y) ∙ (ι ‚ y := (N' ∙ σ'))
    lemma∼2 = lemma-subst P∼M'σ,x=y (lemma≺+∼α⇂ {x = y} lemmaι∼α⇂ Q∼N'σ)

  lemma∼α⇂ρ : ∀ {σ M} → σ ∼α σ ⇂ M
  lemma∼α⇂ρ x _ =  ∼ρ

  lemma⇉ₛρ : ∀ {σ} → σ ⇉ₛ σ
  lemma⇉ₛρ {σ} x = ⇉ρ

  lemma⇉ₛ≺+ : (x : 𝒱) {M N : Λ} {σ σ' : Sub} → M ⇉ N → σ ⇉ₛ σ' → σ ‚ x := M ⇉ₛ σ' ‚ x := N
  lemma⇉ₛ≺+ x {M} {N} {σ} {σ'} red red' y with x ≟ y
  lemma⇉ₛ≺+ x red red' .x | yes refl = red
  ... | no ¬p = red' y

  corollary⇉ₛ≺+ : (x : 𝒱) {M N : Λ} → M ⇉ N → ι ‚ x := M ⇉ₛ ι ‚ x := N
  corollary⇉ₛ≺+ x M⇉N = lemma⇉ₛ≺+ x M⇉N lemma⇉ₛρ

  lemma⇉#- : {x y : 𝒱}{M M' : Λ} → M ⇉ M' → y ∉ fv M - x → y ∉ fv M' - x
  lemma⇉#- {x} {y} {M} {N} M⇉N y∉fvM-x = lemma→β*#- M→β*N y∉fvM-x
    where
    M→β*N : M →β*₀ N
    M→β*N = manyStepBetaContainsPar M⇉N
    
  parComm : CommAlpha _⇉_ 
  parComm ∼c (⇉c {k}) = c k , ⇉c , ∼c
  parComm ∼v (⇉v {x}) = v x , ⇉v , ∼v
  parComm λ[x:A]M∼λ[x':A']M'@(∼λ {x}{x'}{y}{A}{A'}{M}{M'} A∼A' y∉fvM-x y∉fvM'-x' M[x=y]=M'[x'=y]) (⇉λ {_}{_}{_}{_}{M″} A'⇉A″ M'⇉M″)
    with compatParSub M'⇉M″ (lemma⇉ₛ≺+ x' {σ = ι} ⇉v lemma⇉ₛρ)
  ... | N , M'[x'=x]⇉N , N∼M″[x'=x] with parComm A∼A' A'⇉A″ | parComm M∼M'[x'=x] M'[x'=x]⇉N
    where
    M∼M'[x'=x] : M ∼α M' [ x' := v x ]
    M∼M'[x'=x] =
      begin 
      M                             ∼⟨ lemma∙ι ⟩
      M ∙ ι                         ≈⟨ sym (lemma≺+ι {x} {y} {M} y∉fvM-x) ⟩ 
      M [ x := v y ] [ y := v x ]   ≈⟨ cong₂ _∙_ M[x=y]=M'[x'=y] refl ⟩ 
      M' [ x' := v y ] [ y := v x ] ≈⟨ sym (composRenUpd {x'} {y} {M'} y∉fvM'-x') ⟩ 
      M' [ x' := v x ]              ∎
      where open PreR ≈-preorder∼
  ... | B , A⇉B , B∼A″ | P , M⇉P , P∼N = λ[ x ∶ B ] P , ⇉λ A⇉B M⇉P , ∼λ B∼A″ y∉fvP-x y∉fvM″-x' P[x=y]=M″[x'=y]
    where
    x∉fvM'-x' : x ∉ fv M' - x'
    x∉fvM'-x' = ∼α⇒∉- λ[x:A]M∼λ[x':A']M'
    x∉fvM″-x' : x ∉ fv M″ - x'
    x∉fvM″-x' = lemma⇉#- M'⇉M″ x∉fvM'-x'
    P[x=y]=M″[x'=y] : P [ x := v y ] ≡ M″ [ x' := v y ]
    P[x=y]=M″[x'=y] =
      begin
      P [ x := v y ]                ≡⟨ compatSubAlpha (∼τ P∼N N∼M″[x'=x]) ⟩
      M″ [ x' := v x ] [ x := v y ] ≡⟨ sym (composRenUpd {x'} {x} {M″} x∉fvM″-x') ⟩                  
      M″ [ x' := v y ]              ∎
      where open PropEq.≡-Reasoning
    y∉fvP-x : y ∉ fv P - x
    y∉fvP-x = lemma⇉#- M⇉P y∉fvM-x
    y∉fvM″-x' : y ∉ fv M″ - x'
    y∉fvM″-x' = lemma⇉#- M'⇉M″ y∉fvM'-x'
  parComm Π[x:A]M∼Π[x':A']M'@(∼Π {x} {x'} {y} {A} {A'} {M} {M'} A∼A' y∉fvM-x y∉fvM'-x' M[x=y]=M'[x'=y]) (⇉Π {_}{_}{_}{_}{M″} A'⇉A″ M'⇉M″)
    with compatParSub M'⇉M″ (lemma⇉ₛ≺+ x' {σ = ι} ⇉v lemma⇉ₛρ)
  ... | N , M'[x'=x]⇉N , N∼M″[x'=x] with parComm A∼A' A'⇉A″ | parComm M∼M'[x'=x] M'[x'=x]⇉N
    where
    M∼M'[x'=x] : M ∼α M' [ x' := v x ]
    M∼M'[x'=x] =
      begin 
      M                             ∼⟨ lemma∙ι ⟩
      M ∙ ι                         ≈⟨ sym (lemma≺+ι {x} {y} {M} y∉fvM-x) ⟩ 
      M [ x := v y ] [ y := v x ]   ≈⟨ cong₂ _∙_ M[x=y]=M'[x'=y] refl ⟩ 
      M' [ x' := v y ] [ y := v x ] ≈⟨ sym (composRenUpd {x'} {y} {M'} y∉fvM'-x') ⟩ 
      M' [ x' := v x ]              ∎
      where open PreR ≈-preorder∼
  ... | B , A⇉B , B∼A″ | P , M⇉P , P∼N = Π[ x ∶ B ] P , ⇉Π A⇉B M⇉P , ∼Π B∼A″ y∉fvP-x y∉fvM″-x' P[x=y]=M″[x'=y]
    where
    x∉fvM'-x' : x ∉ fv M' - x'
    x∉fvM'-x' = ∼α⇒∉-Π Π[x:A]M∼Π[x':A']M'
    x∉fvM″-x' : x ∉ fv M″ - x'
    x∉fvM″-x' = lemma⇉#- M'⇉M″ x∉fvM'-x'
    P[x=y]=M″[x'=y] : P [ x := v y ] ≡ M″ [ x' := v y ]
    P[x=y]=M″[x'=y] =
      begin
      P [ x := v y ]                ≡⟨ compatSubAlpha (∼τ P∼N N∼M″[x'=x]) ⟩
      M″ [ x' := v x ] [ x := v y ] ≡⟨ sym (composRenUpd {x'} {x} {M″} x∉fvM″-x') ⟩                  
      M″ [ x' := v y ]              ∎
      where open PropEq.≡-Reasoning
    y∉fvP-x : y ∉ fv P - x
    y∉fvP-x = lemma⇉#- M⇉P y∉fvM-x
    y∉fvM″-x' : y ∉ fv M″ - x'
    y∉fvM″-x' = lemma⇉#- M'⇉M″ y∉fvM'-x'
  parComm (∼· M∼M' N∼N') (⇉· M'⇉M″ N'⇉N″) with parComm M∼M' M'⇉M″ | parComm N∼N' N'⇉N″
  ... | P , M⇉P , P∼M″ | Q , N⇉Q , Q∼N″ = P · Q , ⇉· M⇉P N⇉Q , ∼· P∼M″ Q∼N″
  parComm (∼· λ[x:A]M∼λ[x':A']M'@(∼λ{x}{x'}{y}{A}{A'}{M}{M'}A∼A' y∉fvM-x y∉fvM'-x' M[x=y]=M'[x'=y])N∼N')(⇉β{M' = M″}{N'}{N″}M'⇉M″ N'⇉N″)
    with compatParSub M'⇉M″(lemma⇉ₛ≺+ x' {σ = ι} ⇉v lemma⇉ₛρ)
  ... | P , M'[x'=x]⇉P , P∼M″[x'=x] with parComm M∼M'[x'=x] M'[x'=x]⇉P | parComm N∼N' N'⇉N″
    where
    M∼M'[x'=x] : M ∼α M' [ x' := v x ]
    M∼M'[x'=x] =
      begin 
      M                             ∼⟨ lemma∙ι ⟩
      M ∙ ι                         ≈⟨ sym (lemma≺+ι {x} {y} {M} y∉fvM-x) ⟩ 
      M [ x := v y ] [ y := v x ]   ≈⟨ cong₂ _∙_ M[x=y]=M'[x'=y] refl ⟩ 
      M' [ x' := v y ] [ y := v x ] ≈⟨ sym (composRenUpd {x'} {y} {M'} y∉fvM'-x') ⟩ 
      M' [ x' := v x ]              ∎
      where open PreR ≈-preorder∼    
  ... | Q , M⇉Q , Q∼P | R , N⇉R , R∼N″ = Q [ x := R ] , ⇉β M⇉Q N⇉R , Q[x=R]∼M″[x'=N″] 
    where
    x∉fvM'-x' : x ∉ fv M' - x'
    x∉fvM'-x' = ∼α⇒∉- λ[x:A]M∼λ[x':A']M'
    x∉fvM″-x' : x ∉ fv M″ - x'
    x∉fvM″-x' = lemma⇉#- M'⇉M″ x∉fvM'-x'
    Q[x=R]∼M″[x'=N″] : Q [ x := R ] ∼α M″ [ x' := N″ ]
    Q[x=R]∼M″[x'=N″] =
      begin 
      Q [ x := R ]                 ≈⟨ compatSubAlpha Q∼P ⟩
      P [ x := R ]                 ∼⟨ lemma-subst P∼M″[x'=x] (lemma≺+∼α⇂ {x = x} lemmaι∼α⇂ R∼N″) ⟩      
      M″ [ x' := v x ] [ x := N″ ] ≈⟨ sym (composRenUpd {x'} {x} {M″} x∉fvM″-x') ⟩ 
      M″ [ x' := N″ ]              ∎
      where open PreR ≈-preorder∼