------------------------------------------------------------------------
-- Properties of the extraction function.
------------------------------------------------------------------------

open import Graded.Modality
open import Tools.PropositionalEquality as PE hiding (subst)

module Graded.Erasure.Extraction.Properties
  {a} {M : Set a}
  (𝕄 : Modality M)
  where

open Modality 𝕄

open import Graded.Modality.Nr-instances
open import Graded.Modality.Properties 𝕄

open import Graded.Erasure.Extraction 𝕄
open import Graded.Erasure.Target as T
  hiding (refl; trans)
open import Graded.Erasure.Target.Non-terminating
open import Graded.Erasure.Target.Properties

open import Definition.Untyped M as U
  hiding (Term; wk; _[_]; _[_,_]₁₀; liftSubst)
open import Definition.Untyped.Erased 𝕄 using (substᡉ; Jᡉ)
open import Definition.Untyped.Identity 𝕄 using (subst)
open import Definition.Untyped.Omega M as O using (Ξ©)
import Definition.Untyped.Properties M as UP

open import Graded.Context 𝕄
open import Graded.Context.Properties 𝕄
import Graded.Usage 𝕄 as MU
import Graded.Usage.Properties 𝕄 as MUP
import Graded.Usage.Properties.Has-well-behaved-zero 𝕄 as MUP𝟘
open import Graded.Usage.Restrictions 𝕄
open import Graded.Mode 𝕄

open import Tools.Bool
open import Tools.Empty
open import Tools.Fin
open import Tools.Function
open import Tools.List
open import Tools.Nat as Nat using (Nat; 1+) renaming (_+_ to _+ⁿ_)
open import Tools.Product
open import Tools.Relation
open import Tools.Sum using (inj₁; injβ‚‚)

import Tools.Reasoning.Equivalence
import Tools.Reasoning.PartialOrder
import Tools.Reasoning.PropositionalEquality

private
  variable
    b : Bool
    Ξ± m n : Nat
    𝕋 π•Œ : Set _
    A A₁ Aβ‚‚ t t₁ tβ‚‚ t₃ tβ‚„ u : U.Term n
    v v₁ vβ‚‚ : T.Term n
    ts : DCon (U.Term _) _
    βˆ‡ : List (T.Term n)
    Οƒ : U.Subst m n
    Οƒβ€² : T.Subst m n
    ρ : Wk _ _
    Ξ³ : Conβ‚˜ n
    x : Fin n
    p q r : M
    k : Strength
    s : Strictness

-- Some lemmas related to how eraseβ€² computes.

opaque

  prod-𝟘 :
    βˆ€ k β†’ p PE.≑ 𝟘 β†’
    eraseβ€² b s (U.prod k p t u) PE.≑ eraseβ€² b s u
  prod-𝟘 {p} _ pβ‰‘πŸ˜ with is-𝟘? p
  … | yes _  = PE.refl
  … | no pβ‰’πŸ˜ = βŠ₯-elim (pβ‰’πŸ˜ pβ‰‘πŸ˜)

opaque

  prod-Ο‰ :
    βˆ€ k β†’ p PE.β‰’ 𝟘 β†’
    eraseβ€² b s (U.prod k p t u) PE.≑
    prod⟨ s ⟩ (eraseβ€² b s t) (eraseβ€² b s u)
  prod-Ο‰ {p} _ pβ‰’πŸ˜ with is-𝟘? p
  … | yes pβ‰‘πŸ˜ = βŠ₯-elim (pβ‰’πŸ˜ pβ‰‘πŸ˜)
  … | no _    = PE.refl

opaque

  fst-β‰’πŸ˜ :
    p PE.β‰’ 𝟘 β†’
    eraseβ€² b s (U.fst p t) PE.≑ T.fst (eraseβ€² b s t)
  fst-β‰’πŸ˜ {p} pβ‰’πŸ˜ with is-𝟘? p
  … | yes pβ‰‘πŸ˜ = βŠ₯-elim (pβ‰’πŸ˜ pβ‰‘πŸ˜)
  … | no _    = PE.refl

opaque

  snd-𝟘 :
    p PE.≑ 𝟘 β†’
    eraseβ€² b s (U.snd p t) PE.≑ eraseβ€² b s t
  snd-𝟘 {p} pβ‰‘πŸ˜ with is-𝟘? p
  ... | yes _  = PE.refl
  ... | no pβ‰’πŸ˜ = βŠ₯-elim (pβ‰’πŸ˜ pβ‰‘πŸ˜)

opaque

  snd-Ο‰ :
    p PE.β‰’ 𝟘 β†’
    eraseβ€² b s (U.snd p t) PE.≑ T.snd (eraseβ€² b s t)
  snd-Ο‰ {p} pβ‰’πŸ˜ with is-𝟘? p
  … | yes pβ‰‘πŸ˜ = βŠ₯-elim (pβ‰’πŸ˜ pβ‰‘πŸ˜)
  … | no _    = PE.refl

opaque

  prodrec-Ο‰ :
    βˆ€ q A β†’ r PE.β‰’ 𝟘 β†’
    eraseβ€² b s (U.prodrec r p q A t u) PE.≑
    erase-prodrecω s p (erase′ b s t) (erase′ b s u)
  prodrec-Ο‰ {r} _ _ rβ‰’πŸ˜ with is-𝟘? r
  ... | yes rβ‰‘πŸ˜ = βŠ₯-elim (rβ‰’πŸ˜ rβ‰‘πŸ˜)
  ... | no _    = PE.refl

opaque

  prodrec-𝟘 :
    βˆ€ q A β†’
    eraseβ€² b s (U.prodrec 𝟘 p q A t u) ≑
    eraseβ€² b s u T.[ loop s , loop s ]₁₀
  prodrec-𝟘 _ _ with is-𝟘? 𝟘
  … | yes _  = refl
  … | no πŸ˜β‰’πŸ˜ = βŠ₯-elim (πŸ˜β‰’πŸ˜ refl)

opaque

  prodrec-β‰’πŸ˜-𝟘 :
    βˆ€ q A β†’ r β‰’ 𝟘 β†’
    eraseβ€² b s (U.prodrec r 𝟘 q A t u) ≑
    T.lam (eraseβ€² b s u T.[ T.sgSubst (loop s) T.⇑ ]) T.∘⟨ s ⟩
      eraseβ€² b s t
  prodrec-β‰’πŸ˜-𝟘 {b} {s} {t} {u} q A rβ‰’πŸ˜
    rewrite prodrec-Ο‰ {b = b} {s = s} {p = 𝟘} {t = t} {u = u} q A rβ‰’πŸ˜
    with is-𝟘? 𝟘
  … | yes _  = refl
  … | no πŸ˜β‰’πŸ˜ = βŠ₯-elim (πŸ˜β‰’πŸ˜ refl)

opaque

  prodrec-β‰’πŸ˜-β‰’πŸ˜ :
    βˆ€ q A β†’ r β‰’ 𝟘 β†’ p β‰’ 𝟘 β†’
    eraseβ€² b s (U.prodrec r p q A t u) ≑
    T.prodrec (eraseβ€² b s t) (eraseβ€² b s u)
  prodrec-β‰’πŸ˜-β‰’πŸ˜ {p} {b} {s} {t} {u} q A rβ‰’πŸ˜ pβ‰’πŸ˜
    rewrite prodrec-Ο‰ {b = b} {s = s} {p = p} {t = t} {u = u} q A rβ‰’πŸ˜
    with is-𝟘? p
  … | no _    = refl
  … | yes pβ‰‘πŸ˜ = βŠ₯-elim (pβ‰’πŸ˜ pβ‰‘πŸ˜)

opaque

  unitrec-𝟘 :
    βˆ€ l q A β†’ p PE.≑ 𝟘 β†’
    eraseβ€² b s (U.unitrec l p q A t u) PE.≑ eraseβ€² b s u
  unitrec-𝟘 {p} _ _ _ pβ‰‘πŸ˜ with is-𝟘? p
  … | yes _  = PE.refl
  … | no pβ‰’πŸ˜ = βŠ₯-elim (pβ‰’πŸ˜ pβ‰‘πŸ˜)

opaque

  unitrec-Ο‰ :
    βˆ€ l q A β†’ p PE.β‰’ 𝟘 β†’
    eraseβ€² b s (U.unitrec l p q A t u) PE.≑
    T.unitrec (eraseβ€² b s t) (eraseβ€² b s u)
  unitrec-Ο‰ {p} _ _ _ pβ‰’πŸ˜ with is-𝟘? p
  … | yes pβ‰‘πŸ˜ = βŠ₯-elim (pβ‰’πŸ˜ pβ‰‘πŸ˜)
  … | no _    = PE.refl

