module linear.Typing.Inversion where

open import Data.Nat
open import Data.Vec hiding (map ; [_] ; _++_)
open import Data.Product

open import linear.Type
open import linear.Context hiding (_++_)
open import linear.Language
open import linear.Usage
open import linear.Typing

-- inversion lemmas
app-inv :
  {n : } {γ : Context n} {t : Infer n} {u : Check n} {Γ Δ : Usages γ} {τ : Type} 
  Γ  `app t u  τ  Δ  Σ[ θ  Usages γ ] Σ[ σ  Type ] Γ  t  σ ─o τ  θ × θ  σ  u  Δ
app-inv (`app t u) = , , t , u

fst-inv :
  {n : } {γ : Context n} {t : Infer n} {Γ Δ : Usages γ} {σ : Type} 
  Γ  `fst t  σ  Δ  Σ[ τ  Type ] Γ  t  σ & τ  Δ
fst-inv (`fst t) = , t

snd-inv :
  {n : } {γ : Context n} {t : Infer n} {Γ Δ : Usages γ} {τ : Type} 
  Γ  `snd t  τ  Δ  Σ[ σ  Type ] Γ  t  σ & τ  Δ
snd-inv (`snd t) = , t

exfalso-inv :
  {n : } {γ : Context n} {t : Infer n} {Γ Δ : Usages γ} {σ τ : Type} 
  Γ  `exfalso σ t  τ  Δ  Γ  t  𝟘  Δ
exfalso-inv (`exfalso σ t) = t

case-inv : 
  {n : } {γ : Context n} {t : Infer n} {l r : Check (suc n)} {Γ Δ : Usages γ} {ν₁ ν₂ : Type} 
  Γ  `case t return ν₁ of l %% r  ν₂  Δ 
  Σ[ θ  Usages γ ] Σ[ σ  Type ] Σ[ τ  Type ]
  Γ  t  σ  τ  θ × [ σ ]  θ  ν₁  l  ] σ [  Δ × [ τ ]  θ  ν₁  r  ] τ [  Δ
case-inv (`case t return ν of l %% r) = , , , t , l , r

neu-inv :
  {n : } {γ : Context n} {t : Infer n} {Γ Δ : Usages γ} {σ : Type} 
  Γ  σ  `neu t  Δ  Γ  t  σ  Δ
neu-inv (`neu t) = t

lam-inv : 
  {n : } {γ : Context n} {t : Check (suc n)} {Γ Δ : Usages γ} {σ τ : Type} 
  Γ  σ ─o τ  `lam t  Δ  [ σ ]  Γ  τ  t  ] σ [  Δ
lam-inv (`lam t) = t

let-inv : 
  {n o : } {γ : Context n} {p : Pattern o} {t : Infer n} {u : Check (o + n)}
  {Γ Δ : Usages γ} {τ : Type}  Γ  τ  `let p ∷= t `in u  Δ 
  Σ[ θ  Usages γ ] Σ[ σ  Type ] Σ[ δ  Context o ]
  Γ  t  σ  θ × σ  p  δ × [[ δ ]] ++ θ  τ  u  ]] δ [[ ++ Δ
let-inv (`let p ∷= t `in u) = , , , t , p , u

prd⊗-inv :
  {n : } {γ : Context n} {t u : Check n} {Γ Δ : Usages γ} {σ τ : Type} 
  Γ  σ  τ  `prd t u  Δ  Σ[ θ  Usages γ ] Γ  σ  t  θ × θ  τ  u  Δ
prd⊗-inv (`prd⊗ t u) = , t , u

prd&-inv :
  {n : } {γ : Context n} {t u : Check n} {Γ Δ : Usages γ} {σ τ : Type} 
  Γ  σ & τ  `prd t u  Δ  Γ  σ  t  Δ × Γ  τ  u  Δ
prd&-inv (`prd& t u) = t , u

