module linear.Usage.Erasure where

open import Level
open import Data.Nat hiding (_⊔_)
open import Data.Vec  as Vec hiding (_∷ʳ_)
open import Data.List as List hiding (_∷ʳ_)
open import Data.Product
open import Function
open import Relation.Binary.PropositionalEquality as PEq using (_≡_)

open import linear.Type
open import linear.Scope as Sc
open import linear.Context as C
open import linear.Context.Pointwise hiding (sym)
open import linear.Usage as U
open import linear.Usage.Consumption as UC hiding (divide)
open import linear.Usage.Pointwise as UP hiding (sym)

⌊_⌋ : {n : } {γ : Context n} {Γ Δ : Usages γ}  Γ  Δ 
      Σ[ k   ] Σ[ m  Sc.Mergey k n ]
      Σ[ δ  Context k ] Σ[ M  C.Mergey m ]
      Σ[ 𝓜  U.Mergey M ] Σ[ eq  Context[ _≡_ ] γ (δ C.⋈ M) ]
      coerce eq Γ  coerce eq Δ  [[ δ ]] U.⋈ 𝓜  ]] δ [[ U.⋈ 𝓜
 []       = , , , , finish , [] , []
 ─∷ inc   =
  let (k , m , δ , M , 𝓜 , eq , usg) =  inc 
  in k , insert m , δ , insert _ M , insert (U.[ _ ]) 𝓜 , PEq.refl  eq , (─∷ usg)
 σ  inc  =
  let (k , m , δ , M , 𝓜 , eq , usg) =  inc 
  in , , _  _ , , copy 𝓜 , PEq.refl  eq , σ  usg

used : {n : } {γ : Context n} {Γ Δ : Usages γ}  Γ  Δ  List Type
used []      = []
used (─∷ γ)  = used γ
used (σ  γ) = σ  used γ

used-refl : {n : } {γ : Context n} {Γ : Usages γ} (inc : Γ  Γ)  used inc  []
used-refl []       = PEq.refl
used-refl (─∷ inc) = used-refl inc

used-all : {n : } {γ : Context n} (pr : [[ γ ]]  ]] γ [[)  used pr  toList γ
used-all []      = PEq.refl
used-all (σ  γ) = PEq.cong (σ ∷_) (used-all γ)

used-++ : {k l : } {γ : Context k} {δ : Context l} {Γ Γ′ : Usages γ} {Δ Δ′ : Usages δ}
          (incΓ : Γ  Γ′) (incΔ : Δ  Δ′) 
          used (incΓ UC.++ incΔ)  used incΓ List.++ used incΔ
used-++ []         incΔ = PEq.refl
used-++ (─∷ incΓ)  incΔ = used-++ incΓ incΔ
used-++ (a  incΓ) incΔ = PEq.cong (a ∷_) (used-++ incΓ incΔ)


toList-++ : {k l : } (xs : Context k) (ys : Context l) 
            toList (xs Vec.++ ys)  toList xs List.++ toList ys
toList-++ []       ys = PEq.refl
toList-++ (x  xs) ys = PEq.cong (x ∷_) (toList-++ xs ys)

infix  1  _++_≅_
infixr 20 _∷ˡ_ _∷ʳ_
data _++_≅_ {A : Set} : (xs ys zs : List A)  Set where
  []   : [] ++ []  []
  _∷ˡ_ : (a : A) {xs ys zs : List A} (as : xs ++ ys  zs)  a  xs ++ ys  a  zs
  _∷ʳ_ : (a : A) {xs ys zs : List A} (as : xs ++ ys  zs)  xs ++ a  ys  a  zs  

sym : {A : Set} {xs ys zs : List A}  xs ++ ys  zs  ys ++ xs  zs
sym []        = []
sym (x ∷ˡ xs) = x ∷ʳ sym xs
sym (x ∷ʳ xs) = x ∷ˡ sym xs


divide : {n : } {γ : Context n} {Γ Δ θ : Usages γ} (p : Γ  Δ) (q : Δ  θ) (pq : Γ  θ) 
         used p ++ used q  used pq
divide []      []      []        = []
divide (─∷ p)  (─∷ q)  (─∷ pq)   = divide p q pq
divide (─∷ p)  (a  q) (.a  pq) = a ∷ʳ divide p q pq
divide (a  p) (─∷ q)  (.a  pq) = a ∷ˡ divide p q pq

infixr 10 _++ʳ_ _++ˡ_

_++ʳ_ : {A : Set} {xs ys zs : List A} (ts : List A) (eq : xs ++ ys  zs) 
        xs ++ ts List.++ ys  ts List.++ zs
[]       ++ʳ eq = eq
(t  ts) ++ʳ eq = t ∷ʳ (ts ++ʳ eq)

_++ˡ_ : {A : Set} {xs ys zs : List A} (ts : List A) (eq : xs ++ ys  zs) 
        ts List.++ xs ++ ys  ts List.++ zs
[]       ++ˡ eq = eq
(t  ts) ++ˡ eq = t ∷ˡ (ts ++ˡ eq)

trivial : {A : Set} {xs ys : List A}  xs ++ ys  xs List.++ ys
trivial {xs = []}     {[]}     = []
trivial {xs = []}     {y  ys} = y ∷ʳ trivial
trivial {xs = x  xs}          = x ∷ˡ trivial