opaque

  ∘-β‰’πŸ˜ :
    p β‰’ 𝟘 β†’
    eraseβ€² b s (t U.∘⟨ p ⟩ u) ≑ eraseβ€² b s t T.∘⟨ s ⟩ eraseβ€² b s u
  ∘-β‰’πŸ˜ {p} pβ‰’πŸ˜ with is-𝟘? p
  … | no _    = refl
  … | yes pβ‰‘πŸ˜ = βŠ₯-elim $ pβ‰’πŸ˜ pβ‰‘πŸ˜

opaque

  ∘-𝟘 : eraseβ€² b s (t U.∘⟨ 𝟘 ⟩ u) ≑ app-πŸ˜β€² b s (eraseβ€² b s t)
  ∘-𝟘 with is-𝟘? 𝟘
  … | yes _  = refl
  … | no πŸ˜β‰’πŸ˜ = βŠ₯-elim $ πŸ˜β‰’πŸ˜ refl

opaque

  lam-β‰’πŸ˜ :
    βˆ€ b β†’ p β‰’ 𝟘 β†’
    eraseβ€² b s (U.lam p t) ≑ T.lam (eraseβ€² b s t)
  lam-β‰’πŸ˜     false _   = refl
  lam-β‰’πŸ˜ {p} true  pβ‰’πŸ˜ with is-𝟘? p
  … | no _    = refl
  … | yes pβ‰‘πŸ˜ = βŠ₯-elim $ pβ‰’πŸ˜ pβ‰‘πŸ˜

opaque

  lam-𝟘-keep :
    (t : U.Term (1+ n)) β†’
    eraseβ€² false s (U.lam 𝟘 t) ≑ T.lam (eraseβ€² false s t)
  lam-𝟘-keep _ with is-𝟘? 𝟘
  … | yes _  = refl
  … | no πŸ˜β‰’πŸ˜ = βŠ₯-elim $ πŸ˜β‰’πŸ˜ refl

opaque

  lam-𝟘-remove :
    eraseβ€² true s (U.lam 𝟘 t) ≑ eraseβ€² true s t T.[ loop s ]β‚€
  lam-𝟘-remove with is-𝟘? 𝟘
  … | yes _  = refl
  … | no πŸ˜β‰’πŸ˜ = βŠ₯-elim $ πŸ˜β‰’πŸ˜ refl

opaque
  unfolding subst

  erase-subst :
    eraseβ€² b s (subst p A₁ Aβ‚‚ t₁ tβ‚‚ t₃ tβ‚„) PE.≑ eraseβ€² b s tβ‚„
  erase-subst = PE.refl

opaque
  unfolding substᡉ subst

  erase-substᡉ :
    eraseβ€² b s (substᡉ k A₁ Aβ‚‚ t₁ tβ‚‚ t₃ tβ‚„) PE.≑ eraseβ€² b s tβ‚„
  erase-substᡉ = PE.refl

opaque
  unfolding Jᡉ substᡉ subst

  erase-Jᡉ : eraseβ€² b s (Jᡉ k A₁ t₁ Aβ‚‚ tβ‚‚ t₃ tβ‚„) PE.≑ eraseβ€² b s tβ‚‚
  erase-Jᡉ = PE.refl

opaque

  -- The result of weakening loop?Β s is loop?Β s.

  wk-loop? : βˆ€ s β†’ wk ρ (loop? s) ≑ loop? s
  wk-loop? non-strict = wk-loop
  wk-loop? strict     = refl

opaque

  -- The result of applying a substitution to loop?Β s is loop?Β s.

  loop?-[] : βˆ€ s β†’ loop? s T.[ Οƒβ€² ] ≑ loop? s
  loop?-[] non-strict = loop-[]
  loop?-[] strict     = refl

opaque

  -- The term loop?Β s is closed.

  loop?-closed : βˆ€ s β†’ Β¬ HasX x (loop? s)
  loop?-closed non-strict = loop-closed
  loop?-closed strict     = Ξ» ()

opaque

  -- The term loop? {n = n} s satisfies Value⟨ s ⟩.

  Value⟨⟩-loop? : βˆ€ s β†’ Value⟨ s ⟩ (loop? {n = n} s)
  Value⟨⟩-loop? non-strict = _
  Value⟨⟩-loop? strict     = T.β†―

opaque

  -- A reduction lemma for app-πŸ˜β€².

  app-πŸ˜β€²-subst : βˆ‡ T.⊒ v₁ β‡’ vβ‚‚ β†’ βˆ‡ T.⊒ app-πŸ˜β€² b s v₁ β‡’ app-πŸ˜β€² b s vβ‚‚
  app-πŸ˜β€²-subst {b = true}  v₁⇒vβ‚‚ = v₁⇒vβ‚‚
  app-πŸ˜β€²-subst {b = false} v₁⇒vβ‚‚ = app-subst v₁⇒vβ‚‚

-- The functions wk ρ/U.wk ρ and eraseβ€²Β bΒ s commute.

wk-erase-comm : (ρ : U.Wk m n) (t : U.Term n)
              β†’ wk ρ (eraseβ€² b s t) ≑ eraseβ€² b s (U.wk ρ t)
wk-erase-comm _ (var _) = refl
wk-erase-comm _ (defn _) = refl
wk-erase-comm {s} _ (U _) = wk-loop? s
wk-erase-comm {s} _ (ΠΣ⟨ _ ⟩ _ , _ β–· _ β–Ή _) = wk-loop? s
wk-erase-comm {b = true} {s} ρ (U.lam p t) with is-𝟘? p
... | no _  = cong T.lam $ wk-erase-comm _ t
... | yes _ =
  wk ρ (eraseβ€² true s t T.[ loop s ]β‚€)                β‰‘βŸ¨ wk-Ξ² (eraseβ€² _ _ t) ⟩
  wk (lift ρ) (eraseβ€² true s t) T.[ wk ρ (loop s) ]β‚€  β‰‘βŸ¨ cong (wk _ (eraseβ€² _ _ t) T.[_]β‚€) wk-loop ⟩
  wk (lift ρ) (eraseβ€² true s t) T.[ loop s ]β‚€         β‰‘βŸ¨ cong T._[ _ ]β‚€ $ wk-erase-comm _ t ⟩
  eraseβ€² true s (U.wk (lift ρ) t) T.[ loop s ]β‚€       ∎
  where
  open Tools.Reasoning.PropositionalEquality
wk-erase-comm {b = false} ρ (U.lam p t) =
  cong T.lam (wk-erase-comm (lift ρ) t)
wk-erase-comm ρ (t U.∘⟨ p ⟩ u) with is-𝟘? p
wk-erase-comm {b = false} {s} _ (t U.∘⟨ _ ⟩ _) | yes _ =
  congβ‚‚ T._∘⟨ _ ⟩_ (wk-erase-comm _ t) (wk-loop? s)
wk-erase-comm {b = true} _ (t U.∘⟨ _ ⟩ _) | yes _ =
  wk-erase-comm _ t
wk-erase-comm _ (t U.∘⟨ _ ⟩ u) | no _ =
  congβ‚‚ T._∘⟨ _ ⟩_ (wk-erase-comm _ t) (wk-erase-comm _ u)
wk-erase-comm {b} {s} ρ (U.prod _ p t u) with is-𝟘? p
... | yes _ = wk-erase-comm ρ u
... | no _ =
  wk ρ (prod⟨ s ⟩ (eraseβ€² b s t) (eraseβ€² b s u))             β‰‘βŸ¨ wk-prod⟨⟩ ⟩
  prod⟨ s ⟩ (wk ρ (eraseβ€² b s t)) (wk ρ (eraseβ€² b s u))      β‰‘βŸ¨ congβ‚‚ prod⟨ _ ⟩ (wk-erase-comm _ t) (wk-erase-comm _ u) ⟩
  prod⟨ s ⟩ (eraseβ€² b s (U.wk ρ t)) (eraseβ€² b s (U.wk ρ u))  ∎
  where
  open Tools.Reasoning.PropositionalEquality
