module linear.Usage.Functional where

open import Data.Nat
open import Data.Fin
open import Data.Vec hiding (_++_ ; tail ; map)
open import Data.Product
open import Function
open import Relation.Binary.PropositionalEquality

open import linear.Scope as Sc hiding (Env)
open import linear.Type
open import linear.Context as C hiding (_++_)
open import linear.Usage
open import linear.Relation.Functional

R++ : {o k : } (δ : Context o) (γ : Context k) (ΔΓ : Usages (δ C.++ γ))  (i : Usages δ) (o : Usages γ)  Set
R++ δ γ ΔΓ Δ Γ = ΔΓ  (Δ ++ Γ)

functional++ : {o k : } {δ : Context o} {γ : Context k} {ΔΓ : Usages (δ C.++ γ)}  Functional′ (R++ δ γ ΔΓ)
functional++ []      refl refl = refl
functional++ (A  Δ) eq₁  eq₂  = functional++ Δ (cong tail eq₁) (cong tail eq₂)

RFin : (k : Σ[ n   ] Context n × Fin n)  (let (_ , γ , _) = k in Usages γ × Usages γ)  Type  Set
RFin (_ , _ , k) (Γ , Δ) σ = Γ  k ∈[ σ ]⊠ Δ

functionalFin : Functional RFin
functionalFin _ z      z      = refl
functionalFin _ (s k₁) (s k₂) = cong _ $ functionalFin _ k₁ k₂

RFinPost : (k : Σ[ n   ] Σ[ γ  Context n ] Usages γ × Fin n) 
           (let (_ , γ , _) = k in Type × Usages γ)  Set
RFinPost (_ , _ , Γ , k) (A , Δ) = Γ  k ∈[ A ]⊠ Δ

RFinPre : (k : Σ[ n   ] Σ[ γ  Context n ] Usages γ × Fin n) 
           (let (_ , γ , _) = k in Type × Usages γ)  Set
RFinPre (_ , _ , Δ , k) (A , Γ) = Γ  k ∈[ A ]⊠ Δ

functionalFinPost : Functional′ RFinPost
functionalFinPost _ z      z      = refl
functionalFinPost _ (s k₁) (s k₂) = cong (map id _) $ functionalFinPost _ k₁ k₂

functionalFinPre : Functional′ RFinPre
functionalFinPre _ z      z      = refl
functionalFinPre _ (s k₁) (s k₂) = cong _ $ functionalFinPre _ k₁ k₂

InferTypingPost :
  {T :   Set} (𝓣 : Typing T) (i : Σ[ n   ] Σ[ γ  Context n ] Usages γ × T n) 
  (let (_ , γ , _) = i in Type × Usages γ)  Set
InferTypingPost 𝓣 (_ , _ , Γ , t) (A , Δ) = 𝓣 Γ t A Δ

CheckTypingPost :
  {T :   Set} (𝓣 : Typing T) (i : Σ[ n   ] Σ[ γ  Context n ] Usages γ × Type × T n) 
  (let (_ , γ , _) = i in Usages γ)  Set
CheckTypingPost 𝓣 (_ , _ , Γ , A , t) Δ = 𝓣 Γ t A Δ

REnvPost :
  {E :   Set} (𝓔 : Typing E)
  (i : Σ[ k   ] Σ[ l   ] Σ[ θ  Context l ] Usages θ × Sc.Env E k l × Σ[ γ  Context k ] Usages γ) 
  (let (_ , l , θ , _) = i in Usages θ)  Set
REnvPost 𝓔 (_ , _ , _ , Τ₁ , ρ , _ , Γ) Τ₂ = Env 𝓔 Τ₁ ρ Τ₂ Γ

functionalEnvPost :
  {E :   Set} {𝓔 : Typing E}  Functional′ (InferTypingPost 𝓔)  Functional′ (REnvPost 𝓔)
functionalEnvPost det𝓔 _ []        []        = refl
functionalEnvPost det𝓔 _ (T₁  ρ₁) (T₂  ρ₂) rewrite cong proj₂ (det𝓔 _ T₁ T₂) = functionalEnvPost det𝓔 _ ρ₁ ρ₂
functionalEnvPost det𝓔 _ (─∷ ρ₁)   (─∷ ρ₂)   = functionalEnvPost det𝓔 _ ρ₁ ρ₂
functionalEnvPost det𝓔 _ ([v]∷ ρ₁) ([v]∷ ρ₂) = cong _ $ functionalEnvPost det𝓔 _ ρ₁ ρ₂
functionalEnvPost det𝓔 _ (]v[∷ ρ₁) (]v[∷ ρ₂) = cong _ $ functionalEnvPost det𝓔 _ ρ₁ ρ₂

InferTypingPre :
  {T :   Set} (𝓣 : Typing T) (i : Σ[ n   ] Σ[ γ  Context n ] Usages γ × T n) 
  (let (_ , γ , _) = i in Type × Usages γ)  Set
InferTypingPre 𝓣 (_ , _ , Δ , t) (A , Γ) = 𝓣 Γ t A Δ

CheckTypingPre :
  {T :   Set} (𝓣 : Typing T) (i : Σ[ n   ] Σ[ γ  Context n ] Usages γ × Type × T n) 
  (let (_ , γ , _) = i in Usages γ)  Set
CheckTypingPre 𝓣 (_ , _ , Δ , A , t) Γ = 𝓣 Γ t A Δ

REnvPre :
  {E :   Set} (𝓔 : Typing E)
  (i : Σ[ k   ] Σ[ l   ] Σ[ θ  Context l ] Usages θ × Sc.Env E k l × Σ[ γ  Context k ] Usages γ) 
  (let (_ , l , θ , _) = i in Usages θ)  Set
REnvPre 𝓔 (_ , _ , _ , Τ₂ , ρ , _ , Γ) Τ₁ = Env 𝓔 Τ₁ ρ Τ₂ Γ

functionalEnvPre :
  {E :   Set} {𝓔 : Typing E}  Functional′ (InferTypingPre 𝓔)  Functional′ (REnvPre 𝓔)
functionalEnvPre det𝓔 _ []        []        = refl
functionalEnvPre det𝓔 _ (T₁  ρ₁) (T₂  ρ₂) rewrite functionalEnvPre det𝓔 _ ρ₁ ρ₂ = cong proj₂ (det𝓔 _ T₁ T₂)
functionalEnvPre det𝓔 _ (─∷ ρ₁)   (─∷ ρ₂)   = functionalEnvPre det𝓔 _ ρ₁ ρ₂
functionalEnvPre det𝓔 _ ([v]∷ ρ₁) ([v]∷ ρ₂) = cong _ $ functionalEnvPre det𝓔 _ ρ₁ ρ₂
functionalEnvPre det𝓔 _ (]v[∷ ρ₁) (]v[∷ ρ₂) = cong _ $ functionalEnvPre det𝓔 _ ρ₁ ρ₂

InferTyping :
  {T :   Set} (𝓣 : Typing T) (ri : Σ[ n   ] Σ[ γ  Context n ] T n)
  (ii : let (_ , γ , _) = ri in Usages γ × Usages γ) (o : Type)  Set
InferTyping 𝓣 (_ , _ , t) (Γ , Δ) A = 𝓣 Γ t A Δ