\begin{code}
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 Δ
\end{code}