wk-erase-comm ρ (U.fst p t) with is-𝟘? p
... | yes _ = wk-loop
... | no _ = cong T.fst (wk-erase-comm ρ t)
wk-erase-comm ρ (U.snd p t) with is-𝟘? p
... | yes _ = wk-erase-comm ρ t
... | no _ = cong T.snd (wk-erase-comm ρ t)
wk-erase-comm {b} {s} ρ (U.prodrec r p _ A t u) with is-𝟘? r
... | yes _ =
  wk ρ (eraseβ€² b s u [ loop s , loop s ]₁₀)                              β‰‘βŸ¨ wk-Ξ²-doubleSubst _ (eraseβ€² _ _ u) _ _ ⟩
  wk (lift (lift ρ)) (eraseβ€² b s u) [ wk ρ (loop s) , wk ρ (loop s) ]₁₀  β‰‘βŸ¨ cong₃ _[_,_]₁₀ (wk-erase-comm _ u) wk-loop wk-loop ⟩
  eraseβ€² b s (U.wk (lift (lift ρ)) u) [ loop s , loop s ]₁₀              ∎
  where
  open Tools.Reasoning.PropositionalEquality
... | no _ with is-𝟘? p
... | yes _ =
  T.lam (wk (lift ρ) (eraseβ€² b s u T.[ liftSubst (T.sgSubst (loop s)) ]))
    T.∘⟨ s ⟩ wk ρ (eraseβ€² b s t)                                           β‰‘βŸ¨ cong (Ξ» u β†’ T.lam u T.∘⟨ _ ⟩ _) $
                                                                              wk-lift-Ξ² (eraseβ€² _ _ u) ⟩
  T.lam (wk (lift (lift ρ)) (eraseβ€² b s u)
           T.[ liftSubst (T.sgSubst (wk ρ (loop s))) ])
    T.∘⟨ s ⟩ wk ρ (eraseβ€² b s t)                                           β‰‘βŸ¨ cong₃ (Ξ» u v t β†’ T.lam (u T.[ T.liftSubst (T.sgSubst v) ]) T.∘⟨ _ ⟩ t)
                                                                                (wk-erase-comm _ u)
                                                                                wk-loop
                                                                                (wk-erase-comm _ t) ⟩
  T.lam (eraseβ€² b s (U.wk (lift (lift ρ)) u)
           T.[ liftSubst (T.sgSubst (loop s)) ])
    T.∘⟨ s ⟩ eraseβ€² b s (U.wk ρ t)                                         ∎
  where
  open Tools.Reasoning.PropositionalEquality
... | no _ = congβ‚‚ T.prodrec (wk-erase-comm ρ t)
                   (wk-erase-comm (lift (lift ρ)) u)
wk-erase-comm {s} _ β„• = wk-loop? s
wk-erase-comm ρ U.zero = refl
wk-erase-comm {b} {s} ρ (U.suc t) =
  wk ρ (suc⟨ s ⟩ (eraseβ€² b s t))    β‰‘βŸ¨ wk-suc⟨⟩ ⟩
  suc⟨ s ⟩ (wk ρ (eraseβ€² b s t))    β‰‘βŸ¨ cong suc⟨ _ ⟩ (wk-erase-comm _ t) ⟩
  suc⟨ s ⟩ (eraseβ€² b s (U.wk ρ t))  ∎
  where
  open Tools.Reasoning.PropositionalEquality
wk-erase-comm ρ (U.natrec p q r A z s n) =
  cong₃ T.natrec (wk-erase-comm ρ z)
                 (wk-erase-comm (lift (lift ρ)) s)
                 (wk-erase-comm ρ n)
wk-erase-comm {s} _ Unit! = wk-loop? s
wk-erase-comm ρ U.star! = refl
wk-erase-comm ρ (U.unitrec _ p _ _ t u)
  with is-𝟘? p
... | yes _ =
  wk-erase-comm _ u
... | no _ =
  congβ‚‚ T.unitrec (wk-erase-comm ρ t)
                  (wk-erase-comm ρ u)
wk-erase-comm {s} _ Empty = wk-loop? s
wk-erase-comm _ (emptyrec _ _ _) = wk-loop
wk-erase-comm {s} _ (Id _ _ _) = wk-loop? s
wk-erase-comm {s} _ U.rfl = wk-loop? s
wk-erase-comm _ (J _ _ _ _ _ u _ _) = wk-erase-comm _ u
wk-erase-comm _ (K _ _ _ _ u _) = wk-erase-comm _ u
wk-erase-comm {s} _ ([]-cong _ _ _ _ _) = wk-loop? s

-- Lifting substitutions commute with erase

liftSubst-erase-comm :
  (x : Fin (1+ n)) β†’
  liftSubst (eraseSubstβ€² b s Οƒ) x ≑ eraseSubstβ€² b s (U.liftSubst Οƒ) x
liftSubst-erase-comm     x0     = refl
liftSubst-erase-comm {Οƒ} (_ +1) = wk-erase-comm _ (Οƒ _)

-- Multiple lifts commutes with erase

liftSubsts-erase-comm :
  (k : Nat) (x : Fin (k +ⁿ n)) β†’
  T.liftSubstn (eraseSubstβ€² b s Οƒ) k x ≑
  eraseSubstβ€² b s (U.liftSubstn Οƒ k) x
liftSubsts-erase-comm 0 x = refl
liftSubsts-erase-comm (1+ k) x0 = refl
liftSubsts-erase-comm {b} {s} {Οƒ} (1+ k) (x +1) =
  T.wk1 (T.liftSubstn (eraseSubstβ€² b s Οƒ) k x)          β‰‘βŸ¨ cong T.wk1 $ liftSubsts-erase-comm k _ ⟩
  T.wk1 (eraseSubstβ€² b s (U.liftSubstn Οƒ k) x)          β‰‘βŸ¨βŸ©
  wk (step id) (eraseSubstβ€² b s (U.liftSubstn Οƒ k) x)   β‰‘βŸ¨ wk-erase-comm _ (U.liftSubstn Οƒ _ _) ⟩
  eraseβ€² b s (U.wk (U.step U.id) (U.liftSubstn Οƒ k x))  β‰‘βŸ¨βŸ©
  eraseSubstβ€² b s (U.liftSubstn Οƒ (1+ k)) (x +1)        ∎
  where
  open Tools.Reasoning.PropositionalEquality

opaque

  -- A substitution lemma for app-πŸ˜β€².

  app-𝟘-[] :
    (t : T.Term n) β†’
    app-πŸ˜β€² b s t T.[ Οƒβ€² ] ≑
    app-πŸ˜β€² b s (t T.[ Οƒβ€² ])
  app-𝟘-[] {b = true}      _ = refl
  app-𝟘-[] {b = false} {s} _ = cong (T._∘⟨_⟩_ _ _) $ loop?-[] s

-- Substitution commutes with eraseβ€²Β bΒ s (modulo the translation of
-- the substitution to the target language).

subst-erase-comm :
  (Οƒ : U.Subst m n) (t : U.Term n) β†’
  eraseβ€² b s t T.[ eraseSubstβ€² b s Οƒ ] ≑ eraseβ€² b s (t U.[ Οƒ ])
subst-erase-comm Οƒ (var x) = refl
subst-erase-comm _ (defn _) = refl
subst-erase-comm {s} _ (U _) = loop?-[] s
subst-erase-comm {s} _ (ΠΣ⟨ _ ⟩ _ , _ β–· _ β–Ή _) = loop?-[] s
subst-erase-comm {b = true} {s} Οƒ (U.lam p t) with is-𝟘? p
... | no _ =
  cong T.lam
    (eraseβ€² true s t T.[ liftSubst (eraseSubstβ€² true s Οƒ) ]    β‰‘βŸ¨ substVar-to-subst liftSubst-erase-comm (eraseβ€² _ _ t) ⟩
     eraseβ€² true s t T.[ eraseSubstβ€² true s (U.liftSubst Οƒ) ]  β‰‘βŸ¨ subst-erase-comm _ t ⟩
     eraseβ€² true s (t U.[ U.liftSubst Οƒ ])                     ∎)
  where
  open Tools.Reasoning.PropositionalEquality
... | yes _ =
  eraseβ€² true s t T.[ loop s ]β‚€ T.[ (eraseSubstβ€² true s Οƒ) ]            β‰‘βŸ¨ singleSubstLift (eraseβ€² _ _ t) _ ⟩

  eraseβ€² true s t T.[ liftSubst (eraseSubstβ€² true s Οƒ) ]
    T.[ loop s [ eraseSubstβ€² true s Οƒ ] ]β‚€                              β‰‘βŸ¨ cong (eraseβ€² _ _ t T.[ liftSubst _ ] T.[_]β‚€) loop-[] ⟩

  eraseβ€² true s t T.[ liftSubst (eraseSubstβ€² true s Οƒ) ] T.[ loop s ]β‚€  β‰‘βŸ¨ cong T._[ _ ]β‚€ $ substVar-to-subst liftSubst-erase-comm (eraseβ€² _ _ t) ⟩

  eraseβ€² true s t T.[ eraseSubstβ€² true s (U.liftSubst Οƒ) ]
    T.[ loop s ]β‚€                                                       β‰‘βŸ¨ cong T._[ _ ]β‚€ $ subst-erase-comm _ t ⟩

  eraseβ€² true s (t U.[ U.liftSubst Οƒ ]) T.[ loop s ]β‚€                   ∎
  where
  open Tools.Reasoning.PropositionalEquality
