open import Stoughton.Var

module Stoughton.Alpha (𝒞 : 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.Renaming 𝒞 var
  
  open import Function
  open import Data.Empty
  open import Relation.Nullary
  open import Data.List hiding (length)
  open import Data.Nat hiding (_*_; _≟_)
  open import Data.Nat.Properties hiding (_≟_)
  open import Data.Product renaming (Σ to Σₓ) hiding (map)
  open import Data.List.Relation.Unary.Any hiding (map)
  open import Data.List.Membership.Propositional
  open import Data.List.Membership.Propositional.Properties  
  open import Data.List.Properties
  open import Relation.Binary
  open import Relation.Binary.PropositionalEquality as PropEq using (_≡_; _≢_; refl; sym; cong; cong₂; trans; subst)
  open PropEq.≡-Reasoning 

  infix  3 _∼α_ 
  data _∼α_ : Λ → Λ → Set where
    ∼c : ∀ {k} → c k ∼α c k
    ∼v : ∀ {x} → v x ∼α v x
    ∼· : ∀ {M M' N N'} → M ∼α M' → N ∼α N' → M · N ∼α M' · N'
    ∼λ : ∀ {x x' y A A' M M'} → A ∼α A' → y ∉ fv M - x → y ∉ fv M' - x' → M [ x := v y ] ≡ M' [ x' := v y ] → λ[ x ∶ A ] M ∼α λ[ x' ∶ A' ] M'
    ∼Π : ∀ {x x' y A A' B B'} → A ∼α A' → y ∉ fv B - x → y ∉ fv B' - x' → B [ x := v y ] ≡ B' [ x' := v y ] → Π[ x ∶ A ] B ∼α Π[ x' ∶ A' ] B' 

  iotaAlpha : {M M' : Λ} → M ∙ ι ≡ M' ∙ ι → M ∼α M' 
  iotaAlpha {c x}   {c .x}   refl = ∼c
  iotaAlpha {c x}   {v y}     ()
  iotaAlpha {c x}   {M · N}   ()
  iotaAlpha {c x}   {λ[ _ ∶ _ ] _}  ()
  iotaAlpha {v x}   {c k}     () 
  iotaAlpha {v x}   {v .x}    refl = ∼v
  iotaAlpha {v x}   {M · N}   () 
  iotaAlpha {v x}   {λ[ _ ∶ _ ] _}  ()
  iotaAlpha {M · N} {c x}        ()
  iotaAlpha {M · N} {v x}      ()
  iotaAlpha {M · N} {M' · N'} MNι≡M'N'ι = ∼· (iotaAlpha (proj₁ (aux MNι≡M'N'ι))) (iotaAlpha (proj₂ (aux MNι≡M'N'ι)))
    where 
    aux : (M · N) ∙ ι ≡ (M' · N') ∙ ι → M ∙ ι ≡ M' ∙ ι × N ∙ ι ≡ N' ∙ ι
    aux MNι≡M'N'ι with M' ∙ ι | N' ∙ ι | MNι≡M'N'ι
    ... | .(M ∙ ι) | .(N ∙ ι) | refl = refl , refl
  iotaAlpha {M · N} {λ[ _ ∶ _ ] _}     ()
  iotaAlpha {λ[ _ ∶ _ ] _} {c _}        () 
  iotaAlpha {λ[ _ ∶ _ ] _} {v y}        () 
  iotaAlpha {λ[ _ ∶ _ ] _} {M' · N'}    ()
  iotaAlpha {λ[ x ∶ A ] M} {λ[ x' ∶ A' ] M'} λxMι≡λx'M' with aux λxMι≡λx'M' 
    where
    aux : λ[ x ∶ A ] M ∙ ι ≡ λ[ x' ∶ A' ] M' ∙ ι → X (ι , fv M - x) ≡ X (ι , fv M' - x') × M ∙ ι ‚ x := v (X (ι , fv M - x)) ≡ M' ∙ ι ‚ x' := v (X (ι , fv M' - x')) × A ∙ ι ≡ A' ∙ ι
    aux λxMι#λx'M'ι with A ∙ ι | X (ι , fv M - x) | M ∙ ι ‚ x := v (X (ι , fv M - x)) | λxMι#λx'M'ι 
    ... | .(A' ∙ ι) | .(X (ι , fv M' - x')) | .(M' ∙ ι ‚ x' := v (X (ι , fv M' - x'))) | refl = refl , refl , refl
  ... | y≡y' , Mι≺+xy≡M'ι≺+xy' , Aι=A'ι with X (ι , fv M - x) | X (ι , fv M' - x') | lemma-χιₗ (fv M - x) | lemma-χιₗ (fv M' - x') | y≡y' 
  ... | y | .y | y#λxM | y#λx'M' | refl = ∼λ {x} {x'} {y} {A} {A'} {M} {M'} A∼A' y#λxM y#λx'M' Mι≺+xy≡M'ι≺+xy'
    where    
    A∼A' : A ∼α A'
    A∼A' = iotaAlpha Aι=A'ι
  iotaAlpha {Π[ x ∶ A ] M} {Π[ x' ∶ A' ] M'} ΠxMι≡Πx'M' with aux ΠxMι≡Πx'M' 
    where
    aux : Π[ x ∶ A ] M ∙ ι ≡ Π[ x' ∶ A' ] M' ∙ ι → X (ι , fv M - x) ≡ X (ι , fv M' - x') × M ∙ ι ‚ x := v (X (ι , fv M - x)) ≡ M' ∙ ι ‚ x' := v (X (ι , fv M' - x')) × A ∙ ι ≡ A' ∙ ι
    aux ΠxMι#Πx'M'ι with A ∙ ι | X (ι , fv M - x) | M ∙ ι ‚ x := v (X (ι , fv M - x)) | ΠxMι#Πx'M'ι 
    ... | .(A' ∙ ι) | .(X (ι , fv M' - x')) | .(M' ∙ ι ‚ x' := v (X (ι , fv M' - x'))) | refl = refl , refl , refl
  ... | y≡y' , Mι≺+xy≡M'ι≺+xy' , Aι=A'ι with X (ι , fv M - x) | X (ι , fv M' - x') | lemma-χιₗ (fv M - x) | lemma-χιₗ (fv M' - x') | y≡y' 
  ... | y | .y | y#ΠxM | y#Πx'M' | refl = ∼Π {x} {x'} {y} {A} {A'} {M} {M'} A∼A' y#ΠxM y#Πx'M' Mι≺+xy≡M'ι≺+xy'
    where    
    A∼A' : A ∼α A'
    A∼A' = iotaAlpha Aι=A'ι

  ∼ρ : Reflexive _∼α_
  ∼ρ {M} = iotaAlpha refl

  ≡⇒∼ : ∀ {M N} → M ≡ N → M ∼α N
  ≡⇒∼ {M} {.M} refl = ∼ρ

  infix  3 _∼αₛ_ 
  data _∼αₛ_ : Λ → Λ → Set where
    ∼c : ∀ {k} → c k ∼αₛ c k
    ∼v : ∀ {x} → v x ∼αₛ v x
    ∼· : ∀ {M M' N N'} → M ∼αₛ M' → N ∼αₛ N' → M · N ∼αₛ M' · N'
    ∼λ : ∀ {M M' x x' y A A'} → A ∼αₛ A' → y ∉ fv M - x → y ∉ fv M' - x' → [ v y / x ] M ∼αₛ [ v y / x' ] M' → λ[ x ∶ A ] M ∼αₛ λ[ x' ∶ A' ] M'
    ∼Π : ∀ {B B' x x' y A A'} → A ∼αₛ A' → y ∉ fv B - x → y ∉ fv B' - x' → [ v y / x ] B ∼αₛ [ v y / x' ] B' → Π[ x ∶ A ] B ∼αₛ Π[ x' ∶ A' ] B' 

  infixl 8 _≺+_
  _≺+_ : Sub → 𝒱 × Λ → Sub
  σ ≺+ (x , N) = σ ‚ x := N

  open import Data.Nat.Induction
  
  iotaAlphaSt-aux : (n : ℕ) → ((y : ℕ) → suc y ≤′ n → (M M' : Λ) → y ≡ length M → M ∙ ι ≡ M' ∙ ι → M ∼αₛ M') → (M M' : Λ) → n ≡ length M → M ∙ ι ≡ M' ∙ ι → M ∼αₛ M'
  iotaAlphaSt-aux .(suc zero) rec (c x)   (c .x)    refl refl = ∼c
  iotaAlphaSt-aux .(suc zero) rec (c x)   (v y)     refl ()
  iotaAlphaSt-aux .(suc zero) rec (c x)   (M · N)   refl ()
  iotaAlphaSt-aux .(suc zero) rec (c x)   (λ[ _ ∶ _ ] _)   refl ()
  iotaAlphaSt-aux .(suc zero) rec (v x)   (c k)     refl () 
  iotaAlphaSt-aux .(suc zero) rec (v x)   (v .x)    refl refl = ∼v
  iotaAlphaSt-aux .(suc zero) rec (v x)   (M · N)   refl () 
  iotaAlphaSt-aux .(suc zero) rec (v x)   (λ[ _ ∶ _ ] _)   refl ()
  iotaAlphaSt-aux n           rec (M · N) (c x)     _    ()
  iotaAlphaSt-aux n           rec (M · N) (v x)     _    ()
  iotaAlphaSt-aux .(suc (length M + length N)) rec (M · N) (M' · N') refl MNι≡M'N'ι
    = ∼· (rec (length M) (s≤′s (m≤′m+n (length M) (length N))) M M' refl (proj₁ (lemmaMι≡M'ι MNι≡M'N'ι)))
         (rec (length N) (s≤′s (n≤′m+n (length M) (length N))) N N' refl (proj₂ (lemmaMι≡M'ι MNι≡M'N'ι)))
    where 
    lemmaMι≡M'ι : (M · N) ∙ ι ≡ (M' · N') ∙ ι → M ∙ ι ≡ M' ∙ ι × N ∙ ι ≡ N' ∙ ι
    lemmaMι≡M'ι MNι≡M'N'ι with M' ∙ ι | N' ∙ ι | MNι≡M'N'ι
    ... | .(M ∙ ι) | .(N ∙ ι) | refl = refl , refl
  iotaAlphaSt-aux n           rec (M · N) (λ[ _ ∶ _ ] _)  _    ()
  iotaAlphaSt-aux n           rec (λ[ _ ∶ _ ] _) (c _)     _    () 
  iotaAlphaSt-aux n           rec (λ[ _ ∶ _ ] _) (v y)     _    () 
  iotaAlphaSt-aux n           rec (λ[ _ ∶ _ ] _) (M' · N') _    ()
  iotaAlphaSt-aux .(suc (length A + length M)) rec (λ[ x ∶ A ] M) (λ[ x' ∶ A' ] M') refl λxMι≡λx'M' with λxMι≡λx'M'ι λxMι≡λx'M' 
    where
    λxMι≡λx'M'ι : λ[ x ∶ A ] M ∙ ι ≡ λ[ x' ∶ A' ] M' ∙ ι → X (ι , fv M - x) ≡ X (ι , fv M' - x') × M ∙ ι ≺+ (x , v (X (ι , fv M - x))) ≡ M' ∙ ι ≺+ (x' , v (X (ι , fv M' - x'))) × A ∙ ι ≡ A' ∙ ι
    λxMι≡λx'M'ι λxMι#λx'M'ι with A ∙ ι | X (ι , fv M - x) | M ∙ ι ≺+ (x , v (X (ι , fv M - x))) | λxMι#λx'M'ι 
    ... | .(A' ∙ ι) | .(X (ι , fv M' - x')) | .(M' ∙ ι ≺+ (x' , v (X (ι , fv M' - x')))) | refl = refl , refl , refl
  ... | y≡y' , Mι≺+xy≡M'ι≺+xy' , Aι=A'ι with X (ι , fv M - x) | X (ι , fv M' - x') | lemma-χιₗ (fv M - x) | lemma-χιₗ (fv M' - x') | y≡y' 
  ... | y | .y | y#λxM | y#λx'M' | refl = ∼λ {M} {M'} {x} {x'} {y} {A} {A'} A∼A' y#λxM y#λx'M' M∼M'
    where    
    A∼A' : A ∼αₛ A'
    A∼A' = rec (length A) (s≤′s (m≤′m+n (length A) (length M))) A A' refl Aι=A'ι
    sMxy=sA+M : suc (length (M ∙ ι ≺+ (x , v y))) ≤′ suc (length A + length M)
    sMxy=sA+M rewrite coroRen {M} {x} {y} = s≤′s (n≤′m+n (length A) (length M))
    M∼M' : M [ x := v y ] ∼αₛ M' [ x' := v y ]
    M∼M' = rec (length (M ∙ (ι ≺+ (x , v y)))) sMxy=sA+M (M ∙ (ι ≺+ (x , v y))) (M' ∙ (ι ≺+ (x' , v y))) refl (cong (λ M → M ∙ ι)  Mι≺+xy≡M'ι≺+xy')
  iotaAlphaSt-aux .(suc (length A + length M)) rec (Π[ x ∶ A ] M) (Π[ x' ∶ A' ] M') refl ΠxMι≡Πx'M' with ΠxMι≡Πx'M'ι ΠxMι≡Πx'M' 
    where
    ΠxMι≡Πx'M'ι : Π[ x ∶ A ] M ∙ ι ≡ Π[ x' ∶ A' ] M' ∙ ι → X (ι , fv M - x) ≡ X (ι , fv M' - x') × M ∙ ι ≺+ (x , v (X (ι , fv M - x))) ≡ M' ∙ ι ≺+ (x' , v (X (ι , fv M' - x'))) × A ∙ ι ≡ A' ∙ ι
    ΠxMι≡Πx'M'ι ΠxMι#Πx'M'ι with A ∙ ι | X (ι , fv M - x) | M ∙ ι ≺+ (x , v (X (ι , fv M - x))) | ΠxMι#Πx'M'ι 
    ... | .(A' ∙ ι) | .(X (ι , fv M' - x')) | .(M' ∙ ι ≺+ (x' , v (X (ι , fv M' - x')))) | refl = refl , refl , refl
  ... | y≡y' , Mι≺+xy≡M'ι≺+xy' , Aι=A'ι with X (ι , fv M - x) | X (ι , fv M' - x') | lemma-χιₗ (fv M - x) | lemma-χιₗ (fv M' - x') | y≡y' 
  ... | y | .y | y#ΠxM | y#Πx'M' | refl = ∼Π {M} {M'} {x} {x'} {y} {A} {A'} A∼A' y#ΠxM y#Πx'M' M∼M'
    where    
    A∼A' : A ∼αₛ A'
    A∼A' = rec (length A) (s≤′s (m≤′m+n (length A) (length M))) A A' refl Aι=A'ι
    sMxy=sA+M : suc (length (M ∙ ι ≺+ (x , v y))) ≤′ suc (length A + length M)
    sMxy=sA+M rewrite coroRen {M} {x} {y} = s≤′s (n≤′m+n (length A) (length M))
    M∼M' : M [ x := v y ] ∼αₛ M' [ x' := v y ]
    M∼M' = rec (length (M ∙ (ι ≺+ (x , v y)))) sMxy=sA+M (M ∙ (ι ≺+ (x , v y))) (M' ∙ (ι ≺+ (x' , v y))) refl (cong (λ M → M ∙ ι)  Mι≺+xy≡M'ι≺+xy')

  iotaAlphaSt : {M M' : Λ} → M ∙ ι ≡ M' ∙ ι → M ∼αₛ M' 
  iotaAlphaSt {M} {M'} = (<′-rec _ iotaAlphaSt-aux) (length M) M M' refl

  infix 1 _∼α_⇂_
  _∼α_⇂_ : Sub → Sub → List 𝒱 → Set
  σ ∼α σ' ⇂ xs = ∀ x → x ∈ xs → σ x ∼α σ' x

  lemmaι∼α⇂ : {M : List 𝒱} → ι ∼α ι ⇂ M
  lemmaι∼α⇂ {M} x _ = ∼v 

  lemma≺+∼α⇂ : {x : 𝒱}{M : List 𝒱}{N P : Λ}{σ σ' : Sub} → σ ∼α σ' ⇂ M → N ∼α P → σ ‚ x := N ∼α σ' ‚ x := P ⇂ M
  lemma≺+∼α⇂ {x} σ∼ασ'⇂M N~P y y*M with x ≟ y
  ... | yes  _ = N~P 
  ... | no   _ = σ∼ασ'⇂M y y*M 

  comm-++ : ∀ xs ys z → (xs ++ ys) - z ≡ xs - z ++ ys - z
  comm-++ [] ys z = refl
  comm-++ (x ∷ xs) ys z with z ≟ x
  ... | yes _ = comm-++ xs ys z
  ... | no _ = cong (_∷_ x) (comm-++ xs ys z)

  ∉-≡ : ∀ y xs → y ∉ xs → xs - y ≡ xs
  ∉-≡ y [] _ = refl
  ∉-≡ y (x ∷ xs) y∉x::xs with y ≟ x
  ... | yes y=x = ⊥-elim (y∉x::xs (here y=x))
  ... | no y≠x = cong (_∷_ x) (∉-≡ y xs (λ y∈xs → ⊥-elim (y∉x::xs (there y∈xs))))

  lemma-concat-map : ∀ {σ x y} xs → y #⇂ (σ , xs - x) →  concat (map (fv ∘ (σ ‚ x := v y)) xs) - y ≡ concat (map (fv ∘ σ) (xs - x))
  lemma-concat-map [] _ = refl
  lemma-concat-map {σ} {x} {y} (z ∷ xs') y#σ⇂z::xs'-x with x ≟ z 
  ... | yes x=z with y ≟ y
  ... | yes _ = lemma-concat-map {σ} {x} {y} xs' y#σ⇂z::xs'-x
  ... | no y≢y = ⊥-elim (y≢y refl)  
  lemma-concat-map {σ} {x} {y} (z ∷ xs') y#σ⇂z::xs'-x | no x≠z = 
    begin
    (fv (σ z) ++ concat (map (fv ∘ (σ ‚ x := v y)) xs')) - y
    ≡⟨ comm-++ (fv (σ z)) (concat (map (fv ∘ (σ ‚ x := v y)) xs')) y ⟩
    (fv (σ z) - y) ++ (concat (map (fv ∘ (σ ‚ x := v y)) xs') - y)
    ≡⟨ cong₂ _++_ refl (lemma-concat-map xs' y#σ⇂xs'-x) ⟩
    (fv (σ z) - y) ++ concat (map (fv ∘ σ) (xs' - x))
    ≡⟨ cong₂ _++_ (∉-≡ y (fv (σ z)) y∉fvσz) refl ⟩        
    fv (σ z) ++ concat (map (fv ∘ σ) (xs' - x))
    ≡⟨⟩    
    concat (fv (σ z) ∷ map (fv ∘ σ) (xs' - x))        
    ≡⟨⟩    
    concat (map (fv ∘ σ) (z ∷ (xs' - x)))
    ∎
    where
    y#σ⇂xs'-x : y #⇂ (σ , xs' - x)
    y#σ⇂xs'-x w w∈xs'-x = y#σ⇂z::xs'-x w (there w∈xs'-x)
    y∉fvσz : y # σ z
    y∉fvσz = y#σ⇂z::xs'-x z (here refl)
    
  lemmafv∙ : ∀ {M σ} → fv (M ∙ σ) ≡ concat (map (fv ∘ σ) (fv M))
  lemmafv∙ {c _} = refl
  lemmafv∙ {v x} {σ} = sym (++-identityʳ (fv (σ x)))
  lemmafv∙ {λ[ x ∶ A ] M} {σ} =
    begin
    fv ((λ[ x ∶ A ] M) ∙ σ)
    ≡⟨⟩
    fv (A ∙ σ) ++ (fv (M ∙ σ ‚ x := v y) - y)
    ≡⟨ cong₂ _++_ (lemmafv∙ {A} {σ}) (cong (flip _-_ y) (lemmafv∙ {M} {σ ‚ x := v y})) ⟩
    concat (map (fv ∘ σ) (fv A)) ++ (concat (map (fv ∘ (σ ‚ x := v y)) (fv M)) - y)
    ≡⟨ cong (_++_ (concat (map (fv ∘ σ) (fv A)))) (lemma-concat-map (fv M) y#σ⇂fvM-x) ⟩         
    concat (map (fv ∘ σ) (fv A)) ++ concat (map (fv ∘ σ) (fv M - x))    
    ≡⟨ concat-++ (map (fv ∘ σ) (fv A)) (map (fv ∘ σ) (fv M - x)) ⟩ 
    concat (map (fv ∘ σ) (fv A) ++ map (fv ∘ σ) (fv M - x))
    ≡⟨ cong concat (sym (map-++-commute (fv ∘ σ) (fv A) (fv M - x))) ⟩ 
    concat (map (fv ∘ σ) (fv A ++ (fv M - x)))
    ≡⟨⟩
    concat (map (fv ∘ σ) (fv (λ[ x ∶ A ] M)))
    ∎
    where
    y : 𝒱
    y = X (σ , fv M - x)
    y#σ⇂fvM-x : y #⇂ (σ , (fv M - x))
    y#σ⇂fvM-x = Xfresh σ (fv M - x)    
  lemmafv∙ {Π[ x ∶ A ] M} {σ} = 
    begin
    fv ((Π[ x ∶ A ] M) ∙ σ)
    ≡⟨⟩
    fv (A ∙ σ) ++ (fv (M ∙ σ ‚ x := v y) - y)
    ≡⟨ cong₂ _++_ (lemmafv∙ {A} {σ}) (cong (flip _-_ y) (lemmafv∙ {M} {σ ‚ x := v y})) ⟩
    concat (map (fv ∘ σ) (fv A)) ++ (concat (map (fv ∘ (σ ‚ x := v y)) (fv M)) - y)
    ≡⟨ cong (_++_ (concat (map (fv ∘ σ) (fv A)))) (lemma-concat-map (fv M) y#σ⇂fvM-x) ⟩         
    concat (map (fv ∘ σ) (fv A)) ++ concat (map (fv ∘ σ) (fv M - x))    
    ≡⟨ concat-++ (map (fv ∘ σ) (fv A)) (map (fv ∘ σ) (fv M - x)) ⟩ 
    concat (map (fv ∘ σ) (fv A) ++ map (fv ∘ σ) (fv M - x))
    ≡⟨ cong concat (sym (map-++-commute (fv ∘ σ) (fv A) (fv M - x))) ⟩ 
    concat (map (fv ∘ σ) (fv A ++ (fv M - x)))
    ≡⟨⟩
    concat (map (fv ∘ σ) (fv (Π[ x ∶ A ] M)))
    ∎
    where
    y : 𝒱
    y = X (σ , fv M - x)
    y#σ⇂fvM-x : y #⇂ (σ , (fv M - x))
    y#σ⇂fvM-x = Xfresh σ (fv M - x) 
  lemmafv∙ {M · N} {σ} =
    begin
    fv (M · N ∙ σ)
    ≡⟨⟩
    fv (M ∙ σ) ++ fv (N ∙ σ)
    ≡⟨ cong₂ _++_ (lemmafv∙ {M} {σ}) (lemmafv∙ {N} {σ}) ⟩
    concat (map (fv ∘ σ) (fv M)) ++ concat (map (fv ∘ σ) (fv N))
    ≡⟨ concat-++ (map (fv ∘ σ) (fv M)) (map (fv ∘ σ) (fv N)) ⟩
    concat (map (fv ∘ σ) (fv M) ++ map (fv ∘ σ) (fv N))
    ≡⟨ cong concat (sym (map-++-commute (fv ∘ σ) (fv M) (fv N))) ⟩  
    concat (map (fv ∘ σ) (fv M ++ fv N))    
    ≡⟨⟩    
    concat (map (fv ∘ σ) (fv (M · N)))
    ∎

  concat-map-fv∘ι : ∀ xs → concat (map (fv ∘ ι) xs) ≡ xs
  concat-map-fv∘ι [] = refl
  concat-map-fv∘ι (x ∷ xs') = cong (_∷_ x) (concat-map-fv∘ι xs')

  lemmafv∉ : ∀ {x y M} → y ∉ fv M - x → fv (M [ x := v y ]) - y ≡ fv M - x
  lemmafv∉ {x} {y} {M} y∉fvM-x =
    begin
    fv (M [ x := v y ]) - y
    ≡⟨ cong (flip _-_ y) (lemmafv∙ {M} {ι ‚ x := v y}) ⟩    
    concat (map (fv ∘ (ι ‚ x := v y)) (fv M)) - y
    ≡⟨ lemma-concat-map {ι} {x} {y} (fv M) (lemma#→ι#⇂ {y} {x} {fv M} y∉fvM-x) ⟩
    concat (map (fv ∘ ι) (fv M - x))
    ≡⟨ concat-map-fv∘ι (fv M - x) ⟩
    fv M - x
    ∎ 

  M∼M'→fvM≡fvM' : ∀ {M M'} → M ∼α M' → fv M ≡ fv M'
  M∼M'→fvM≡fvM' ∼c = refl
  M∼M'→fvM≡fvM' ∼v = refl
  M∼M'→fvM≡fvM' (∼· e1 e2) = cong₂ _++_ (M∼M'→fvM≡fvM' e1) (M∼M'→fvM≡fvM' e2)
  M∼M'→fvM≡fvM' (∼λ {x} {x'} {y} {A} {A'} {M} {M'} A∼A' y∉fvM-x y∉fvM'-x' M[x=y]∼M'[x'=y]) =
    begin
    fv (λ[ x ∶ A ] M)
    ≡⟨⟩
    fv A ++ (fv M - x)    
    ≡⟨ cong (_++_ (fv A)) (sym (lemmafv∉ {x} {y} {M} y∉fvM-x)) ⟩    
    fv A ++ (fv (M [ x := v y ]) - y)
    ≡⟨ cong₂ _++_ (M∼M'→fvM≡fvM' A∼A') (cong (flip _-_ y) (cong fv M[x=y]∼M'[x'=y])) ⟩
    fv A' ++ (fv (M' [ x' := v y ]) - y)    
    ≡⟨ cong (_++_ (fv A')) (lemmafv∉ {x'} {y} {M'} y∉fvM'-x') ⟩
    fv A' ++ (fv M' - x')
    ≡⟨⟩ 
    fv (λ[ x' ∶ A' ] M')
    ∎
  M∼M'→fvM≡fvM' (∼Π {x} {x'} {y} {A} {A'} {M} {M'} A∼A' y∉fvM-x y∉fvM'-x' M[x=y]∼M'[x'=y]) =
    begin
    fv (Π[ x ∶ A ] M)
    ≡⟨⟩
    fv A ++ (fv M - x)    
    ≡⟨ cong (_++_ (fv A)) (sym (lemmafv∉ {x} {y} {M} y∉fvM-x)) ⟩    
    fv A ++ (fv (M [ x := v y ]) - y)
    ≡⟨ cong₂ _++_ (M∼M'→fvM≡fvM' A∼A') (cong (flip _-_ y) (cong fv M[x=y]∼M'[x'=y])) ⟩
    fv A' ++ (fv (M' [ x' := v y ]) - y)    
    ≡⟨ cong (_++_ (fv A')) (lemmafv∉ {x'} {y} {M'} y∉fvM'-x') ⟩
    fv A' ++ (fv M' - x')
    ≡⟨⟩ 
    fv (Π[ x' ∶ A' ] M')
    ∎

  lemmaM∼M'→free← : ∀ {M M' z} → M ∼α M' → z * M' → z * M
  lemmaM∼M'→free← M∼M' x*M' rewrite M∼M'→fvM≡fvM' M∼M' = x*M'

  lemmaM∼M'→fresh→ : ∀ {M M' z} → M ∼α M' → z # M → z # M'
  lemmaM∼M'→fresh→ M∼M' z#M z*M' = ⊥-elim (z#M (lemmaM∼M'→free← M∼M' z*M'))

  ++-injectiveˡ : (xs ys ys' : List 𝒱) → xs ++ ys ≡ xs ++ ys' → ys ≡ ys'
  ++-injectiveˡ [] ys ys' e = e
  ++-injectiveˡ (x ∷ xs') ys ys' e = ++-injectiveˡ xs' ys ys' (∷-injectiveʳ e)

  ∼α⇒∉- : ∀ {x y A B M N} → λ[ x ∶ A ] M ∼α λ[ y ∶ B ] N → x ∉ fv N - y
  ∼α⇒∉- {x} {y} {A} {B} {M} {N} λxM∼λyN@(∼λ A∼B _ _ _) = subst (λ l → x ∉ l) e3 (∉- {x} (fv M))
    where
    e1 : fv A ++ (fv M - x) ≡ fv B ++ (fv N - y)
    e1 = M∼M'→fvM≡fvM' λxM∼λyN
    e2 : fv A ≡ fv B
    e2 = M∼M'→fvM≡fvM' A∼B
    e3 : fv M - x ≡ fv N - y
    e3 = ++-injectiveˡ (fv A) (fv M - x) (fv N - y) (subst (λ l → fv A ++ (fv M - x) ≡ l ++ (fv N - y)) (sym e2) e1)

  ∼α⇒∉-Π : ∀ {x y A B M N} → Π[ x ∶ A ] M ∼α Π[ y ∶ B ] N → x ∉ fv N - y
  ∼α⇒∉-Π {x} {y} {A} {B} {M} {N} ΠxM∼ΠyN@(∼Π A∼B _ _ _) = subst (λ l → x ∉ l) e3 (∉- {x} (fv M))
    where
    e1 : fv A ++ (fv M - x) ≡ fv B ++ (fv N - y)
    e1 = M∼M'→fvM≡fvM' ΠxM∼ΠyN
    e2 : fv A ≡ fv B
    e2 = M∼M'→fvM≡fvM' A∼B
    e3 : fv M - x ≡ fv N - y
    e3 = ++-injectiveˡ (fv A) (fv M - x) (fv N - y) (subst (λ l → fv A ++ (fv M - x) ≡ l ++ (fv N - y)) (sym e2) e1)

  CommAlpha : (Λ → Λ → Set) → Set
  CommAlpha _𝒮_ = ∀ {M N P} → M ∼α N → N 𝒮 P → ∃ λ Q → M 𝒮 Q × Q ∼α P