-- useful corrolaries
app-inv-function : 
  {n : } {γ : Context n} {t : Infer n} {u : Check n} {Γ Δ : Usages γ} {τ : Type} 
  (p : Γ  `app t u  τ  Δ)  let (θ , σ , _) = app-inv p in Γ  t  σ ─o τ  θ
app-inv-function p = let (_ , _ , T , _) = app-inv p in T

app-inv-argument : 
  {n : } {γ : Context n} {t : Infer n} {u : Check n} {Γ Δ : Usages γ} {τ : Type} 
  (p : Γ  `app t u  τ  Δ)  let (θ , σ , _) = app-inv p in θ  σ  u  Δ
app-inv-argument p = let (_ , _ , _ , U) = app-inv p in U

case-inv-scrutinee : 
  {n : } {γ : Context n} {t : Infer n} {l r : Check (suc n)} {Γ Δ : Usages γ} {ν₁ ν₂ : Type} 
  (p : Γ  `case t return ν₁ of l %% r  ν₂  Δ) 
  let (θ , σ , τ , _) = case-inv p in Γ  t  σ  τ  θ
case-inv-scrutinee p = let (_ , _ , _ , T , _) = case-inv p in T

case-inv-left : 
  {n : } {γ : Context n} {t : Infer n} {l r : Check (suc n)} {Γ Δ : Usages γ} {ν₁ ν₂ : Type} 
  (p : Γ  `case t return ν₁ of l %% r  ν₂  Δ) 
  let (θ , σ , τ , _) = case-inv p in [ σ ]  θ  ν₁  l  ] σ [  Δ
case-inv-left p = let (_ , _ , _ , _ , L , _) = case-inv p in L

case-inv-right : 
  {n : } {γ : Context n} {t : Infer n} {l r : Check (suc n)} {Γ Δ : Usages γ} {ν₁ ν₂ : Type} 
  (p : Γ  `case t return ν₁ of l %% r  ν₂  Δ) 
  let (θ , σ , τ , _) = case-inv p in [ τ ]  θ  ν₁  r  ] τ [  Δ
case-inv-right p = let (_ , _ , _ , _ , _ , R) = case-inv p in R

let-inv-bound : 
  {n o : } {γ : Context n} {p : Pattern o} {t : Infer n} {u : Check (o + n)}
  {Γ Δ : Usages γ} {τ : Type} (p : Γ  τ  `let p ∷= t `in u  Δ) 
  let (θ , σ , _) = let-inv p in Γ  t  σ  θ
let-inv-bound p = let (_ , _ , _ , T , _) = let-inv p in T

let-inv-pattern : 
  {n o : } {γ : Context n} {p : Pattern o} {t : Infer n} {u : Check (o + n)}
  {Γ Δ : Usages γ} {τ : Type} (d : Γ  τ  `let p ∷= t `in u  Δ) 
  let (_ , σ , δ , _) = let-inv d in σ  p  δ
let-inv-pattern p = let (_ , _ , _ , _ , P , _) = let-inv p in P

let-inv-body : 
  {n o : } {γ : Context n} {p : Pattern o} {t : Infer n} {u : Check (o + n)}
  {Γ Δ : Usages γ} {τ : Type} (p : Γ  τ  `let p ∷= t `in u  Δ) 
  let (θ , σ , δ , _) = let-inv p in [[ δ ]] ++ θ  τ  u  ]] δ [[ ++ Δ
let-inv-body p = let (_ , _ , _ , _ , _ , U) = let-inv p in U

prd-inv-fst : 
  {n : } {γ : Context n} {t u : Check n} {Γ Δ : Usages γ} {σ τ : Type} 
  (p : Γ  σ  τ  `prd t u  Δ)  let (θ , _) = prd⊗-inv p in Γ  σ  t  θ
prd-inv-fst p = let (_ , T , _) = prd⊗-inv p in T

prd-inv-snd : 
  {n : } {γ : Context n} {t u : Check n} {Γ Δ : Usages γ} {σ τ : Type} 
  (p : Γ  σ  τ  `prd t u  Δ)  let (θ , _) = prd⊗-inv p in θ  τ  u  Δ
prd-inv-snd p = let (_ , _ , U) = prd⊗-inv p in U