subst-erase-comm {b = false} {s} Οƒ (U.lam _ t) =
  cong Term.lam
    (eraseβ€² false s t T.[ liftSubst (eraseSubstβ€² false s Οƒ) ]    β‰‘βŸ¨ substVar-to-subst (liftSubsts-erase-comm 1) (eraseβ€² _ _ t) ⟩
     eraseβ€² false s t T.[ eraseSubstβ€² false s (U.liftSubst Οƒ) ]  β‰‘βŸ¨ subst-erase-comm _ t ⟩
     eraseβ€² false s (t U.[ U.liftSubst Οƒ ])                      ∎)
  where
  open Tools.Reasoning.PropositionalEquality
subst-erase-comm Οƒ (t U.∘⟨ p ⟩ u) with is-𝟘? p
subst-erase-comm {b} {s} Οƒ (t U.∘⟨ _ ⟩ _) | yes _ =
  app-πŸ˜β€² b s (eraseβ€² b s t) T.[ eraseSubstβ€² b s Οƒ ]  β‰‘βŸ¨ app-𝟘-[] (eraseβ€² _ _ t) ⟩
  app-πŸ˜β€² b s (eraseβ€² b s t T.[ eraseSubstβ€² b s Οƒ ])  β‰‘βŸ¨ cong (app-πŸ˜β€² _ _) $ subst-erase-comm _ t ⟩
  app-πŸ˜β€² b s (eraseβ€² b s (t U.[ Οƒ ]))                ∎
  where
  open Tools.Reasoning.PropositionalEquality
subst-erase-comm Οƒ (t U.∘⟨ _ ⟩ u) | no _ =
  congβ‚‚ T._∘⟨ _ ⟩_ (subst-erase-comm Οƒ t) (subst-erase-comm Οƒ u)
subst-erase-comm {b} {s} Οƒ (U.prod _ p t u) with is-𝟘? p
... | yes _ = subst-erase-comm Οƒ u
... | no _ =
  prod⟨ s ⟩ (eraseβ€² b s t) (eraseβ€² b s u) [ eraseSubstβ€² b s Οƒ ]  β‰‘βŸ¨ prod⟨⟩-[] ⟩

  prod⟨ s ⟩ (eraseβ€² b s t [ eraseSubstβ€² b s Οƒ ])
    (eraseβ€² b s u [ eraseSubstβ€² b s Οƒ ])                         β‰‘βŸ¨ congβ‚‚ prod⟨ _ ⟩ (subst-erase-comm _ t) (subst-erase-comm _ u) ⟩

  prod⟨ s ⟩ (eraseβ€² b s (t U.[ Οƒ ])) (eraseβ€² b s (u U.[ Οƒ ]))    ∎
  where
  open Tools.Reasoning.PropositionalEquality
subst-erase-comm Οƒ (U.fst p t) with is-𝟘? p
... | yes _ = loop-[]
... | no _ = cong T.fst (subst-erase-comm Οƒ t)
subst-erase-comm Οƒ (U.snd p t) with is-𝟘? p
... | yes _ = subst-erase-comm Οƒ t
... | no _  = cong T.snd (subst-erase-comm Οƒ t)
subst-erase-comm {b} {s} Οƒ (U.prodrec r p _ A t u) with is-𝟘? r
... | yes _ =
  eraseβ€² b s u [ loop s , loop s ]₁₀ T.[ eraseSubstβ€² b s Οƒ ]   β‰‘βŸ¨ doubleSubstLift _ (eraseβ€² _ _ u) _ _ ⟩

  eraseβ€² b s u T.[ T.liftSubstn (eraseSubstβ€² b s Οƒ) 2 ]
    [ loop s T.[ eraseSubstβ€² b s Οƒ ]
    , loop s T.[ eraseSubstβ€² b s Οƒ ]
    ]₁₀                                                        β‰‘βŸ¨ cong₃ _[_,_]₁₀
                                                                    (substVar-to-subst (liftSubsts-erase-comm 2) (eraseβ€² _ _ u))
                                                                    loop-[]
                                                                    loop-[] ⟩
  eraseβ€² b s u T.[ eraseSubstβ€² b s (U.liftSubstn Οƒ 2) ]
    [ loop s , loop s ]₁₀                                      β‰‘βŸ¨ cong _[ _ , _ ]₁₀ $
                                                                  subst-erase-comm _ u ⟩
  eraseβ€² b s (u U.[ U.liftSubstn Οƒ 2 ]) [ loop s , loop s ]₁₀  ∎
  where
  open Tools.Reasoning.PropositionalEquality
... | no _ with is-𝟘? p
... | yes _ =
  T.lam (eraseβ€² b s u T.[ liftSubst (T.sgSubst (loop s)) ]
           T.[ liftSubst (eraseSubstβ€² b s Οƒ) ])
    T.∘⟨ s ⟩ (eraseβ€² b s t T.[ eraseSubstβ€² b s Οƒ ])                       β‰‘βŸ¨ cong (Ξ» u β†’ T.lam u T.∘⟨ _ ⟩ _) $
                                                                             subst-liftSubst-sgSubst (eraseβ€² _ _ u) ⟩
  T.lam (eraseβ€² b s u T.[ T.liftSubstn (eraseSubstβ€² b s Οƒ) 2 ]
           T.[ liftSubst (T.sgSubst (loop s T.[ eraseSubstβ€² b s Οƒ ])) ])
    T.∘⟨ s ⟩ (eraseβ€² b s t T.[ eraseSubstβ€² b s Οƒ ])                       β‰‘βŸ¨ cong (Ξ» u β†’ T.lam (u T.[ _ ]) T.∘⟨ _ ⟩ _) $
                                                                             substVar-to-subst (liftSubsts-erase-comm 2) (eraseβ€² _ _ u) ⟩
  T.lam (eraseβ€² b s u T.[ eraseSubstβ€² b s (U.liftSubstn Οƒ 2) ]
           T.[ liftSubst (T.sgSubst (loop s T.[ eraseSubstβ€² b s Οƒ ])) ])
    T.∘⟨ s ⟩ (eraseβ€² b s t T.[ eraseSubstβ€² b s Οƒ ])                       β‰‘βŸ¨ cong₃ (Ξ» u v t β†’ T.lam (u T.[ liftSubst (T.sgSubst v) ]) T.∘⟨ _ ⟩ t)
                                                                               (subst-erase-comm _ u)
                                                                               loop-[]
                                                                               (subst-erase-comm _ t) ⟩
  T.lam (eraseβ€² b s (u U.[ U.liftSubstn Οƒ 2 ])
           T.[ liftSubst (T.sgSubst (loop s)) ])
    T.∘⟨ s ⟩ eraseβ€² b s (t U.[ Οƒ ])                                       ∎
  where
  open Tools.Reasoning.PropositionalEquality
... | no _ =
  congβ‚‚ Term.prodrec (subst-erase-comm Οƒ t)
    (trans (substVar-to-subst (liftSubsts-erase-comm 2) (eraseβ€² _ _ u))
       (subst-erase-comm (U.liftSubstn Οƒ 2) u))
subst-erase-comm {s} _ β„• = loop?-[] s
subst-erase-comm Οƒ U.zero = refl
subst-erase-comm {b} {s} Οƒ (U.suc t) =
  suc⟨ s ⟩ (eraseβ€² b s t) T.[ eraseSubstβ€² b s Οƒ ]  β‰‘βŸ¨ suc⟨⟩-[] ⟩
  suc⟨ s ⟩ (eraseβ€² b s t T.[ eraseSubstβ€² b s Οƒ ])  β‰‘βŸ¨ cong suc⟨ _ ⟩ (subst-erase-comm _ t) ⟩
  suc⟨ s ⟩ (eraseβ€² b s (t U.[ Οƒ ]))                ∎
  where
  open Tools.Reasoning.PropositionalEquality
subst-erase-comm Οƒ (U.natrec p q r A z s n) = cong₃ T.natrec
  (subst-erase-comm Οƒ z)
  (trans (substVar-to-subst (liftSubsts-erase-comm 2) (eraseβ€² _ _ s))
         (subst-erase-comm (U.liftSubst (U.liftSubst Οƒ)) s))
  (subst-erase-comm Οƒ n)
