module linear.Mix where

open import Data.Fin as F
open import Data.Sum as Sum
open import Data.Product as Prod
open import Function
open import Relation.Binary.PropositionalEquality as Eq hiding ([_])

open import linear.Context as C
open import linear.Context.Pointwise as CP
open import linear.Context.Mix
open import linear.Usage as U hiding ([_])
open import linear.Usage.Pointwise as UP
open import linear.Usage.Mix
open import linear.Typing.Extensional

Mix :  {T}  Typing T  Set
Mix {T} 𝓣 =
   {l m n o} {γ : Context l} {δ : Context m} {θ : Context n} {ξ : Context o}
  {Γ₁ Γ₂ Δ₁ Δ₂ Γ Γ′ Δ Δ′ t σ} {p : γ ++ δ  θ} {q : γ ++ δ  ξ} 
  [ p ] Γ₁ ++ Γ₂  Γ   [ p ] Δ₁ ++ Δ₂  Δ 
  [ q ] Γ₁ ++ Γ₂  Γ′  [ q ] Δ₁ ++ Δ₂  Δ′ 
  𝓣 Γ t σ Δ   λ t  𝓣 Γ′ t σ Δ′

splitFin :
   {m n p} {γ : Context m} {δ : Context n} {θ : Context p}
  {Γ₁ Γ₂ Δ₁ Δ₂ Γ Δ k σ} (p : γ ++ δ  θ) 
  [ p ] Γ₁ ++ Γ₂  Γ  [ p ] Δ₁ ++ Δ₂  Δ 
  Γ  k ∈[ σ ]⊠ Δ  ( λ k  Γ₁  k ∈[ σ ]⊠ Δ₁ × Γ₂  Δ₂)
                   ( λ k  Γ₂  k ∈[ σ ]⊠ Δ₂ × Γ₁  Δ₁)
splitFin [] [] [] ()
splitFin (σ ∷ˡ p) (_ ∷ˡ eq₁) (_  ∷ˡ eq₂) z
  rewrite proj₁ (≅-inj p eq₁ eq₂) = inj₁ (, z , proj₂ (≅-inj p eq₁ eq₂))
splitFin (σ ∷ʳ p) (_ ∷ʳ eq₁) (_  ∷ʳ eq₂) z
  rewrite proj₂ (≅-inj p eq₁ eq₂) = inj₂ (, z , proj₁ (≅-inj p eq₁ eq₂))
splitFin (σ ∷ˡ p) (u ∷ˡ eq₁) (.u ∷ˡ eq₂) (s K) =
  Sum.map
    (Prod.map F.suc (Prod.map s_ id))
    (Prod.map id (Prod.map id (cong (u ∷_))))
  $ splitFin p eq₁ eq₂ K
splitFin (σ ∷ʳ p) (u ∷ʳ eq₁) (.u ∷ʳ eq₂) (s K) =
  Sum.map
    (Prod.map id (Prod.map id (cong (u ∷_))))
    (Prod.map F.suc (Prod.map s_ id))
  $ splitFin p eq₁ eq₂ K

unsplitContext :  {m n p} {γ : Context m} {δ : Context n} {θ : Context p} (p : γ ++ δ  θ) 
   λ (mM₁ :  C.Mergey) 
   λ (mM₂ :  C.Mergey) 
    Context[ _≡_ ] θ (γ C.⋈ proj₂ mM₁)
  × Context[ _≡_ ] θ (δ C.⋈ proj₂ mM₂)
unsplitContext []       = (, finish) , (, finish) , ([] , [])
unsplitContext (σ ∷ˡ p) =
  let ((_ , M₁) , (_ , M₂) , eq₁ , eq₂) = unsplitContext p
  in (, copy M₁) , (, insert σ M₂) , Eq.refl  eq₁ , Eq.refl  eq₂
unsplitContext (σ ∷ʳ p) =
  let ((m₁ , M₁) , (m₂ , M₂) , eq₁ , eq₂) = unsplitContext p
  in (, insert σ M₁) , (, copy M₂) , Eq.refl  eq₁ , Eq.refl  eq₂


