{-# OPTIONS --cubical-compatible --safe #-}
module Data.Fin.Substitution where
open import Data.Nat.Base hiding (_⊔_; _/_)
open import Data.Fin.Base using (Fin; zero; suc)
open import Data.Vec.Base
open import Function.Base as Fun using (flip)
open import Relation.Binary.Construct.Closure.ReflexiveTransitive
  as Star using (Star; ε; _◅_)
open import Level using (Level; _⊔_; 0ℓ)
open import Relation.Unary using (Pred)
private
  variable
    ℓ ℓ₁ ℓ₂ : Level
    k m n o : ℕ
Sub : Pred ℕ ℓ → ℕ → ℕ → Set ℓ
Sub T m n = Vec (T n) m
Subs : Pred ℕ ℓ → ℕ → ℕ → Set ℓ
Subs T = flip (Star (flip (Sub T)))
record Simple (T : Pred ℕ ℓ) : Set ℓ where
  infix  10 _↑
  infixl 10 _↑⋆_ _↑✶_
  field
    var    : ∀ {n} → Fin n → T n      
    weaken : ∀ {n} → T n → T (suc n)  
  
  _↑ : Sub T m n → Sub T (suc m) (suc n)
  ρ ↑ = var zero ∷ map weaken ρ
  _↑⋆_ : Sub T m n → ∀ k → Sub T (k + m) (k + n)
  ρ ↑⋆ zero  = ρ
  ρ ↑⋆ suc k = (ρ ↑⋆ k) ↑
  _↑✶_ : Subs T m n → ∀ k → Subs T (k + m) (k + n)
  ρs ↑✶ k = Star.gmap (_+_ k) (λ ρ → ρ ↑⋆ k) ρs
  
  id : Sub T n n
  id {zero}  = []
  id {suc n} = id ↑
  
  wk⋆ : ∀ k → Sub T n (k + n)
  wk⋆ zero    = id
  wk⋆ (suc k) = map weaken (wk⋆ k)
  wk : Sub T n (suc n)
  wk = wk⋆ 1
  
  sub : T n → Sub T (suc n) n
  sub t = t ∷ id
record Application (T₁ : Pred ℕ ℓ₁) (T₂ : Pred ℕ ℓ₂) : Set (ℓ₁ ⊔ ℓ₂) where
  infixl 8 _/_ _/✶_
  infixl 9 _⊙_
  
  field _/_ : ∀ {m n} → T₁ m → Sub T₂ m n → T₁ n
  
  _⊙_ : Sub T₁ m n → Sub T₂ n o → Sub T₁ m o
  ρ₁ ⊙ ρ₂ = map (_/ ρ₂) ρ₁
  
  _/✶_ : T₁ m → Subs T₂ m n → T₁ n
  _/✶_ = Star.gfold Fun.id _ (flip _/_) {k = zero}
record Subst (T : Pred ℕ ℓ) : Set ℓ where
  field
    simple      : Simple      T
    application : Application T T
  open Simple      simple      public
  open Application application public
  
  ⨀ : Subs T m n → Sub T m n
  ⨀ ε        = id
  ⨀ (ρ ◅ ε)  = ρ  
  ⨀ (ρ ◅ ρs) = ⨀ ρs ⊙ ρ
record Lift (T₁ : Pred ℕ ℓ₁) (T₂ : Pred ℕ ℓ₂) : Set (ℓ₁ ⊔ ℓ₂) where
  field
    simple : Simple T₁
    lift   : ∀ {n} → T₁ n → T₂ n
  open Simple simple public
module VarSubst where
  subst : Subst Fin
  subst = record
    { simple      = record { var = Fun.id; weaken = suc }
    ; application = record { _/_ = flip lookup }
    }
  open Subst subst public
record TermSubst (T : Pred ℕ 0ℓ) : Set₁ where
  field
    var : ∀ {n} → Fin n → T n
    app : ∀ {T′ : Pred ℕ 0ℓ} → Lift T′ T → ∀ {m n} → T m → Sub T′ m n → T n
  module Lifted {T′ : Pred ℕ 0ℓ} (lift : Lift T′ T) where
    application : Application T T′
    application = record { _/_ = app lift }
    open Lift lift public
    open Application application public
  varLift : Lift Fin T
  varLift = record { simple = VarSubst.simple; lift = var }
  infixl 8 _/Var_
  _/Var_ : T m → Sub Fin m n → T n
  _/Var_ = app varLift
  simple : Simple T
  simple = record
    { var    = var
    ; weaken = λ t → t /Var VarSubst.wk
    }
  termLift : Lift T T
  termLift = record { simple = simple; lift = Fun.id }
  subst : Subst T
  subst = record
    { simple      = simple
    ; application = Lifted.application termLift
    }
  open Subst subst public hiding (var; simple)