subst-erase-comm {s} _ Unit! = loop?-[] s
subst-erase-comm Οƒ U.star! = refl
subst-erase-comm Οƒ (U.unitrec _ p _ _ t u) with is-𝟘? p
... | yes _ =
  subst-erase-comm Οƒ u
... | no _ =
  congβ‚‚ T.unitrec (subst-erase-comm Οƒ t)
                  (subst-erase-comm Οƒ u)
subst-erase-comm {s} _ Empty = loop?-[] s
subst-erase-comm _ (emptyrec _ _ _) = loop-[]
subst-erase-comm {s} _ (Id _ _ _) = loop?-[] s
subst-erase-comm {s} _ U.rfl = loop?-[] s
subst-erase-comm _ (J _ _ _ _ _ u _ _) = subst-erase-comm _ u
subst-erase-comm _ (K _ _ _ _ u _) = subst-erase-comm _ u
subst-erase-comm {s} _ ([]-cong _ _ _ _ _) = loop?-[] s

subst-undefined : (x : Fin (1+ n)) β†’
      eraseSubstβ€² b s (U.sgSubst Empty) x ≑
      T.sgSubst (loop? s) x
subst-undefined x0 = refl
subst-undefined (x +1) = refl

erase-consSubst-var : (Οƒ : U.Subst m n) (a : U.Term m) (x : Fin (1+ n))
                    β†’ T.consSubst (eraseSubstβ€² b s Οƒ) (eraseβ€² b s a) x
                    ≑ eraseSubstβ€² b s (U.consSubst Οƒ a) x
erase-consSubst-var Οƒ a x0 = refl
erase-consSubst-var Οƒ a (x +1) = refl

erase-consSubst : (Οƒ : U.Subst m n) (a : U.Term m) (t : T.Term (1+ n))
                β†’ t T.[ T.consSubst (eraseSubstβ€² b s Οƒ) (eraseβ€² b s a) ]
                ≑ t T.[ eraseSubstβ€² b s (U.consSubst Οƒ a) ]
erase-consSubst Οƒ a t = substVar-to-subst (erase-consSubst-var Οƒ a) t

opaque
  unfolding eraseDConβ€³

  -- Glassification does not affect the result of eraseDConβ€².

  eraseDConβ€³-glassify :
    {erase : 𝕋 β†’ π•Œ} {βˆ‡ : DCon 𝕋 n} β†’
    eraseDConβ€³ erase (glassify βˆ‡) ≑ eraseDConβ€³ erase βˆ‡
  eraseDConβ€³-glassify {βˆ‡ = Ξ΅}    = refl
  eraseDConβ€³-glassify {βˆ‡ = βˆ‡ βˆ™!} =
    cong (_++ _) (eraseDConβ€³-glassify {βˆ‡ = βˆ‡})

opaque
  unfolding eraseDConβ€²

  -- Glassification does not affect the result of eraseDConβ€².

  eraseDCon-glassify :
    {βˆ‡ : DCon (U.Term 0) n} β†’
    eraseDConβ€² b s (glassify βˆ‡) ≑ eraseDConβ€² b s βˆ‡
  eraseDCon-glassify {βˆ‡} = eraseDConβ€³-glassify {βˆ‡ = βˆ‡}

opaque
  unfolding eraseDConβ€²

  -- The length of eraseDConβ€²Β bΒ sΒ ts is the length of ts.

  length-eraseDCon :
    (ts : DCon (U.Term 0) n) β†’ length (eraseDConβ€² b s ts) ≑ n
  length-eraseDCon         Ξ΅                         = refl
  length-eraseDCon {b} {s} (_βˆ™βŸ¨_⟩[_∷_] {n} ts _ t _) =
    length (eraseDConβ€² b s ts ++ eraseβ€² b s t ∷ [])  β‰‘βŸ¨ length-++ (eraseDConβ€² _ _ ts) ⟩
    length (eraseDConβ€² b s ts) +ⁿ 1                  β‰‘Λ˜βŸ¨ Nat.+-comm 1 _ ⟩
    1+ (length (eraseDConβ€² b s ts))                  β‰‘βŸ¨ cong 1+ (length-eraseDCon ts) ⟩
    1+ n                                             ∎
    where
    open Tools.Reasoning.PropositionalEquality

opaque
  unfolding eraseDConβ€²

  -- If Ξ± points to t in ts, then Ξ± points to eraseβ€²Β bΒ sΒ t in
  -- eraseDConβ€²Β bΒ sΒ ts.

  ↦erase∈eraseDCon :
    Ξ± U.↦ t ∷ A ∈ ts β†’ Ξ± ↦ eraseβ€² b s t ∈ eraseDConβ€² b s ts
  ↦erase∈eraseDCon (there α↦t) = β†¦βˆˆ++ (↦erase∈eraseDCon α↦t)
  ↦erase∈eraseDCon (here {βˆ‡})  =
    PE.subst₃ _↦_∈_ (length-eraseDCon βˆ‡) refl refl lengthβ†¦βˆˆ++∷

opaque

  -- If Ξ± points to t in glassifyΒ ts, then Ξ± points to eraseβ€²Β bΒ sΒ t in
  -- eraseDConβ€²Β bΒ sΒ ts.

  ↦erase∈eraseDConβ€² :
    Ξ± U.↦ t ∷ A ∈ glassify ts β†’ Ξ± ↦ eraseβ€² b s t ∈ eraseDConβ€² b s ts
  ↦erase∈eraseDConβ€² =
    PE.subst (_↦_∈_ _ _) eraseDCon-glassify βˆ˜β†’
    ↦erase∈eraseDCon

opaque
  unfolding Ξ© O.Ο‰ loop

  -- The term eraseβ€²Β bΒ sΒ (Ω {nΒ =Β n}Β p) does not reduce to a value.
  --
  -- Note that eraseβ€²Β trueΒ sΒ (Ω {nΒ =Β n} 𝟘) could have been a value if
  -- erasure had been defined differently for lambdas with erased
  -- arguments in the "bΒ =Β true" case: this term is (at the time of
  -- writing) equal to loopΒ s due to the use of loopΒ s in
  -- eraseβ€³Β tΒ T.[Β loopΒ sΒ ]β‚€, but if this right-hand side had instead
  -- been eraseβ€³Β tΒ T.[Β zeroΒ ]β‚€, then the term would have been equal to
  -- zero.

  erase-Ξ©-does-not-have-a-value :
    Value v β†’ Β¬ βˆ‡ ⊒ eraseβ€² b s (Ξ© {n = n} p) β‡’* v
  erase-Ξ©-does-not-have-a-value {v} {βˆ‡} {b} {s} {p} v-value
    with is-𝟘? p
  … | no pβ‰’πŸ˜ =
    PE.subst (Ξ» t β†’ Β¬ βˆ‡ ⊒ t ∘⟨ s ⟩ t β‡’* v) (PE.sym $ lam-β‰’πŸ˜ b pβ‰’πŸ˜) $
    PE.subst (Ξ» t β†’ Β¬ βˆ‡ ⊒ lam t ∘⟨ s ⟩ lam t β‡’* v) (PE.sym $ ∘-β‰’πŸ˜ pβ‰’πŸ˜) $
    ¬loop⇒* v-value
  erase-Ξ©-does-not-have-a-value {v} {βˆ‡} {b = true} {s} {p} v-value
    | yes refl =
    PE.subst (Ξ» t β†’ Β¬ βˆ‡ ⊒ t β‡’* v) (PE.sym lam-𝟘-remove) $
    PE.subst (Ξ» t β†’ Β¬ βˆ‡ ⊒ t T.[ loop s ]β‚€ β‡’* v) (PE.sym ∘-𝟘) $
    ¬loop⇒* v-value
  erase-Ξ©-does-not-have-a-value {v} {βˆ‡} {b = false} {s} {p} v-value
    | yes refl =
    PE.subst (Ξ» t β†’ Β¬ βˆ‡ ⊒ lam t ∘⟨ s ⟩ loop? s β‡’* v) (PE.sym ∘-𝟘)
      (lemma _)
    where
    lemma : βˆ€ s β†’ Β¬ βˆ‡ ⊒ lam (var x0 ∘⟨ s ⟩ loop? s) ∘⟨ s ⟩ loop? s β‡’* v
    lemma strict T.refl =
      case v-value of Ξ» ()
    lemma strict (T.trans (app-subst ()) _)
    lemma strict (T.trans (app-subst-arg _ ()) _)
    lemma strict (T.trans (Ξ²-red _) T.refl) =
      case v-value of Ξ» ()
    lemma strict (T.trans (Ξ²-red _) (T.trans (app-subst ()) _))
    lemma strict (T.trans (Ξ²-red _) (T.trans (app-subst-arg _ ()) _))
    lemma non-strict T.refl =
      case v-value of Ξ» ()
    lemma non-strict (T.trans (app-subst ()) _)
    lemma non-strict (T.trans (Ξ²-red _) loop∘loopβ‡’v) =
      let _ , vβ€²-value , loopβ‡’vβ€² = βˆ˜β‡’Valueβ†’β‡’Value v-value loop∘loopβ‡’v in
      ¬loop⇒* v′-value loop⇒v′