unsplitUsages₁ :
   {m n p} {γ : Context m} {δ : Context n} {θ : Context p}
  (p : γ ++ δ  θ) (Δ : Usages δ) 
  let ((_ , M₁) , _) = unsplitContext p
  in U.Mergey M₁
unsplitUsages₁ []       Δ       = finish
unsplitUsages₁ (a ∷ˡ p) Δ       = copy (unsplitUsages₁ p Δ)
unsplitUsages₁ (a ∷ʳ p) (A  Δ) = insert A (unsplitUsages₁ p Δ)

unsplitUsages₁-eq :
   {m n p} {γ : Context m} {δ : Context n} {θ : Context p} {p : γ ++ δ  θ} {Δ : Usages δ} 
  let ((_ , M₁) , _ , eq₁ , _) = unsplitContext p in
   {Γ Θ}  [ p ] Γ ++ Δ  Θ  Usages[ _≡_ , UsageEq ] eq₁ Θ (Γ U.⋈ unsplitUsages₁ p Δ)
unsplitUsages₁-eq []        = []
unsplitUsages₁-eq (S ∷ˡ eq) = Eq.refl  (unsplitUsages₁-eq eq)
unsplitUsages₁-eq (S ∷ʳ eq) = Eq.refl  (unsplitUsages₁-eq eq)

unsplitUsages₂ :
   {m n p} {γ : Context m} {δ : Context n} {θ : Context p} (p : γ ++ δ  θ) (Γ : Usages γ) 
  let (_ , (_ , M₂) , _) = unsplitContext p
  in U.Mergey M₂
unsplitUsages₂ []       Γ       = finish
unsplitUsages₂ (a ∷ˡ p) (A  Γ) = insert A (unsplitUsages₂ p Γ)
unsplitUsages₂ (a ∷ʳ p) Γ       = copy (unsplitUsages₂ p Γ)

unsplitUsages₂-eq :
   {m n p} {γ : Context m} {δ : Context n} {θ : Context p} {p : γ ++ δ  θ} {Γ : Usages γ} 
  let (_ , (_ , M₂) , _ , eq₂) = unsplitContext p in
   {Δ Θ}  [ p ] Γ ++ Δ  Θ  Usages[ _≡_ , UsageEq ] eq₂ Θ (Δ U.⋈ unsplitUsages₂ p Γ)
unsplitUsages₂-eq []        = []
unsplitUsages₂-eq (S ∷ˡ eq) = Eq.refl  (unsplitUsages₂-eq eq)
unsplitUsages₂-eq (S ∷ʳ eq) = Eq.refl  (unsplitUsages₂-eq eq)

mixFin : Mix TFin
mixFin {Γ₁ = Γ₁} {Γ₂} {p = p} {q} eqΓ eqΔ eqΓ′ eqΔ′ K =
  case splitFin p eqΓ eqΔ K of λ
  { (inj₁ (k , K , Γ₂≡Δ₂)) 
    let (_ , _ , eq₁ , _) = unsplitContext q
        inc               = unsplitUsages₁ q Γ₂
        EQΓ′              = unsplitUsages₁-eq eqΓ′
        EQΔ′              = unsplitUsages₁-eq eqΔ′
        K′                = U.weakFin inc K
        EQΔ′              = UP.irrelevance _ (subst _ (Eq.sym Γ₂≡Δ₂) (UP.sym EQΔ′))
    in , extensionalFin eq₁ (CP.sym eq₁) EQΓ′ EQΔ′ K′
  ; (inj₂ (k , K , Γ₁≡Δ₁)) 
    let (_ , _ , _ , eq₂) = unsplitContext q
        inc               = unsplitUsages₂ q Γ₁
        EQΓ′              = unsplitUsages₂-eq eqΓ′
        EQΔ′              = unsplitUsages₂-eq eqΔ′
        K′                = U.weakFin inc K
        EQΔ′              = UP.irrelevance _ (subst _ (Eq.sym Γ₁≡Δ₁) (UP.sym EQΔ′))
    in , extensionalFin eq₂ (CP.sym eq₂) EQΓ′ EQΔ′ K′
  }