module Support where

open import Prelude
open import Unfinite
open import oc-Sets
open import Freshness
open import LocalClosedness

----------------------------------------------------------------------
-- Locally nameless sets [Definition 2.9]
----------------------------------------------------------------------
record lns (X : Set) : Set where
  constructor mklns
  field
    {{ocSet}} : oc X
    asupp : (x : X)  И a  𝔸 , a # x
    isupp : (x : X)   i   , i  x

open lns{{...}} public

infix 4 _atom-supports_
_atom-supports_ :
  {X : Set}
  {{_ : oc X}}
  (A : Fset 𝔸)
  (x : X)
   ----------
  Set
A atom-supports x =  a  a  A  a # x

----------------------------------------------------------------------
-- Locally nameless set of indices and atoms [Example 2.10]
----------------------------------------------------------------------
lnsℕ𝔸 : lns ℕ𝔸
ocSet {{lnsℕ𝔸}} = ocℕ𝔸
asupp {{lnsℕ𝔸}} (ι₁ i) = Иi Ø λ _  refl
asupp {{lnsℕ𝔸}} (ι₂ a) = Иi [ a ] и₂
  where
  и₂ : (b : 𝔸){{_ : b  [ a ]}}  b # ι₂ a
  и₂ b {{∉[]{{p}}}} rewrite p = refl
isupp {{lnsℕ𝔸}} (ι₁ i) = (i +1 , s₂)
  where
  s₂ : i +1  ι₁ i
  s₂ j {{p}} rewrite +1≤→≠ i j p = (new Ø , refl)
isupp {{lnsℕ𝔸}} (ι₂ a) = (0 , λ _  (a , refl))