module hasX (R : Usage-restrictions) where

  open MU R
  open MUP R
  open MUP𝟘 R

  open import Graded.Usage.Restrictions.Instance R

  private

    -- A definition used in the proof of erase-[].

    OK : U.Subst n n β†’ Conβ‚˜ n β†’ Set a
    OK Οƒ Ξ³ = βˆ€ {x} β†’ Οƒ x β‰’ var x β†’ x β—‚ 𝟘 ∈ Ξ³

  private opaque

    -- Some lemmas used in the proof of erase-[].

    Β¬β—‚πŸ˜βˆˆ,β‰”πŸ™ :
      ⦃ 𝟘-well-behaved : Has-well-behaved-zero M semiring-with-meet ⦄ β†’
      Β¬ x β—‚ 𝟘 ∈ (Ξ³ , x ≔ πŸ™)
    Β¬β—‚πŸ˜βˆˆ,β‰”πŸ™ {x} {Ξ³} =
      x β—‚ 𝟘 ∈ (Ξ³ , x ≔ πŸ™)    β‡”βŸ¨ β—‚βˆˆβ‡” βŸ©β†’
      (Ξ³ , x ≔ πŸ™) ⟨ x ⟩ ≑ 𝟘  β‰‘βŸ¨ cong (_≑ _) $ update-lookup Ξ³ x βŸ©β†’
      πŸ™ ≑ 𝟘                  β†’βŸ¨ non-trivial ⟩
      βŠ₯                      β–‘

    erase-[]-var :
      ⦃ 𝟘-well-behaved : Has-well-behaved-zero M semiring-with-meet ⦄
      (y : Fin n) β†’
      OK Οƒ (𝟘ᢜ , y ≔ πŸ™) β†’
      eraseβ€² b s (Οƒ y) ≑ var y
    erase-[]-var {Οƒ} {b} {s} y ok
      with Οƒ y | ok {x = y} | UP.is-var? (Οƒ y)
    … | _ | ok | UP.not-var β‰’var =
      βŠ₯-elim $ Β¬β—‚πŸ˜βˆˆ,β‰”πŸ™ $ ok β‰’var
    … | _ | ok | UP.var x with x β‰Ÿβ±½ y
    …  | yes x≑y = cong var x≑y
    …  | no xβ‰’y  =
      βŠ₯-elim $ Β¬β—‚πŸ˜βˆˆ,β‰”πŸ™ $ ok (xβ‰’y βˆ˜β†’ UP.var-PE-injectivity)

    OK-⇑-βˆ™ : OK Οƒ Ξ³ β†’ OK (Οƒ U.⇑) (Ξ³ βˆ™ p)
    OK-⇑-βˆ™ _      {x = x0}   β‰’var = βŠ₯-elim $ β‰’var refl
    OK-⇑-βˆ™ erased {x = _ +1} β‰’var =
      there (erased (β‰’var βˆ˜β†’ cong U.wk1))

  opaque

    -- Substituting anything for erasable variables (using a
    -- substitution of type U.SubstΒ nΒ n) does not affect the result of
    -- erasure (assuming that the modality has a well-behaved zero).

    erase-[] :
      {Οƒ : U.Subst n n}
      ⦃ 𝟘-well-behaved : Has-well-behaved-zero M semiring-with-meet ⦄ β†’
      (βˆ€ {x} β†’ Οƒ x β‰’ var x β†’ x β—‚ 𝟘 ∈ Ξ³) β†’
      Ξ³ β–Έ[ πŸ™α΅ ] t β†’
      eraseβ€² b s (t U.[ Οƒ ]) ≑ eraseβ€² b s t
    erase-[] ok (sub β–Έt γ≀δ) =
      erase-[] (flip xβ—‚πŸ˜βˆˆΞ³β‰€Ξ΄ γ≀δ βˆ˜β†’ ok) β–Έt
    erase-[] ok var =
      erase-[]-var _ ok
    erase-[] _ defn =
      refl
    erase-[] _ Uβ‚˜ =
      refl
    erase-[] _ Emptyβ‚˜ =
      refl
    erase-[] _ (emptyrecβ‚˜ _ _ _) =
      refl
    erase-[] _ Unitβ‚˜ =
      refl
    erase-[] _ (starΛ’β‚˜ _) =
      refl
    erase-[] _ starΚ·β‚˜ =
      refl
    erase-[] ok (unitrecβ‚˜ {p} β–Έt₁ β–Έtβ‚‚ _ _) with is-𝟘? p
    … | no pβ‰’πŸ˜ =
      congβ‚‚ unitrec
        (erase-[] (xβ—‚πŸ˜βˆˆpΞ³ refl pβ‰’πŸ˜ βˆ˜β†’ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Λ‘ refl βˆ˜β†’ ok)
           (β–Έ-cong (β‰’πŸ˜β†’βŒžβŒŸβ‰‘πŸ™α΅ pβ‰’πŸ˜) β–Έt₁))
        (erase-[] (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ ok) β–Έtβ‚‚)
    … | yes _ =
      erase-[] (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ ok) β–Έtβ‚‚
    erase-[] _ (Ξ Ξ£β‚˜ _ _) =
      refl
    erase-[] {b = false} ok (lamβ‚˜ β–Έt) =
      cong lam (erase-[] (OK-⇑-βˆ™ ok) β–Έt)
    erase-[] {b = true} ok (lamβ‚˜ {p} β–Έt) with is-𝟘? p
    … | no _ =
      cong lam (erase-[] (OK-⇑-βˆ™ ok) β–Έt)
    … | yes _ =
      cong T._[ _ ]β‚€ (erase-[] (OK-⇑-βˆ™ ok) β–Έt)
    erase-[] ok (_βˆ˜β‚˜_ {p} β–Έt β–Έu) with is-𝟘? p
    … | no pβ‰’πŸ˜ =
      congβ‚‚ _∘⟨ _ ⟩_ (erase-[] (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Λ‘ refl βˆ˜β†’ ok) β–Έt)
        (erase-[] (xβ—‚πŸ˜βˆˆpΞ³ refl pβ‰’πŸ˜ βˆ˜β†’ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ ok)
           (β–Έ-cong (β‰’πŸ˜β†’βŒžβŒŸβ‰‘πŸ™α΅ pβ‰’πŸ˜) β–Έu))
    erase-[] {b = false} ok (β–Έt βˆ˜β‚˜ _) | yes _ =
      cong (_∘⟨ _ ⟩ _) (erase-[] (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Λ‘ refl βˆ˜β†’ ok) β–Έt)
    erase-[] {b = true} ok (β–Έt βˆ˜β‚˜ _) | yes _ =
      erase-[] (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Λ‘ refl βˆ˜β†’ ok) β–Έt
    erase-[] ok (prodΛ’β‚˜ {p} β–Έt₁ β–Έtβ‚‚) with is-𝟘? p
    … | yes _ =
      erase-[] (xβ—‚πŸ˜βˆˆΞ³βˆ§Ξ΄Κ³ refl βˆ˜β†’ ok) β–Έtβ‚‚
    … | no pβ‰’πŸ˜ =
      congβ‚‚ prod⟨ _ ⟩
        (erase-[] (xβ—‚πŸ˜βˆˆpΞ³ refl pβ‰’πŸ˜ βˆ˜β†’ xβ—‚πŸ˜βˆˆΞ³βˆ§Ξ΄Λ‘ refl βˆ˜β†’ ok)
           (β–Έ-cong (β‰’πŸ˜β†’βŒžβŒŸβ‰‘πŸ™α΅ pβ‰’πŸ˜) β–Έt₁))
        (erase-[] (xβ—‚πŸ˜βˆˆΞ³βˆ§Ξ΄Κ³ refl βˆ˜β†’ ok) β–Έtβ‚‚)
    erase-[] ok (fstβ‚˜ {p} _ β–Έt eq _) with is-𝟘? p
    … | yes _ =
      refl
    … | no _ =
      cong fst (erase-[] ok (β–Έ-cong eq β–Έt))
    erase-[] ok (sndβ‚˜ {p} β–Έt) with is-𝟘? p
    … | yes _ =
      erase-[] ok β–Έt
    … | no _ =
      cong snd (erase-[] ok β–Έt)
    erase-[] ok (prodΚ·β‚˜ {p} β–Έt₁ β–Έtβ‚‚) with is-𝟘? p
    … | yes _ =
      erase-[] (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ ok) β–Έtβ‚‚
    … | no pβ‰’πŸ˜ =
      congβ‚‚ prod⟨ _ ⟩
        (erase-[] (xβ—‚πŸ˜βˆˆpΞ³ refl pβ‰’πŸ˜ βˆ˜β†’ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Λ‘ refl βˆ˜β†’ ok)
           (β–Έ-cong (β‰’πŸ˜β†’βŒžβŒŸβ‰‘πŸ™α΅ pβ‰’πŸ˜) β–Έt₁))
        (erase-[] (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ ok) β–Έtβ‚‚)
    erase-[] ok (prodrecβ‚˜ {r} β–Έt₁ β–Έtβ‚‚ _ _) with is-𝟘? r
    … | yes _ =
      cong _[ _ , _ ]₁₀
        (erase-[] (OK-⇑-βˆ™ $ OK-⇑-βˆ™ $ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ ok) β–Έtβ‚‚)
    … | no rβ‰’πŸ˜ =
      cong₂ (erase-prodrecω _ _)
        (erase-[] (xβ—‚πŸ˜βˆˆpΞ³ refl rβ‰’πŸ˜ βˆ˜β†’ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Λ‘ refl βˆ˜β†’ ok)
           (β–Έ-cong (β‰’πŸ˜β†’βŒžβŒŸβ‰‘πŸ™α΅ rβ‰’πŸ˜) β–Έt₁))
        (erase-[] (OK-⇑-βˆ™ $ OK-⇑-βˆ™ $ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ ok) β–Έtβ‚‚)
    erase-[] _ β„•β‚˜ =
      refl
    erase-[] _ zeroβ‚˜ =
      refl
    erase-[] ok (sucβ‚˜ β–Έt) =
      cong suc⟨ _ ⟩ (erase-[] ok β–Έt)
    erase-[] ok (natrecβ‚˜ {Ξ³} {Ξ΄} {p} {r} {Ξ·} β–Έt₁ β–Έtβ‚‚ β–Έt₃ _) =
      cong₃ natrec
        (erase-[]
           (λ {x = x} σx≒var-x →
              let xβ—‚πŸ˜ = ok Οƒxβ‰’var-x in
                                                                        $⟨ xβ—‚πŸ˜ ⟩
              x β—‚ 𝟘 ∈ nrᢜ p r Ξ³ Ξ΄ Ξ·                                     β†’βŸ¨ β—‚βˆˆβ‡” .proj₁ ⟩

              nrᢜ p r Ξ³ Ξ΄ Ξ· ⟨ x ⟩ ≑ 𝟘                                   β†’βŸ¨ trans (sym (nrᢜ-⟨⟩ Ξ³)) ⟩

              nr p r (Ξ³ ⟨ x ⟩) (Ξ΄ ⟨ x ⟩) (Ξ· ⟨ x ⟩) ≑ 𝟘                  β†’βŸ¨ trans (update-lookup Ξ³ _) ⟩

              (Ξ³ , x ≔ nr p r (Ξ³ ⟨ x ⟩) (Ξ΄ ⟨ x ⟩) (Ξ· ⟨ x ⟩)) ⟨ x ⟩ ≑ 𝟘  β†’βŸ¨ β—‚βˆˆβ‡” .projβ‚‚ ⟩

              x β—‚ 𝟘 ∈ Ξ³ , x ≔ nr p r (Ξ³ ⟨ x ⟩) (Ξ΄ ⟨ x ⟩) (Ξ· ⟨ x ⟩)      β†’βŸ¨ flip xβ—‚πŸ˜βˆˆΞ³β‰€Ξ΄ $ begin

                 Ξ³ , x ≔ nr p r (Ξ³ ⟨ x ⟩) (Ξ΄ ⟨ x ⟩) (Ξ· ⟨ x ⟩)                β‰€βŸ¨ update-monotoneΚ³ _
                                                                                  ($⟨ xβ—‚πŸ˜ ⟩
                   x β—‚ 𝟘 ∈ nrᢜ p r Ξ³ Ξ΄ Ξ·                                           β†’βŸ¨ β—‚πŸ˜βˆˆnrαΆœβ‚ƒ refl ⟩
                   x β—‚ 𝟘 ∈ Ξ·                                                       β†’βŸ¨ β—‚βˆˆβ‡” .proj₁ ⟩
                   Ξ· ⟨ x ⟩ ≑ 𝟘                                                     β†’βŸ¨ nr-zero βˆ˜β†’ ≀-reflexive ⟩
                   nr p r (Ξ³ ⟨ x ⟩) (Ξ΄ ⟨ x ⟩) (Ξ· ⟨ x ⟩) ≀ Ξ³ ⟨ x ⟩                  β–‘) ⟩

                 Ξ³ , x ≔ Ξ³ ⟨ x ⟩                                             β‰‘βŸ¨ update-self _ _ ⟩

                 γ                                                           ∎ ⟩

              x β—‚ 𝟘 ∈ Ξ³                                                 β–‘)
           β–Έt₁)
        (erase-[] (OK-⇑-βˆ™ $ OK-⇑-βˆ™ $ β—‚πŸ˜βˆˆnrαΆœβ‚‚ refl βˆ˜β†’ ok) β–Έtβ‚‚)
        (erase-[] (β—‚πŸ˜βˆˆnrαΆœβ‚ƒ refl βˆ˜β†’ ok) β–Έt₃)
      where
      open β‰€αΆœ-reasoning
    erase-[] ok (natrec-no-nrβ‚˜ β–Έt₁ β–Έtβ‚‚ β–Έt₃ _ γ≀₁ _ γ≀₃ γ≀₄) =
      cong₃ natrec (erase-[] ok (sub β–Έt₁ γ≀₁))
        (erase-[]
           (OK-⇑-βˆ™ $ OK-⇑-βˆ™ $ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Λ‘ refl βˆ˜β†’ flip xβ—‚πŸ˜βˆˆΞ³β‰€Ξ΄ γ≀₄ βˆ˜β†’ ok)
           β–Έtβ‚‚)
        (erase-[] (flip xβ—‚πŸ˜βˆˆΞ³β‰€Ξ΄ γ≀₃ βˆ˜β†’ ok) β–Έt₃)
    erase-[]
      ok (natrec-no-nr-glbβ‚˜ {Ξ³} {Ξ΄} {r} {Ο‡} β–Έt₁ β–Έtβ‚‚ β–Έt₃ _ glb Ο‡-glb) =
      cong₃ natrec
        (erase-[] (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ ok) $
         sub β–Έt₁ $ begin
           Ο‡             β‰€βŸ¨ Ο‡-glb .proj₁ 0 ⟩
           nrᡒᢜ r Ξ³ Ξ΄ 0  β‰ˆβŸ¨ nrᡒᢜ-zero ⟩
           γ             ∎)
        (erase-[]
           (OK-⇑-βˆ™ $ OK-⇑-βˆ™ $ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Λ‘ refl βˆ˜β†’
            flip xβ—‚πŸ˜βˆˆΞ³β‰€Ξ΄
              (begin
                 Ο‡                       β‰€βŸ¨ Ο‡-glb .proj₁ 1 ⟩
                 nrᡒᢜ r Ξ³ Ξ΄ 1            β‰ˆβŸ¨ nrᡒᢜ-suc ⟩
                 Ξ΄ +ᢜ r ·ᢜ nrᡒᢜ r Ξ³ Ξ΄ 0  ∎) βˆ˜β†’
            xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ ok)
           β–Έtβ‚‚)
        (erase-[]
           (xβ—‚πŸ˜βˆˆpΞ³ refl (Ξ» { refl β†’ πŸ˜β‰°πŸ™ (glb .proj₁ 0) }) βˆ˜β†’
            xβ—‚πŸ˜βˆˆΞ³+Ξ΄Λ‘ refl βˆ˜β†’ ok)
           β–Έt₃)
      where
      open β‰€αΆœ-reasoning
    erase-[] _ (Idβ‚˜ _ _ _ _) =
      refl
    erase-[] _ (Idβ‚€β‚˜ _ _ _ _) =
      refl
    erase-[] _ rflβ‚˜ =
      refl
    erase-[] ok (Jβ‚˜ _ _ _ _ _ β–Έt _ _) =
      erase-[]
        (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Λ‘ refl βˆ˜β†’ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’
         xβ—‚πŸ˜βˆˆpΞ³ refl Ο‰β‰’πŸ˜ βˆ˜β†’ ok)
        β–Έt
    erase-[] ok (Jβ‚€β‚˜β‚ _ _ _ _ _ _ β–Έt _ _) =
      erase-[] (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ xβ—‚πŸ˜βˆˆpΞ³ refl Ο‰β‰’πŸ˜ βˆ˜β†’ ok) β–Έt
    erase-[] ok (Jβ‚€β‚˜β‚‚ _ _ _ _ β–Έt _ _) =
      erase-[] ok β–Έt
    erase-[] ok (Kβ‚˜ _ _ _ _ _ β–Έt _) =
      erase-[]
        (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Λ‘ refl βˆ˜β†’ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’
         xβ—‚πŸ˜βˆˆpΞ³ refl Ο‰β‰’πŸ˜ βˆ˜β†’ ok)
        β–Έt
    erase-[] ok (Kβ‚€β‚˜β‚ _ _ _ _ _ β–Έt _) =
      erase-[] (xβ—‚πŸ˜βˆˆΞ³+Ξ΄Κ³ refl βˆ˜β†’ xβ—‚πŸ˜βˆˆpΞ³ refl Ο‰β‰’πŸ˜ βˆ˜β†’ ok) β–Έt
    erase-[] ok (Kβ‚€β‚˜β‚‚ _ _ _ _ β–Έt _) =
      erase-[] ok β–Έt
    erase-[] _ ([]-congβ‚˜ _ _ _ _ _) =
      refl

  opaque

    -- A variant of erase-[] with a closing substitution.

    wkβ‚€-erase-[] :
      {Οƒ : U.Subst 0 n}
      ⦃ 𝟘-well-behaved : Has-well-behaved-zero M semiring-with-meet ⦄ β†’
      𝟘ᢜ β–Έ[ πŸ™α΅ ] t β†’
      T.wk wkβ‚€ (eraseβ€² b s (t U.[ Οƒ ])) ≑ eraseβ€² b s t
    wkβ‚€-erase-[] {t} {b} {s} {Οƒ} β–Έt =
      T.wk wkβ‚€ (eraseβ€² b s (t U.[ Οƒ ]))  β‰‘βŸ¨ wk-erase-comm _ (t U.[ _ ]) ⟩
      eraseβ€² b s (U.wk wkβ‚€ (t U.[ Οƒ ]))  β‰‘βŸ¨ cong (eraseβ€² _ _) $ UP.wk-subst t ⟩
      eraseβ€² b s (t U.[ wkβ‚€ U.β€’β‚› Οƒ ])    β‰‘βŸ¨ erase-[] (Ξ» {x = x} _ β†’ β—‚βˆˆβ‡” .projβ‚‚ (𝟘ᢜ-lookup x)) β–Έt ⟩
      eraseβ€² b s t                       ∎
      where
      open Tools.Reasoning.PropositionalEquality

  opaque

    -- A variant of erase-[] stated using ⟨_≔_βŸ©β†‘.

    erase-≔↑ :
      ⦃ 𝟘-well-behaved : Has-well-behaved-zero M semiring-with-meet ⦄ β†’
      x β—‚ 𝟘 ∈ Ξ³ β†’ Ξ³ β–Έ[ πŸ™α΅ ] t β†’
      eraseβ€² b s (t U.[ ⟨ x ≔ u βŸ©β†‘ ]) ≑ eraseβ€² b s t
    erase-≔↑ xβ—‚ β–Έt = erase-[] (flip (lemma _ _) xβ—‚) β–Έt
      where
      lemma :
        βˆ€ (x y : Fin n) {u} β†’
        ⟨ x ≔ u βŸ©β†‘ y β‰’ var y β†’ x β—‚ 𝟘 ∈ Ξ³ β†’ y β—‚ 𝟘 ∈ Ξ³
      lemma x0 x0 _ xβ—‚πŸ˜ =
        xβ—‚πŸ˜
      lemma x0 (_ +1) β‰’var =
        βŠ₯-elim $ β‰’var refl
      lemma (_+1 {n = 0}    ())
      lemma (_+1 {n = 1+ _} x)  x0 β‰’var =
        βŠ₯-elim $ β‰’var refl
      lemma (_+1 {n = 1+ _} x) (y +1) β‰’var (there xβ—‚πŸ˜) =
        there (lemma x y (β‰’var βˆ˜β†’ cong U.wk1) xβ—‚πŸ˜)

  opaque

    -- A special case of erase-≔↑.

    erase-[]↑ :
      ⦃ 𝟘-well-behaved : Has-well-behaved-zero M semiring-with-meet ⦄ β†’
      x0 β—‚ 𝟘 ∈ Ξ³ β†’ Ξ³ β–Έ[ πŸ™α΅ ] t β†’
      eraseβ€² b s (t U.[ u ]↑) ≑ eraseβ€² b s t
    erase-[]↑ = erase-≔↑

  opaque

    -- A variant of erase-≔↑.

    erase-≔ :
      ⦃ 𝟘-well-behaved : Has-well-behaved-zero M semiring-with-meet ⦄ β†’
      x β—‚ 𝟘 ∈ Ξ³ β†’ Ξ³ β–Έ[ πŸ™α΅ ] t β†’
      wk (step-at x) (eraseβ€² b s (t U.[ ⟨ x ≔ u ⟩ ])) ≑ eraseβ€² b s t
    erase-≔ {x} {t} {b} {s} {u} xβ—‚ β–Έt =
      wk (step-at x) (eraseβ€² b s (t U.[ ⟨ x ≔ u ⟩ ]))     β‰‘βŸ¨ wk-erase-comm _ (t U.[ _ ]) ⟩
      eraseβ€² b s (U.wk (step-at x) (t U.[ ⟨ x ≔ u ⟩ ]))   β‰‘βŸ¨ PE.cong (eraseβ€² _ _) $ UP.wk-subst t ⟩
      eraseβ€² b s (t U.[ step-at x U.β€’β‚› ⟨ x ≔ u ⟩ ])       β‰‘βŸ¨βŸ©
      eraseβ€² b s (t U.[ U.wk (step-at x) βˆ˜β†’ ⟨ x ≔ u ⟩ ])  β‰‘βŸ¨ PE.cong (eraseβ€² _ _) $ UP.substVar-to-subst UP.βŸ¨β‰”βŸ©β‰‘βŸ¨β‰”βŸ©β†‘ t ⟩
      eraseβ€² b s (t U.[ ⟨ x ≔ U.wk (step-atβ€² x) u βŸ©β†‘ ])   β‰‘βŸ¨ erase-≔↑ xβ—‚ β–Έt ⟩
      eraseβ€² b s t                                        ∎
      where
      open Tools.Reasoning.PropositionalEquality

  opaque

    -- A special case of erase-≔.

    erase-[]β‚€ :
      ⦃ 𝟘-well-behaved : Has-well-behaved-zero M semiring-with-meet ⦄ β†’
      x0 β—‚ 𝟘 ∈ Ξ³ β†’ Ξ³ β–Έ[ πŸ™α΅ ] t β†’
      T.wk1 (eraseβ€² b s (t U.[ u ]β‚€)) ≑ eraseβ€² b s t
    erase-[]β‚€ = erase-≔

  opaque

    -- If the modality's zero is well-behaved, then erased variables do
    -- not occur after extraction.

    erased-hasX :
      ⦃ 𝟘-well-behaved : Has-well-behaved-zero M semiring-with-meet ⦄ β†’
      x β—‚ 𝟘 ∈ Ξ³ β†’ Ξ³ β–Έ[ πŸ™α΅ ] t β†’ Β¬ HasX x (eraseβ€² b s t)
    erased-hasX {x} {t} {b} {s} x∈ β–Έt =
      HasX x (eraseβ€² b s t)                                     β†’βŸ¨ PE.subst (HasX _) (sym $ erase-≔ x∈ β–Έt) ⟩
      HasX x (wk (step-at x) (eraseβ€² b s (t U.[ ⟨ x ≔ β„• ⟩ ])))  β†’βŸ¨ Β¬-HasX-wk-step-at ⟩
      βŠ₯                                                         β–‘