----------------------------------------------------------------------
-- Lambda terms [Example 2.11]
----------------------------------------------------------------------
module LambdaTerm where
  data Lam : Set where
    var : ℕ𝔸  Lam
    lam : Lam  Lam
    app : Lam × Lam  Lam

  pattern bvar i = var (ι₁ i)
  pattern fvar a = var (ι₂ a)

  lam-inj : ∀{t t'}  lam t  lam t'  t  t'
  lam-inj refl = refl

  app-inj :
    {t₁ t₂ t₁' t₂' : Lam}
    (_ : app(t₁ , t₂)  app(t₁' , t₂'))
     ---------------------------------
    (t₁  t₁') × (t₂  t₂')
  app-inj refl = refl , refl

  -- Lam is an oc-set
  instance
    ocLam : oc Lam
    ocLam = mkoc opn cls ax₁ ax₂ ax₃ ax₄ ax₅ ax₆ ax₇ ax₈ ax₉
      where
      X = Lam
      opn :   𝔸  X  X
      opn i a (var v)   = var ((i ~> a) v)
      opn i a (lam t) = lam(opn (i +1) a t)
      opn i a (app(t , t')) = app(opn i a t , opn i a t')
      cls :   𝔸  X  X
      cls i a (var v)   = var ((i <~ a) v)
      cls i a (lam t) = lam(cls (i +1) a t)
      cls i a (app(t , t')) = app(cls i a t , cls i a t')
      ax₁ :
        (i : )
        (a b : 𝔸)
        (t : X)
         -----------------------------
        opn i a (opn i b t)  opn i b t
      ax₁ i a b (var v)
        rewrite oc₁ i a b v = refl
      ax₁ i a b (lam t)
        rewrite ax₁ (i + 1) a b t = refl
      ax₁ i a b (app(t , t'))
        rewrite ax₁ i a b t | ax₁ i a b t' = refl
      ax₂ :
        (i j : )
        (a : 𝔸)
        (t : X)
         -----------------------------
        cls i a (cls j a t)  cls j a t
      ax₂ i j a (var v)
        rewrite oc₂ i j a v = refl
      ax₂ i j a (lam t)
        rewrite ax₂ (i + 1) (j +1) a t  = refl
      ax₂ i j a (app(t , t'))
        rewrite ax₂ i j a t | ax₂ i j a t' =  refl
      ax₃ :
        (i : )
        (a : 𝔸)
        (t : X)
         -----------------------------
        cls i a (opn i a t)  cls i a t
      ax₃ i a (var v)
        rewrite oc₃ i a v = refl
      ax₃ i a (lam t)
        rewrite ax₃ (i + 1) a t = refl
      ax₃ i a (app(t , t'))
        rewrite ax₃ i a t | ax₃ i a t' = refl
      ax₄ :
        (i : )
        (a : 𝔸)
        (t : X)
         -----------------------------
          opn i a (cls i a t)  opn i a t
      ax₄ i a (var v)
        rewrite oc₄ i a v = refl
      ax₄ i a (lam t)
        rewrite ax₄ (i + 1) a t = refl
      ax₄ i a (app(t , t'))
        rewrite ax₄ i a t | ax₄ i a t' = refl
      ax₅ :
        (i j : )
        (a b : 𝔸)
        (t : X)
        {{_ : i  j}}
         ---------------------------------------
        opn i a (opn j b t)  opn j b (opn i a t)
      ax₅ i j a b (var v)
        rewrite oc₅ i j a b v {{it}} = refl
      ax₅ i j a b (lam t)
        rewrite ax₅ (i +1) (j +1) a b t {{+1≠ {i} it}} = refl
      ax₅ i j a b  (app(t , t'))
        rewrite ax₅ i j a b t {{it}} | ax₅ i j a b t' {{it}} = refl
      ax₆ :
        (i j : )
        (a b : 𝔸)
        (t : X)
          {{_ : a  b}}
         ---------------------------------------
        cls i a (cls j b t)  cls j b (cls i a t)
      ax₆ i j a b (var v)
        rewrite oc₆ i j a b v {{it}} = refl
      ax₆ i j a b (lam t)
        rewrite ax₆ (i +1) (j +1) a b t {{it}} = refl
      ax₆ i j a b (app(t , t'))
        rewrite ax₆ i j a b t {{it}} | ax₆ i j a b t' {{it}} = refl
      ax₇ :
        (i j : )
        (a b : 𝔸)
        (t : X)
        {{_ : i  j}}
        {{_ : a  b}}
         ---------------------------------------
          opn i a (cls j b t)  cls j b (opn i a t)
      ax₇ i j a b (var v)
        rewrite oc₇ i j a b v {{it}} {{it}} = refl
      ax₇ i j a b (lam t)
        rewrite ax₇ (i +1) (j +1) a b t {{+1≠ {i} it}} {{it}} = refl
      ax₇ i j a b (app(t , t'))
        rewrite ax₇ i j a b t {{it}} {{it}}
        | ax₇ i j a b t' {{it}} {{it}} = refl
      ax₈ :
        (i j : )
        (a b : 𝔸)
        (t : X)
         -----------------------------------------------------------
        opn i b (cls i a (opn j b t))  opn j b (cls j a (opn i a t))
      ax₈ i j a b (var v)
        rewrite oc₈ i j a b v = refl
      ax₈ i j a b (lam t)
        rewrite ax₈ (i +1) (j +1) a b t = refl
      ax₈ i j a b  (app(t , t'))
        rewrite ax₈ i j a b t | ax₈ i j a b t' = refl
      ax₉ :
        (i j : )
        (a b : 𝔸)
        (t : X)
         -----------------------------------------------------------
        cls j a (opn i a (cls j b t))  cls j b (opn i b (cls i a t))
      ax₉ i j a b (var v)
        rewrite oc₉ i j a b v = refl
      ax₉ i j a b (lam t)
        rewrite ax₉ (i +1) (j +1) a b t = refl
      ax₉ i j a b (app(t , t'))
        rewrite ax₉ i j a b t | ax₉ i j a b t' = refl


  -- Free variables deined inductively
  fv : Lam  Fset 𝔸
  fv (bvar _)      = Ø
  fv (fvar a)      = [ a ]
  fv (lam t)       = fv t
  fv (app(t , t')) = fv t  fv t'

  -- Freshness coincides with "not-a-free-variable-of"
  -- (cf. Proposition 4.2)
  fas₁ :
    (t : Lam)
    (a : 𝔸)
    (_ : a  fv t)
     ------------
    a # t
  fas₁ (bvar i) a p = refl
  fas₁ (fvar b) a _          with  a  b
  fas₁ (fvar _) _ _          | neq _ = refl
  fas₁ (fvar b) _ (∉[]{{p}}) | equ with ()¬≠ b p
  fas₁ (lam t) a p = ap lam p'
    where
    p' : (1 <~ a)t  t
    p' =
      proof
        (1 <~ a)t
      [ ap (1 <~ a) (fas₁ t a p) ]≡
      (1 <~ a)((0 <~ a)t)
      ≡[ oc₂ 1 0 a t ]
        (0 <~ a)t
      ≡[ fas₁ t a p ]
        t
      qed
  fas₁ (app(t , t')) a (∉∪{{p}}{{p'}})
    rewrite fas₁ t a p | fas₁ t' a p' = refl

  fas₂ :
    (t : Lam)
    (a : 𝔸)
    (_ : a # t)
     ---------
    a  fv t
  fas₂ (bvar _) _ _ = ∉Ø
  fas₂ (fvar b) a p  with a  b in eq
  fas₂ (fvar b) a _  | neq f = ∉[] {x = a} {b} {{dec-neq a b f}}
  fas₂ (fvar _) _ () | equ
  fas₂ (lam t) a p = fas₂ t a p'
    where
    p' : (0 <~ a)t  t
    p' =
      proof
        (0 <~ a)t
      [ ap (0 <~ a) (lam-inj p) ]≡
        (0 <~ a)((1 <~ a)t)
      ≡[ oc₂ 0 1 a t ]
        (1 <~ a)t
      ≡[ lam-inj p ]
        t
      qed
  fas₂ (app(t , t')) a p = ∉∪ {xs = fv t} {fv t'}
    {{fas₂ t  a (π₁ (app-inj p))}}
    {{fas₂ t' a (π₂ (app-inj p))}}

  -- Inductive closed-at-level predicate
  data lc-at :   Lam  Set where
    lc-at-bvar :
      {i j : }
      {{_ : j < i}}
       --------------
      lc-at i (bvar j)
    lc-at-fvar :
      {i : }
      {a : 𝔸}
       -------------
      lc-at i (fvar a)
    lc-at-lam :
      {i : }
      {t : Lam}
      (_ : lc-at (i +1) t)
       ------------------
      lc-at i (lam t)
    lc-at-app :
      {i : }
      {t t' : Lam}
      (_ : lc-at i t)
      (_ : lc-at i t')
       -------------------
      lc-at i (app(t , t'))

  -- Local closedness coincides with closed-at-level
  -- (cf. Proposition 4.3)
  fis₁ :
    (i : )
    (t : Lam)
    (p : lc-at i t)
     -------------
    i  t
  fis₁ i (bvar j) lc-at-bvar k
    rewrite <→≠ j k (<≤ it it) = (new Ø , refl)
  fis₁ _ (fvar _) lc-at-fvar _ = (new Ø , refl)
  fis₁ i (lam t) (lc-at-lam p) j  =
    (new Ø , ap lam (≻3 {a = new Ø} (fis₁ (i +1) t p) (+1≤ it)))
  fis₁ i (app (t , t')) (lc-at-app p p') j
    with e≻3 {a = new Ø} (fis₁ i t p) it
    | e'≻3 {a = new Ø} (fis₁ i t' p') it
    = (new Ø , ap₂  x y  app (x , y)) e e')

  fis₂ :
    (i : )
    (t : Lam)
    (p : i  t)
     ---------
    lc-at i t
  fis₂ i (bvar j) p = lc-at-bvar{{trich' ¬i≤j}}
    where
    ¬i≤j : ¬ (i  j)
    ¬i≤j i≤j
      with (_ , q)p j {{i≤j}}
      rewrite dec-equ j
      with ()q
  fis₂ _ (fvar _) _ = lc-at-fvar
  fis₂ i (lam t) p = lc-at-lam (fis₂ (i +1) t i+1≻t)
    where
    i+1≻t : i +1  t
    i+1≻t _      {{≤refl}}
      with (a , e)p i {{≤refl}}                  = (a , lam-inj e)
    i+1≻t (j +1) {{≤+1 q}}
      with (a , e)p j  {{≤trans (≤+1 ≤refl) q}} = (a , lam-inj e)
  fis₂ i (app(t , t')) p = lc-at-app (fis₂ i t i≻t) (fis₂ i t' i≻t')
    where
    i≻t : i  t
    i≻t j {{q}} with (a , e)p j {{q}} = (a , π₁ (app-inj e))
    i≻t' : i  t'
    i≻t' j {{q}} with (a , e)p j {{q}} = (a , π₂ (app-inj e))

  -- Boundvariables are not locally closed
  ¬0≻bvar :  i  ¬(0  bvar i)
  ¬0≻bvar i p with fis₂ 0 (bvar i) p
  ... | lc-at-bvar {{q}} with ()q

  -- Free variables are locally closed
  0≻fvar :  a  0  fvar a
  0≻fvar a = fis₁ 0 (fvar a) lc-at-fvar

  -- Local closure of lambda abstractions
  0≻lam :  t  1  t  0  lam t
  0≻lam t p = fis₁ 0 (lam t) (lc-at-lam (fis₂ 1 t p))

  0≻lam' :  t  0  lam t  1  t
  0≻lam' t p with fis₂ 0 (lam t) p
  ... | lc-at-lam q = fis₁ 1 t q

  -- Local closure for application terms
  0≻app :  t t'  0  t  0  t'  0  app(t , t')
  0≻app t t' p p' =
    fis₁ 0 (app(t , t')) (lc-at-app (fis₂ 0 t p) (fis₂ 0 t' p'))
  0≻app' :  t t'  0  app(t , t')  (0  t) × (0  t')
  0≻app' t t' p with fis₂ 0 (app(t , t')) p
  ... | lc-at-app q q' = (fis₁ 0 t q , fis₁ 0 t' q')


  -- Lam is a locally nameless set
  instance
    lnsLam : lns Lam
    ocSet {{lnsLam}} = ocLam
    asupp {{lnsLam}} t = Иi (fv t) λ a {{p}}  fas₁ t a p
    isupp {{lnsLam}} t = (lv t , lv≻ t)
      where
      lv : Lam  
      lv (bvar i)    = i +1
      lv (fvar _)    = 0
      lv (lam t)    = lv t
      lv (app(t , t')) = max (lv t) (lv t')

      lv≻ : (t : Lam)  lv t  t
      lv≻ (bvar i) = fis₁ (i +1) (bvar i) (lc-at-bvar{{<+1 ≤refl}})
      lv≻ (fvar a) = fis₁ 0 (fvar a) lc-at-fvar
      lv≻ (lam t) j with (a , e)lv≻ t (j +1) {{≤+1 it}} = (a , ap lam e)
      lv≻ (app(t , t')) j
        with (a , e)lv≻ t j {{≤trans ≤max₁ it}}
        | (a' , e')lv≻ t' j {{≤trans ≤max₂ it}} =
        (a , ap₂  x y  app (x , y)) e (≻2 {b = a} e'))

----------------------------------------------------------------------
-- Properties of open/close operations wrt freshness [Lemma 2.12]
----------------------------------------------------------------------
module _
  {X : Set}
  {{_ : oc X}}
  {i : }
  {a : 𝔸}
  {A : Fset 𝔸}
  {x : X}
  (f : A atom-supports x)
  where
  ~>atom-supports : A  [ a ] atom-supports (i ~> a)x
  ~>atom-supports b (∉∪{{_}}{{∉[]}}) =
    #1 {i = i +1}{0}
    (proof
       ((i +1) <~ b) ((i ~> a) x)
     [ oc₇ i (i +1) a b x {{ ≠+1 i}} {{symm≠ b a it}} ]≡
       (i ~> a) ((i +1 <~ b)x)
     ≡[ ap (i ~> a) (#1 {j = i +1} (f b it)) ]
       (i ~> a) x
     qed)

  <~atom-supports : A -[ a ] atom-supports (i <~ a)x
  <~atom-supports b p with b  a
  ... | neq g =
    proof
      (0 <~ b) ((i <~ a) x)
    ≡[ oc₆ 0 i b a x {{¬≡→≠ g}} ]
      (i <~ a) ((0 <~ b)x)
    ≡[ ap (i <~ a) (f b (∉-[] p (¬≡→≠ g))) ]
      (i <~ a) x
    qed
  ... | equ = oc₂ 0 i b x

#<~ :
  {X : Set}
  {{_ : oc X}}
  (i : )
  (a b : 𝔸)
  (x : X)
  {{_ : a # x}}
   -----------
  a # (i <~ b)x
#<~ i a b x with a  b
... | equ = oc₂ _ _ _ _
... | neq f =
  proof
    (0 <~ a) ((i <~ b) x)
  ≡[ oc₆ _ _ _ _ _ {{¬≡→≠ f}} ]
    (i <~ b) ((0 <~ a) x)
  ≡[ ap (i <~ b) (#2 it) ]
    (i <~ b)x
  qed

#~> :
  {X : Set}
  {{_ : oc X}}
  (i : )
  (a b : 𝔸)
  (x : X)
  {{_ : a # x}}
  {{_ : a  b}}
   -----------
  a # (i ~> b)x
#~> i a b x = #3 {i = i +1}
  (proof
     (i +1 <~ a)((i ~> b)x)
   [ oc₇ _ _ _ _ _ {{≠+1 i}}{{symm≠ a b it}} ]≡
     (i ~> b)((i +1 <~ a)x)
   ≡[ ap (i ~> b) (#2 it) ]
     (i ~> b)x
   qed)

----------------------------------------------------------------------
-- Properties of open/close operations wrt local closure [Lemma 2.13]
----------------------------------------------------------------------
module _
  {X : Set}
  {{_ : oc X}}
  {i : }
  {a : 𝔸}
  {x : X}
  where
  ~>index-supports : -- Equation (10)
    {j : }
    (_ : j  x)
     -----------
    j  (i ~> a)x
  ~>index-supports p k with k  i
  ... | neq f = (a ,
    (proof
       (k ~> a)((i ~> a) x)
     ≡[ oc₅ _ _ _ _ _ {{¬≡→≠ f}} ]
       (i ~> a)((k ~> a) x)
     ≡[ ap (i ~> a) (≻3 p it) ]
       (i ~> a) x
     qed))
  ... | equ = (a , oc₁ _ _ _ _)

  ~>index-supports' : -- Equation (11)
    i +1  x  i  (i ~> a) x
  ~>index-supports' p j with j  i
  ... | neq f = (a ,
    (proof
       (j ~> a)((i ~> a) x)
     ≡[ oc₅ _ _ _ _ _ {{¬≡→≠ f}}  ]
       (i ~> a) ((j ~> a) x)
     ≡[ ap (i ~> a) (≻3 p (≤≠ it (symm≠ j i (¬≡→≠ f)))) ]
       (i ~> a) x
     qed))
  ... | equ = (a , oc₁ _ _ _ _)

  <~index-supports : -- Equation (12)
    {j : }
    (_ : j  x)
     ------------------------
    max j (i +1)  (i <~ a) x
  <~index-supports p k with (b , ∉[])fresh{𝔸} [ a ] =
    (b ,
      (proof
        (k ~> b)((i <~ a) x)
      ≡[ oc₇ _ _ _ _ _ {{+1≤→≠ i k (≤trans ≤max₂ it)}} ]
        (i <~ a)((k ~> b) x)
      ≡[ ap (i <~ a) (≻3 p (≤trans ≤max₁ it)) ]
        (i <~ a) x
      qed))