------------------------------------------------------------------------
-- Properties of machine states.
------------------------------------------------------------------------

open import Graded.Modality
open import Graded.Usage.Restrictions
open import Definition.Typed.Variant
open import Graded.Usage.Restrictions.Natrec

module Graded.Heap.Untyped.Properties
  {a} {M : Set a} {š•„ : Modality M}
  (type-variant : Type-variant)
  (UR : Usage-restrictions š•„)
  (open Usage-restrictions UR)
  (factoring-nr :
    ⦃ has-nr : Nr-available ⦄ →
    Is-factoring-nr M (Natrec-mode-Has-nr š•„ has-nr))
  where

open Type-variant type-variant
open Modality š•„

open import Tools.Empty
open import Tools.Fin
open import Tools.Function
open import Tools.Nat using (Nat; 1+; 2+; +-suc) renaming (_+_ to _+ⁿ_)
open import Tools.PropositionalEquality
open import Tools.Product
open import Tools.Relation
open import Tools.Reasoning.PropositionalEquality
open import Tools.Sum

open import Graded.Modality.Nr-instances
open import Graded.Modality.Properties š•„
open import Graded.Usage.Erased-matches
open import Graded.Usage.Restrictions.Instance UR

open import Definition.Untyped M
open import Definition.Untyped.Neutral M type-variant
open import Definition.Untyped.Properties M

open import Graded.Heap.Untyped type-variant UR factoring-nr

private variable
  k n n′ n″ m m′ m″ : Nat
  t t′ t″ u v A z : Term _
  H H′ H″ : Heap _ _
  ρ ρ′ ρ″ : Wk _ _
  S S′ S″ : Stack _
  p p′ q q′ r r′ : M
  y y′ : Ptr _
  x : Fin _
  e e′ : Entry _ _
  Ī“ : Con Term _
  c c′ c″ : Cont _
  s s′ : State _ _ _
  σ : Subst _ _
  em : Erased-matches

------------------------------------------------------------------------
-- Properties of states

opaque

  -- Injectivity of states

  State-injectivity :
    {H : Heap _ _} →
    ⟨ H , t , ρ , S ⟩ ≔ ⟨ H′ , t′ , ρ′ , S′ ⟩ →
    H ≔ H′ Ɨ t ≔ t′ Ɨ ρ ≔ ρ′ Ɨ S ≔ S′
  State-injectivity refl = refl , refl , refl , refl

------------------------------------------------------------------------
-- Properties of values

opaque

  -- Values applied to weakenings are values

  wkValue : (ρ : Wk m n) → Value t → Value (wk ρ t)
  wkValue ρ lamᵄ = lamᵄ
  wkValue ρ zeroᵄ = zeroᵄ
  wkValue ρ sucᵄ = sucᵄ
  wkValue ρ starᵄ = starᵄ
  wkValue ρ prodᵄ = prodᵄ
  wkValue ρ rflᵄ = rflᵄ
  wkValue ρ Uᵄ = Uᵄ
  wkValue ρ ΠΣᵄ = ΠΣᵄ
  wkValue ρ ℕᵄ = ℕᵄ
  wkValue ρ Unitᵄ = Unitᵄ
  wkValue ρ Emptyᵄ = Emptyᵄ
  wkValue ρ Idᵄ = Idᵄ
  wkValue ρ (unitrec-ηᵄ η) = unitrec-ηᵄ η

opaque

  -- Values applied to substitutions are values

  substValue : (σ : Subst m n) → Value t → Value (t [ σ ])
  substValue σ lamᵄ = lamᵄ
  substValue σ zeroᵄ = zeroᵄ
  substValue σ sucᵄ = sucᵄ
  substValue σ starᵄ = starᵄ
  substValue σ prodᵄ = prodᵄ
  substValue σ rflᵄ = rflᵄ
  substValue σ Uᵄ = Uᵄ
  substValue σ ΠΣᵄ = ΠΣᵄ
  substValue σ ℕᵄ = ℕᵄ
  substValue σ Unitᵄ = Unitᵄ
  substValue σ Emptyᵄ = Emptyᵄ
  substValue σ Idᵄ = Idᵄ
  substValue ρ (unitrec-ηᵄ η) = unitrec-ηᵄ η

opaque

  -- Values are non-neutrals

  Value→¬Neutral : Value t → ¬ Neutral t
  Value→¬Neutral lamᵄ ()
  Value→¬Neutral zeroᵄ ()
  Value→¬Neutral sucᵄ ()
  Value→¬Neutral starᵄ ()
  Value→¬Neutral prodᵄ ()
  Value→¬Neutral rflᵄ ()
  Value→¬Neutral Uᵄ ()
  Value→¬Neutral ΠΣᵄ ()
  Value→¬Neutral ℕᵄ ()
  Value→¬Neutral Unitᵄ ()
  Value→¬Neutral Emptyᵄ ()
  Value→¬Neutral Idᵄ ()
  Value→¬Neutral (unitrec-ηᵄ Ī·) (unitrecā‚™ no-Ī· _) = no-Ī· Ī·

opaque

  -- If t is a value, then either t is in WHNF, or t is an application
  -- of unitrec and Ī·-equality is allowed for weak unit types.

  Value→Whnf :
    Value t →
    Whnf t āŠŽ āˆƒā‚† Ī» l p q A u v → t ≔ unitrec l p q A u v Ɨ UnitŹ·-Ī·
  Value→Whnf lamᵄ = inj₁ lamā‚™
  Value→Whnf zeroᵄ = inj₁ zeroā‚™
  Value→Whnf sucᵄ = inj₁ sucā‚™
  Value→Whnf starᵄ = inj₁ starā‚™
  Value→Whnf prodᵄ = inj₁ prodā‚™
  Value→Whnf rflᵄ = inj₁ rflā‚™
  Value→Whnf Uᵄ = inj₁ Uā‚™
  Value→Whnf ΠΣᵄ = inj₁ ΠΣₙ
  Value→Whnf ℕᵄ = inj₁ ā„•ā‚™
  Value→Whnf Unitᵄ = inj₁ Unitā‚™
  Value→Whnf Emptyᵄ = inj₁ Emptyā‚™
  Value→Whnf Idᵄ = inj₁ Idā‚™
  Value→Whnf (unitrec-ηᵄ x) = injā‚‚ (_ , _ , _ , _ , _ , _ , refl , x)

------------------------------------------------------------------------
-- Properties of the lookup relations

opaque

  -- Variable lookup with heap update is deterministic.

  lookup-det : {H : Heap k m} {t : Term n} {u : Term n′}
             → H ⊢ y ↦[ r ] t , ρ ⨾ H′
             → H ⊢ y ↦[ r ] u , ρ′ ⨾ H″
             → Ī£ (n ≔ n′) Ī» p → subst Term p t ≔ u
               Ɨ subst (Wk m) p ρ ≔ ρ′ Ɨ H′ ≔ H″
  lookup-det (here p-šŸ™ā‰”q) (here p-šŸ™ā‰”q′) =
    case -≔-functional p-šŸ™ā‰”q p-šŸ™ā‰”q′ of Ī» {
      refl →
    refl , refl , refl , refl }
  lookup-det (there x) (there y) =
    case lookup-det x y of Ī» {
      (refl , refl , refl , refl) →
    refl , refl , refl , refl }
  lookup-det (thereā— x) (thereā— y) =
    case lookup-det x y of Ī» {
      (refl , refl , refl , refl) →
    refl , refl , refl , refl}

opaque

  -- Variable lookup without heap update is deterministic.

  lookup-det′ : {H : Heap k m} {t : Term n} {u : Term n′}
             → H ⊢ y ↦ (t , ρ)
             → H ⊢ y ↦ (u , ρ′)
             → Ī£ (n ≔ n′) Ī» p → subst Term p t ≔ u Ɨ subst (Wk m) p ρ ≔ ρ′
  lookup-det′ here here = refl , refl , refl
  lookup-det′ (there d) (there d′) =
    case lookup-det′ d d′ of Ī» {
      (refl , refl , refl) →
    refl , refl , refl }
  lookup-det′ (thereā— d) (thereā— d′) =
    case lookup-det′ d d′ of Ī» {
      (refl , refl , refl) →
    refl , refl , refl }

opaque

  -- Lookup will either yield an entry or a dummy entry

  ā†¦āŠŽā†¦ā— : āˆ€ y → (āˆƒā‚‚ Ī» n (c : Entry _ n) → H ⊢ y ↦ c) āŠŽ H ⊢ y ā†¦ā—
  ā†¦āŠŽā†¦ā— {H = ε} ()
  ā†¦āŠŽā†¦ā— {H = H āˆ™ c} y0 = inj₁ (_ , _ , here)
  ā†¦āŠŽā†¦ā— {H = H āˆ™ā—} y0 = injā‚‚ here
  ā†¦āŠŽā†¦ā— {H = H āˆ™ c} (y +1) =
    case ā†¦āŠŽā†¦ā— y of Ī» where
      (inj₁ (_ , _ , d)) → inj₁ (_ , _ , there d)
      (injā‚‚ d) → injā‚‚ (there d)
  ā†¦āŠŽā†¦ā— {H = H āˆ™ā—} (y +1) =
    case ā†¦āŠŽā†¦ā— y of Ī» where
      (inj₁ (_ , _ , d)) → inj₁ (_ , _ , thereā— d)
      (injā‚‚ d) → injā‚‚ (thereā— d)

opaque

  -- Lookup cannot yield both an entry and a dummy entry.

  Ā¬ā†¦āˆ§ā†¦ā— : H ⊢ y ↦ e → H ⊢ y ā†¦ā— → ⊄
  Ā¬ā†¦āˆ§ā†¦ā— here ()
  Ā¬ā†¦āˆ§ā†¦ā— (there d) (there d′) = Ā¬ā†¦āˆ§ā†¦ā— d d′
  Ā¬ā†¦āˆ§ā†¦ā— (thereā— d) (thereā— d′) = Ā¬ā†¦āˆ§ā†¦ā— d d′

opaque

  -- If a heap does not contain erased entries then lookup to ā— will
  -- always fail.

  ¬erased-heapā†’Ā¬ā†¦ā— : {H : Heap k _} → H ⊢ y ā†¦ā— → k ≔ 0 → ⊄
  ¬erased-heapā†’Ā¬ā†¦ā— here ()
  ¬erased-heapā†’Ā¬ā†¦ā— (there d) k≔0 = ¬erased-heapā†’Ā¬ā†¦ā— d k≔0
  ¬erased-heapā†’Ā¬ā†¦ā— (thereā— d) ()

opaque

  ¬erased-heap→↦ :
    {H : Heap k m} → k ≔ 0 → (y : Ptr m) →
    āˆƒā‚‚ Ī» n (e : Entry m n) → H ⊢ y ↦ e
  ¬erased-heap→↦ k≔0 y =
    case ā†¦āŠŽā†¦ā— y of Ī» where
      (inj₁ x) → x
      (injā‚‚ x) → ⊄-elim (¬erased-heapā†’Ā¬ā†¦ā— x k≔0)

opaque

  -- If heap lookup with update succeeds lookup without heap update
  -- succeeds with the same result.

  ↦[]→↦ : H ⊢ y ↦[ q ] e ⨾ H′ → H ⊢ y ↦ e
  ↦[]→↦ (here x) = here
  ↦[]→↦ (there d) = there (↦[]→↦ d)
  ↦[]→↦ (thereā— d) = thereā— (↦[]→↦ d)

opaque

  -- Heap lookups match the corresponding substitution.

  heapSubstVar : H ⊢ y ↦[ q ] t , ρ ⨾ H′ → toSubstā‚• H y ≔ wk ρ t [ H ]ā‚•
  heapSubstVar {t} (here _) =
    sym (step-consSubst t)
  heapSubstVar {t} (there d) =
    trans (heapSubstVar d) (sym (step-consSubst t))
  heapSubstVar {H = H āˆ™ā—} {t} {ρ = step ρ} (thereā— d) =
    trans (cong wk1 (heapSubstVar d))
      (trans (sym (wk1-liftSubst (wk ρ t)))
        (cong (_[ H ]⇑ₕ) (wk1-wk ρ t)))

opaque

  -- Heap lookups match the corresponding substitution.

  heapSubstVar′ : H ⊢ y ↦ (t , ρ) → toSubstā‚• H y ≔ wk ρ t [ H ]ā‚•
  heapSubstVar′ {t} here =
    sym (step-consSubst t)
  heapSubstVar′ {t} (there d) =
    trans (heapSubstVar′ d) (sym (step-consSubst t))
  heapSubstVar′ {H = H āˆ™ā—} {t} {ρ = step ρ} (thereā— d) =
    trans (cong wk1 (heapSubstVar′ d))
      (trans (sym (wk1-liftSubst (wk ρ t)))
        (cong (_[ H ]⇑ₕ) (wk1-wk ρ t)))

opaque

  -- If subtraction of the grade correspoding to a heap entry cannot
  -- by subtracted by q then lookup of q copies fails.

  -≢-no-lookup :
    (āˆ€ {r} → H ⟨ y ⟩ʰ - q ≔ r → ⊄) →
    H ⊢ y ↦[ q ] e ⨾ H′ → ⊄
  -≢-no-lookup p-q≢r (here p-q≔r) =
    p-q≢r p-q≔r
  -≢-no-lookup p-q≢r (there d) =
    -≢-no-lookup p-q≢r d
  -≢-no-lookup p-q≢r (thereā— d) =
    -≢-no-lookup p-q≢r d

------------------------------------------------------------------------
-- Properties of stacks and continuations

opaque

  -- Applying a single substitution to a term and then to a continuation

  ā¦…ā¦†į¶œ-sgSubst : āˆ€ c → ⦅ c ā¦†į¶œ (t [ u ]ā‚€) ≔ ⦅ wk1ᶜ c ā¦†į¶œ t [ u ]ā‚€
  ā¦…ā¦†į¶œ-sgSubst (āˆ˜ā‚‘ p u ρ) =
    cong (_ ∘_) (sym (step-sgSubst _ _))
  ā¦…ā¦†į¶œ-sgSubst (fstā‚‘ p) = refl
  ā¦…ā¦†į¶œ-sgSubst (sndā‚‘ p) = refl
  ā¦…ā¦†į¶œ-sgSubst {u = v} (prodrecā‚‘ r p q A u ρ) =
    congā‚‚ (Ī» u A → prodrec r p q A _ u)
      (lifts-step-sgSubst 2 u)
      (lifts-step-sgSubst 1 A)
  ā¦…ā¦†į¶œ-sgSubst {u} (natrecā‚‘ p q r A z s ρ) =
    congā‚ƒ (Ī» A z s → natrec p q r A z s _)
      (lifts-step-sgSubst 1 A)
      (lifts-step-sgSubst 0 z)
      (lifts-step-sgSubst 2 s)
  ā¦…ā¦†į¶œ-sgSubst {u = v} (unitrecā‚‘ _ p q A u ρ) =
    congā‚‚ (Ī» u A → unitrec _ p q A _ u)
      (sym (step-sgSubst _ _))
      (lifts-step-sgSubst 1 A)
  ā¦…ā¦†į¶œ-sgSubst (emptyrecā‚‘ p A ρ) =
    cong (Ī» A → emptyrec p A _)
      (lifts-step-sgSubst 0 A)
  ā¦…ā¦†į¶œ-sgSubst (Jā‚‘ p q A t B u v ρ) =
    sym (congā‚… (Ī» A t B u v → J p q A t B u v _)
      (step-sgSubst A _) (step-sgSubst t _)
      (sym (lifts-step-sgSubst 2 B))
      (step-sgSubst u _) (step-sgSubst v _))
  ā¦…ā¦†į¶œ-sgSubst (Kā‚‘ p A t B u ρ) =
    sym (congā‚„ (Ī» A t B u → K p A t B u _)
      (step-sgSubst A _) (step-sgSubst t _)
      (sym (lifts-step-sgSubst 1 B))
      (step-sgSubst u _))
  ā¦…ā¦†į¶œ-sgSubst ([]-congā‚‘ s A t u ρ) =
    sym (congā‚ƒ (Ī» A t u → []-cong s A t u _)
      (step-sgSubst A _) (step-sgSubst t _)
      (step-sgSubst u _))
  ā¦…ā¦†į¶œ-sgSubst sucā‚‘ = refl

opaque

  -- Applying a single substitution to a term and then to a stack

  ⦅⦆ˢ-sgSubst : āˆ€ S → ⦅ S ⦆ˢ (t [ u ]ā‚€) ≔ ⦅ wk1Ė¢ S ⦆ˢ t [ u ]ā‚€
  ⦅⦆ˢ-sgSubst ε = refl
  ⦅⦆ˢ-sgSubst {t} {u} (e āˆ™ S) = begin
   ⦅ e āˆ™ S ⦆ˢ (t [ u ]ā‚€)              ā‰”āŸØāŸ©
   ⦅ S ⦆ˢ (⦅ e ā¦†į¶œ (t [ u ]ā‚€))          ā‰”āŸØ cong ⦅ S ⦆ˢ_ (ā¦…ā¦†į¶œ-sgSubst e) ⟩
   ⦅ S ⦆ˢ (⦅ wk1ᶜ e ā¦†į¶œ t [ u ]ā‚€)       ā‰”āŸØ ⦅⦆ˢ-sgSubst S ⟩
   ⦅ wk1Ė¢ S ⦆ˢ (⦅ wk1ᶜ e ā¦†į¶œ t) [ u ]ā‚€  ā‰”āŸØāŸ©
   ⦅ wk1Ė¢ (e āˆ™ S) ⦆ˢ t [ u ]ā‚€         āˆŽ

opaque

  -- Applying a double substitution to a term and then to a continuation

  ā¦…ā¦†į¶œ-[,] : āˆ€ e → ⦅ e ā¦†į¶œ (t [ u , v ]₁₀) ≔ ⦅ wk2ᶜ e ā¦†į¶œ t [ u , v ]₁₀
  ā¦…ā¦†į¶œ-[,] (āˆ˜ā‚‘ p u ρ) =
    cong (_ ∘_) (lifts-step-[,] 0 u)
  ā¦…ā¦†į¶œ-[,] (fstā‚‘ x) = refl
  ā¦…ā¦†į¶œ-[,] (sndā‚‘ x) = refl
  ā¦…ā¦†į¶œ-[,] (prodrecā‚‘ r p q A u ρ) =
    congā‚‚ (Ī» x y → prodrec r p q x _ y)
      (lifts-step-[,] 1 A)
      (lifts-step-[,] 2 u)
  ā¦…ā¦†į¶œ-[,] (natrecā‚‘ p q r A z s ρ) =
    congā‚ƒ (Ī» A z s → natrec p q r A z s _)
      (lifts-step-[,] 1 A)
      (lifts-step-[,] 0 z)
      (lifts-step-[,] 2 s)
  ā¦…ā¦†į¶œ-[,] (unitrecā‚‘ _ p q A u ρ) =
    congā‚‚ (Ī» x y → unitrec _ p q x _ y)
      (lifts-step-[,] 1 A) (lifts-step-[,] 0 u)
  ā¦…ā¦†į¶œ-[,] (emptyrecā‚‘ p A ρ) =
    cong (Ī» A → emptyrec p A _) (lifts-step-[,] 0 A)
  ā¦…ā¦†į¶œ-[,] (Jā‚‘ p q A t B u v ρ) =
    congā‚… (Ī» A t B u v → J p q A t B u v _)
      (lifts-step-[,] 0 A) (lifts-step-[,] 0 t)
      (lifts-step-[,] 2 B) (lifts-step-[,] 0 u)
      (lifts-step-[,] 0 v)
  ā¦…ā¦†į¶œ-[,] (Kā‚‘ p A t B u ρ) =
    congā‚„ (Ī» A t B u → K p A t B u _)
      (lifts-step-[,] 0 A) (lifts-step-[,] 0 t)
      (lifts-step-[,] 1 B) (lifts-step-[,] 0 u)
  ā¦…ā¦†į¶œ-[,] ([]-congā‚‘ s A t u ρ) =
    congā‚ƒ (Ī» A t u → []-cong s A t u _)
      (lifts-step-[,] 0 A) (lifts-step-[,] 0 t)
      (lifts-step-[,] 0 u)
  ā¦…ā¦†į¶œ-[,] sucā‚‘ = refl

opaque

  -- Applying a double substitution to a term and then to a stack

  ⦅⦆ˢ-[,] : āˆ€ S → ⦅ S ⦆ˢ (t [ u , v ]₁₀) ≔ ⦅ wk2Ė¢ S ⦆ˢ t [ u , v ]₁₀
  ⦅⦆ˢ-[,] ε = refl
  ⦅⦆ˢ-[,] {t} {u} {v} (e āˆ™ S) = begin
    ⦅ e āˆ™ S ⦆ˢ (t [ u , v ]₁₀)             ā‰”āŸØāŸ©
    ⦅ S ⦆ˢ (⦅ e ā¦†į¶œ (t [ u , v ]₁₀))         ā‰”āŸØ cong ⦅ S ⦆ˢ_ (ā¦…ā¦†į¶œ-[,] e) ⟩
    ⦅ S ⦆ˢ (⦅ wk2ᶜ e ā¦†į¶œ t [ u , v ]₁₀)      ā‰”āŸØ ⦅⦆ˢ-[,] S ⟩
    ⦅ wk2Ė¢ S ⦆ˢ (⦅ wk2ᶜ e ā¦†į¶œ t) [ u , v ]₁₀ ā‰”āŸØāŸ©
    ⦅ wk2Ė¢ (e āˆ™ S) ⦆ˢ t [ u , v ]₁₀        āˆŽ

opaque

  -- Weakening of a continuation applied to a Term

  wk-ā¦…ā¦†į¶œ : āˆ€ {ρ : Wk m n} e → wk ρ (⦅ e ā¦†į¶œ t) ≔ ⦅ wkᶜ ρ e ā¦†į¶œ (wk ρ t)
  wk-ā¦…ā¦†į¶œ {ρ} (āˆ˜ā‚‘ p u ρ′) =
    cong (_ ∘_) (wk-comp ρ ρ′ u)
  wk-ā¦…ā¦†į¶œ (fstā‚‘ p) = refl
  wk-ā¦…ā¦†į¶œ (sndā‚‘ p) = refl
  wk-ā¦…ā¦†į¶œ {ρ} (prodrecā‚‘ r p q A u ρ′) =
    congā‚‚ (Ī» A u → prodrec r p q A _ u)
      (wk-comp (lift ρ) (lift ρ′) A)
      (wk-comp (liftn ρ 2) (liftn ρ′ 2) u)
  wk-ā¦…ā¦†į¶œ {ρ} (natrecā‚‘ p q r A z s ρ′) =
    congā‚ƒ (Ī» A z s → natrec p q r A z s _)
      (wk-comp (lift ρ) (lift ρ′) A)
      (wk-comp ρ ρ′ z)
      (wk-comp (liftn ρ 2) (liftn ρ′ 2) s)
  wk-ā¦…ā¦†į¶œ {ρ} (unitrecā‚‘ _ p q A u ρ′) =
    congā‚‚ (Ī» A u → unitrec _ p q A _ u)
      (wk-comp (lift ρ) (lift ρ′) A)
      (wk-comp ρ ρ′ u)
  wk-ā¦…ā¦†į¶œ {ρ} (emptyrecā‚‘ p A ρ′) =
    cong (Ī» A → emptyrec p A _) (wk-comp ρ ρ′ A)
  wk-ā¦…ā¦†į¶œ {ρ} (Jā‚‘ p q A t B u v ρ′) =
    congā‚… (Ī» A t B u v → J p q A t B u v _)
      (wk-comp ρ ρ′ A) (wk-comp ρ ρ′ t)
      (wk-comp (liftn ρ 2) (liftn ρ′ 2) B)
      (wk-comp ρ ρ′ u) (wk-comp ρ ρ′ v)
  wk-ā¦…ā¦†į¶œ {ρ} (Kā‚‘ p A t B u ρ′) =
    congā‚„ (Ī» A t B u → K p A t B u _)
      (wk-comp ρ ρ′ A) (wk-comp ρ ρ′ t)
      (wk-comp (lift ρ) (lift ρ′) B) (wk-comp ρ ρ′ u)
  wk-ā¦…ā¦†į¶œ {ρ} ([]-congā‚‘ s A t u ρ′) =
    congā‚ƒ (Ī» A t u → []-cong s A t u _)
      (wk-comp ρ ρ′ A) (wk-comp ρ ρ′ t)
      (wk-comp ρ ρ′ u)
  wk-ā¦…ā¦†į¶œ {ρ} sucā‚‘ = refl

opaque

  -- A congruence property for continuations

  ā¦…ā¦†į¶œ-cong : āˆ€ e → t [ σ ] ≔ u [ σ ]
         → ⦅ e ā¦†į¶œ t [ σ ] ≔ ⦅ e ā¦†į¶œ u [ σ ]
  ā¦…ā¦†į¶œ-cong (āˆ˜ā‚‘ p u ρ) t≔u =
    cong (_∘ _) t≔u
  ā¦…ā¦†į¶œ-cong (fstā‚‘ x) t≔u =
    cong (fst _) t≔u
  ā¦…ā¦†į¶œ-cong (sndā‚‘ x) t≔u =
    cong (snd _) t≔u
  ā¦…ā¦†į¶œ-cong (prodrecā‚‘ r p q A u ρ) t≔u =
    cong (Ī» t → prodrec _ _ _ _ t _) t≔u
  ā¦…ā¦†į¶œ-cong (natrecā‚‘ p q r A z s ρ) t≔u =
    cong (Ī» t → natrec _ _ _ _ _ _ t) t≔u
  ā¦…ā¦†į¶œ-cong (unitrecā‚‘ _ p q A u ρ) t≔u =
    cong (Ī» t → unitrec _ _ _ _ t _) t≔u
  ā¦…ā¦†į¶œ-cong (emptyrecā‚‘ p A ρ) t≔u =
    cong (emptyrec _ _) t≔u
  ā¦…ā¦†į¶œ-cong (Jā‚‘ p q A t B u v ρ) t≔u =
    cong (J _ _ _ _ _ _ _) t≔u
  ā¦…ā¦†į¶œ-cong (Kā‚‘ p A t B u ρ) t≔u =
    cong (K _ _ _ _ _) t≔u
  ā¦…ā¦†į¶œ-cong ([]-congā‚‘ s A t u ρ) t≔u =
    cong ([]-cong _ _ _ _) t≔u
  ā¦…ā¦†į¶œ-cong sucā‚‘ t≔u =
    cong suc t≔u

opaque

  -- A congruence property for stacks

  ⦅⦆ˢ-cong : āˆ€ S → t [ σ ] ≔ u [ σ ]
         → ⦅ S ⦆ˢ t [ σ ] ≔ ⦅ S ⦆ˢ u [ σ ]
  ⦅⦆ˢ-cong ε t≔u = t≔u
  ⦅⦆ˢ-cong (e āˆ™ S) t≔u = ⦅⦆ˢ-cong S (ā¦…ā¦†į¶œ-cong e t≔u)

opaque

  -- Weakening of sucₛ k

  wk-sucā‚› : (ρ : Wk m n) (k : Nat) → wkĖ¢ ρ (sucā‚› k) ≔ sucā‚› k
  wk-sucā‚› ρ 0 = refl
  wk-sucā‚› ρ (1+ k) = cong (sucā‚‘ āˆ™_) (wk-sucā‚› ρ k)

opaque

  -- An inversion lemma for multiplicity of non-empty stacks

  āˆ£āˆ£āˆ™-inv : ∣ c āˆ™ S āˆ£ā‰” p → āˆƒā‚‚ Ī» q r → ∣ c āˆ£į¶œā‰” q Ɨ ∣ S āˆ£ā‰” r Ɨ p ≔ r Ā· q
  āˆ£āˆ£āˆ™-inv (e āˆ™ S) = _ , _ , e , S , refl

opaque

  -- Continuation weakening preserves multiplicity

  wk-∣∣ᶜ : ∣ c āˆ£į¶œā‰” p → ∣ wkᶜ ρ c āˆ£į¶œā‰” p
  wk-∣∣ᶜ āˆ˜ā‚‘ = āˆ˜ā‚‘
  wk-∣∣ᶜ fstā‚‘ = fstā‚‘
  wk-∣∣ᶜ sndā‚‘ = sndā‚‘
  wk-∣∣ᶜ (natrecā‚‘ x) = natrecā‚‘ x
  wk-∣∣ᶜ prodrecā‚‘ = prodrecā‚‘
  wk-∣∣ᶜ unitrecā‚‘ = unitrecā‚‘
  wk-∣∣ᶜ emptyrecā‚‘ = emptyrecā‚‘
  wk-∣∣ᶜ (Jā‚‘ x) = Jā‚‘ x
  wk-∣∣ᶜ (Kā‚‘ x) = Kā‚‘ x
  wk-∣∣ᶜ []-congā‚‘ = []-congā‚‘
  wk-∣∣ᶜ sucā‚‘ = sucā‚‘

opaque

  -- Stack weakening preserves multiplicity

  wk-∣∣ : ∣ S āˆ£ā‰” p → ∣ wkĖ¢ ρ S āˆ£ā‰” p
  wk-∣∣ ε = ε
  wk-∣∣ (e āˆ™ S) = wk-∣∣ᶜ e āˆ™ wk-∣∣ S

opaque

  -- The multiplicity relation for natrecā‚‘ is functional

  ∣natrec∣ᶜ-functional :
    ∣natrec p , r āˆ£ā‰” q → ∣natrec p , r āˆ£ā‰” q′ → q ≔ q′
  ∣natrec∣ᶜ-functional
    (has-nrā‚‘ ⦃ has-nr ⦄) (has-nrā‚‘ ⦃ has-nr = has-nr′ ⦄) =
    case Nr-available-propositional _ has-nr has-nr′ of Ī» where
      refl → refl
  ∣natrec∣ᶜ-functional (has-nrā‚‘ ⦃ has-nr ⦄) (no-nrā‚‘ ⦃ no-nr ⦄ x) =
    ⊄-elim (¬[Nr∧No-nr-glb] _ has-nr no-nr)
  ∣natrec∣ᶜ-functional (no-nrā‚‘ ⦃ no-nr ⦄ x) (has-nrā‚‘ ⦃ has-nr ⦄) =
    ⊄-elim (¬[Nr∧No-nr-glb] _ has-nr no-nr)
  ∣natrec∣ᶜ-functional (no-nrā‚‘ x) (no-nrā‚‘ y) =
    GLB-unique x y

opaque

  -- The multiplicity relation for Jā‚‘ is functional

  ∣J∣ᶜ-functional : ∣J em , p , q āˆ£ā‰” r → ∣J em , p , q āˆ£ā‰” r′ → r ≔ r′
  ∣J∣ᶜ-functional J-all J-all = refl
  ∣J∣ᶜ-functional (J-someā‚€ _ _) (J-someā‚€ _ _) = refl
  ∣J∣ᶜ-functional (J-someā‚€ pā‰”šŸ˜ qā‰”šŸ˜) (J-some false) =
    ⊄-elim (false (pā‰”šŸ˜ , qā‰”šŸ˜))
  ∣J∣ᶜ-functional (J-some false) (J-someā‚€ pā‰”šŸ˜ qā‰”šŸ˜) =
    ⊄-elim (false (pā‰”šŸ˜ , qā‰”šŸ˜))
  ∣J∣ᶜ-functional (J-some _) (J-some _) = refl
  ∣J∣ᶜ-functional J-none J-none = refl

opaque

  -- The multiplicity relation for Kā‚‘ is functional

  ∣K∣ᶜ-functional : ∣K em , p āˆ£ā‰” r → ∣K em , p āˆ£ā‰” r′ → r ≔ r′
  ∣K∣ᶜ-functional K-all K-all = refl
  ∣K∣ᶜ-functional (K-someā‚€ _) (K-someā‚€ _) = refl
  ∣K∣ᶜ-functional (K-someā‚€ pā‰”šŸ˜) (K-some pā‰¢šŸ˜) =
    ⊄-elim (pā‰¢šŸ˜ pā‰”šŸ˜)
  ∣K∣ᶜ-functional (K-some pā‰¢šŸ˜) (K-someā‚€ pā‰”šŸ˜) =
    ⊄-elim (pā‰¢šŸ˜ pā‰”šŸ˜)
  ∣K∣ᶜ-functional (K-some _) (K-some _) = refl
  ∣K∣ᶜ-functional K-none K-none = refl

opaque

  -- The multiplicity relation for continuations is functional

  ∣∣ᶜ-functional : ∣ c āˆ£į¶œā‰” p → ∣ c āˆ£į¶œā‰” q → p ≔ q
  ∣∣ᶜ-functional āˆ˜ā‚‘ āˆ˜ā‚‘ = refl
  ∣∣ᶜ-functional fstā‚‘ fstā‚‘ = refl
  ∣∣ᶜ-functional sndā‚‘ sndā‚‘ = refl
  ∣∣ᶜ-functional prodrecā‚‘ prodrecā‚‘ = refl
  ∣∣ᶜ-functional (natrecā‚‘ x) (natrecā‚‘ y) =
    ∣natrec∣ᶜ-functional x y
  ∣∣ᶜ-functional unitrecā‚‘ unitrecā‚‘ = refl
  ∣∣ᶜ-functional emptyrecā‚‘ emptyrecā‚‘ = refl
  ∣∣ᶜ-functional (Jā‚‘ x) (Jā‚‘ y) = ∣J∣ᶜ-functional x y
  ∣∣ᶜ-functional (Kā‚‘ x) (Kā‚‘ y) = ∣K∣ᶜ-functional x y
  ∣∣ᶜ-functional []-congā‚‘ []-congā‚‘ = refl
  ∣∣ᶜ-functional sucā‚‘ sucā‚‘ = refl

opaque

  -- The multiplicity relation for stacks is functional

  ∣∣-functional : ∣ S āˆ£ā‰” p → ∣ S āˆ£ā‰” q → p ≔ q
  ∣∣-functional ε ε = refl
  ∣∣-functional (e āˆ™ S) (e′ āˆ™ S′) =
    Ā·-cong (∣∣-functional S S′) (∣∣ᶜ-functional e e′)

opaque

  -- The multiplicity for natrecā‚‘ always exists if e is not natrecā‚‘ when
  -- the usage rule with greatest lower bounds is used.

  ∣nrāˆ£ā‰” : ⦃ has-nr : Nr-available ⦄ → ∃ Ī» q → ∣natrec p , r āˆ£ā‰” q
  ∣nrāˆ£ā‰” = _ , has-nrā‚‘

opaque

  -- The multiplicity relation for Jā‚‘ always inhabited

  ∣Jāˆ£ā‰” : ∃ Ī» r → ∣J em , p , q āˆ£ā‰” r
  ∣Jāˆ£ā‰” {em = none} = _ , J-none
  ∣Jāˆ£ā‰” {em = all} = _ , J-all
  ∣Jāˆ£ā‰” {em = some} {p} {q} =
    case is-šŸ˜? p of Ī» where
      (yes pā‰”šŸ˜) →
        case is-šŸ˜? q of Ī» where
          (yes qā‰”šŸ˜) → _ , J-someā‚€ pā‰”šŸ˜ qā‰”šŸ˜
          (no qā‰¢šŸ˜) → _ , J-some Ī» (_ , qā‰”šŸ˜) → qā‰¢šŸ˜ qā‰”šŸ˜
      (no pā‰¢šŸ˜) → _ , J-some (Ī» (pā‰”šŸ˜ , _) → pā‰¢šŸ˜ pā‰”šŸ˜)

opaque

  -- The multiplicity relation for Kā‚‘ always inhabited

  ∣Kāˆ£ā‰” : ∃ Ī» r → ∣K em , p āˆ£ā‰” r
  ∣Kāˆ£ā‰” {em = none} = _ , K-none
  ∣Kāˆ£ā‰” {em = all} = _ , K-all
  ∣Kāˆ£ā‰” {em = some} {p} =
    case is-šŸ˜? p of Ī» where
      (yes pā‰”šŸ˜) → _ , K-someā‚€ pā‰”šŸ˜
      (no pā‰¢šŸ˜) → _ , K-some pā‰¢šŸ˜

opaque

  -- The multiplicity for a continuation c always exists if when c is
  -- natrecā‚‘ then the usage rule for natrec using an nr function is used.

  āˆ£āˆ£į¶œā‰” :
    (āˆ€ {n p q r A u v ρ} → c ≔ natrecā‚‘ {n = n} p q r A u v ρ → Nr-available) →
    ∃ ∣ c āˆ£į¶œā‰”_
  āˆ£āˆ£į¶œā‰” {c = āˆ˜ā‚‘ p u ρ} _ = šŸ™ , āˆ˜ā‚‘
  āˆ£āˆ£į¶œā‰” {c = fstā‚‘ x} _ = šŸ™ , fstā‚‘
  āˆ£āˆ£į¶œā‰” {c = sndā‚‘ x} _ = šŸ™ , sndā‚‘
  āˆ£āˆ£į¶œā‰” {c = prodrecā‚‘ r p q A u ρ} _ = r , prodrecā‚‘
  āˆ£āˆ£į¶œā‰” {c = natrecā‚‘ p q r A z s ρ} has-nr =
    _ , natrecā‚‘ (∣nrāˆ£ā‰” ⦃ has-nr refl ⦄ .projā‚‚)
  āˆ£āˆ£į¶œā‰” {c = unitrecā‚‘ l p q A u ρ} _ = p , unitrecā‚‘
  āˆ£āˆ£į¶œā‰” {c = emptyrecā‚‘ p A ρ} _ = p , emptyrecā‚‘
  āˆ£āˆ£į¶œā‰” {c = Jā‚‘ p q A t B u v ρ} _ = _ , Jā‚‘ (∣Jāˆ£ā‰” .projā‚‚)
  āˆ£āˆ£į¶œā‰” {c = Kā‚‘ p A t B u ρ} _ = _ , Kā‚‘ (∣Kāˆ£ā‰” .projā‚‚)
  āˆ£āˆ£į¶œā‰” {c = []-congā‚‘ s A t u ρ} _ = šŸ˜ , []-congā‚‘
  āˆ£āˆ£į¶œā‰” {c = sucā‚‘} _ = šŸ™ , sucā‚‘

opaque

  -- The multiplicity relation for stacks is always inhabited is whenever
  -- the stack contains natrecā‚‘ the usage rule for natrec using nr
  -- functions is used.

  āˆ£āˆ£ā‰” : (āˆ€ {p r} → natrec p , r ∈ S → Nr-available) → ∃ ∣ S āˆ£ā‰”_
  āˆ£āˆ£ā‰” {S = ε} _ = šŸ™ , ε
  āˆ£āˆ£ā‰” {S = e āˆ™ S} has-nr =
    let _ , ∣Sāˆ£ā‰” = āˆ£āˆ£ā‰” (has-nr āˆ˜ā†’ there)
        _ , ∣eāˆ£ā‰” = āˆ£āˆ£į¶œā‰” Ī» { refl → has-nr here}
    in  _ , ∣eāˆ£ā‰” āˆ™ ∣Sāˆ£ā‰”

opaque

  -- A variant of the above for it assumed that the stack does not
  -- contain any occurences of natrecā‚‘.

  nrāˆ‰-āˆ£āˆ£ā‰” : (āˆ€ {p r} → ¬ natrec p , r ∈ S) → ∃ ∣ S āˆ£ā‰”_
  nrāˆ‰-āˆ£āˆ£ā‰” nrāˆ‰ = āˆ£āˆ£ā‰” (Ī» nr∈ → ⊄-elim (nrāˆ‰ nr∈))

opaque

  -- An inequality satisfied by the multiplicity of natrecā‚‘

  ∣natrecāˆ£ā‰¤ : ∣natrec p , r āˆ£ā‰” q → q ≤ p + r Ā· q
  ∣natrecāˆ£ā‰¤ has-nrā‚‘ = nr₂≤
  ∣natrecāˆ£ā‰¤ (no-nrā‚‘ x) = nrįµ¢-GLB-≤ x

opaque

  -- Under some conditions, the multiplicity of Jā‚‘ is ω

  ∣Jāˆ£ā‰”Ļ‰ :
    em ≤ᵉᵐ some → (em ≔ some → ¬ (p ≔ šŸ˜ Ɨ q ≔ šŸ˜)) →
    ∣J em , p , q āˆ£ā‰” ω
  ∣Jāˆ£ā‰”Ļ‰ {(none)} _ _ = J-none
  ∣Jāˆ£ā‰”Ļ‰ {(all)} () _
  ∣Jāˆ£ā‰”Ļ‰ {(some)} _ ā‰¢šŸ˜ = J-some (ā‰¢šŸ˜ refl)

opaque

  -- Under some conditions, the multiplicity of Kā‚‘ is ω

  ∣Kāˆ£ā‰”Ļ‰ :
    em ≤ᵉᵐ some → (em ≔ some → p ≢ šŸ˜) →
    ∣K em , p āˆ£ā‰” ω
  ∣Kāˆ£ā‰”Ļ‰ {(none)} _ _ = K-none
  ∣Kāˆ£ā‰”Ļ‰ {(all)} () _
  ∣Kāˆ£ā‰”Ļ‰ {(some)} _ ā‰¢šŸ˜ = K-some (ā‰¢šŸ˜ refl)

opaque

  ∣sucā‚›āˆ£ā‰”šŸ™ : āˆ€ k → ∣ sucā‚› {m} k āˆ£ā‰” šŸ™
  ∣sucā‚›āˆ£ā‰”šŸ™ 0 = ε
  ∣sucā‚›āˆ£ā‰”šŸ™ (1+ k) =
    subst (∣ _ āˆ™ sucā‚› k āˆ£ā‰”_) (Ā·-identityʳ šŸ™) (sucā‚‘ āˆ™ ∣sucā‚›āˆ£ā‰”šŸ™ k)

opaque

  ∣S++sucā‚›āˆ£ā‰”āˆ£S∣ : ∣ S āˆ£ā‰” p → ∣ S ++ sucā‚› k āˆ£ā‰” p
  ∣S++sucā‚›āˆ£ā‰”āˆ£S∣ ε = ∣sucā‚›āˆ£ā‰”šŸ™ _
  ∣S++sucā‚›āˆ£ā‰”āˆ£S∣ (e āˆ™ S) = e āˆ™ ∣S++sucā‚›āˆ£ā‰”āˆ£S∣ S

opaque

  -- If an erased prodrec token is on the stack then the stack
  -- multiplicity is zero (if it exists).

  prā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ : ∣ S āˆ£ā‰” q → prodrec šŸ˜ , p ∈ S → q ≔ šŸ˜
  prā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ ε ()
  prā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ (prodrecā‚‘ āˆ™ ∣Sāˆ£ā‰”) here = Ā·-zeroʳ _
  prā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ (_ āˆ™ ∣Sāˆ£ā‰”) (there x) =
    trans (Ā·-congʳ (prā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ ∣Sāˆ£ā‰” x)) (Ā·-zeroĖ” _)

opaque

  -- If an erased unitrec token is on the stack then the stack
  -- multiplicity is zero (if it exists).

  urā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ : ∣ S āˆ£ā‰” q → unitrec šŸ˜ ∈ S → q ≔ šŸ˜
  urā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ ε ()
  urā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ (unitrecā‚‘ āˆ™ ∣Sāˆ£ā‰”) here = Ā·-zeroʳ _
  urā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ (_ āˆ™ ∣Sāˆ£ā‰”) (there x) =
    trans (Ā·-congʳ (urā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ ∣Sāˆ£ā‰” x)) (Ā·-zeroĖ” _)

opaque

  -- If an erased emptyrec token is on the stack then the stack
  -- multiplicity is zero (if it exists).

  erā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ : ∣ S āˆ£ā‰” q → emptyrec šŸ˜ ∈ S → q ≔ šŸ˜
  erā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ ε ()
  erā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ (emptyrecā‚‘ āˆ™ ∣Sāˆ£ā‰”) here = Ā·-zeroʳ _
  erā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ (_ āˆ™ ∣Sāˆ£ā‰”) (there x) =
    trans (Ā·-congʳ (erā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ ∣Sāˆ£ā‰” x)) (Ā·-zeroĖ” _)

opaque

  -- Under some conditions, the stack multiplicity is šŸ˜ (if it exists).

  āˆ£āˆ£ā‰”šŸ˜ :
    ∣ S āˆ£ā‰” q → prodrec šŸ˜ , p ∈ S āŠŽ (unitrec šŸ˜ ∈ S) āŠŽ (emptyrec šŸ˜ ∈ S) →
    q ≔ šŸ˜
  āˆ£āˆ£ā‰”šŸ˜ ∣Sāˆ£ā‰” (inj₁ prā‚€āˆˆ) = prā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ ∣Sāˆ£ā‰” prā‚€āˆˆ
  āˆ£āˆ£ā‰”šŸ˜ ∣Sāˆ£ā‰” (injā‚‚ (inj₁ urā‚€āˆˆ)) = urā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ ∣Sāˆ£ā‰” urā‚€āˆˆ
  āˆ£āˆ£ā‰”šŸ˜ ∣Sāˆ£ā‰” (injā‚‚ (injā‚‚ erā‚€āˆˆ)) = erā‚€āˆˆā†’āˆ£Sāˆ£ā‰”šŸ˜ ∣Sāˆ£ā‰” erā‚€āˆˆ

opaque

  -- Under some conditions, the stack multiplicity is šŸ˜.

  nrāˆ‰ā†’āˆ£āˆ£ā‰”šŸ˜ :
    (āˆ€ {p r} → natrec p , r ∈ S → ⊄) →
    prodrec šŸ˜ , p ∈ S āŠŽ (unitrec šŸ˜ ∈ S) āŠŽ (emptyrec šŸ˜ ∈ S) → ∣ S āˆ£ā‰” šŸ˜
  nrāˆ‰ā†’āˆ£āˆ£ā‰”šŸ˜ nrāˆ‰ assumption =
    let _ , ∣Sāˆ£ā‰” = nrāˆ‰-āˆ£āˆ£ā‰” nrāˆ‰
    in  subst (∣ _ āˆ£ā‰”_) (āˆ£āˆ£ā‰”šŸ˜ ∣Sāˆ£ā‰” assumption) ∣Sāˆ£ā‰”

opaque

  -- The multiplicity of natrecā‚‘ is not šŸ˜.

  ∣nrāˆ£ā‰¢šŸ˜ :
   ⦃ Has-well-behaved-zero _ semiring-with-meet ⦄ →
   ∣natrec p , r āˆ£ā‰” q → q ≢ šŸ˜
  ∣nrāˆ£ā‰¢šŸ˜ has-nrā‚‘ = nrā‚‚ā‰¢šŸ˜
  ∣nrāˆ£ā‰¢šŸ˜ (no-nrā‚‘ x) refl = šŸ˜ā‰°šŸ™ (x .proj₁ 0)

opaque

  -- If the stack multiplicity is šŸ˜ then the stack contains an erased
  -- prodrec, unitrec or emptyrec or J, K or []-cong.

  āˆ£āˆ£ā‰”šŸ˜ā†’erased-match :
    ⦃ Has-well-behaved-zero _ semiring-with-meet ⦄ →
    ∣ S āˆ£ā‰” šŸ˜ →
    (∃ Ī» p → prodrec šŸ˜ , p ∈ S) āŠŽ (unitrec šŸ˜ ∈ S) āŠŽ (emptyrec šŸ˜ ∈ S) āŠŽ
    (āˆƒā‚‚ Ī» p q → J p , q ∈ S) āŠŽ (∃ Ī» p → K p ∈ S) āŠŽ ([]-cong∈ S)
  āˆ£āˆ£ā‰”šŸ˜ā†’erased-match = lemma refl
    where
    there′ :
      (∃ Ī» p → prodrec šŸ˜ , p ∈ S) āŠŽ (unitrec šŸ˜ ∈ S) āŠŽ (emptyrec šŸ˜ ∈ S) āŠŽ
      (āˆƒā‚‚ Ī» p q → J p , q ∈ S) āŠŽ (∃ Ī» p → K p ∈ S) āŠŽ ([]-cong∈ S) →
      (∃ Ī» p → prodrec šŸ˜ , p ∈ (c āˆ™ S)) āŠŽ (unitrec šŸ˜ ∈ c āˆ™ S) āŠŽ (emptyrec šŸ˜ ∈ c āˆ™ S) āŠŽ
      (āˆƒā‚‚ Ī» p q → J p , q ∈ c āˆ™ S) āŠŽ (∃ Ī» p → K p ∈ c āˆ™ S) āŠŽ ([]-cong∈ c āˆ™ S)
    there′ (inj₁ (_ , x)) = inj₁ (_ , there x)
    there′ (injā‚‚ (inj₁ x)) = injā‚‚ (inj₁ (there x))
    there′ (injā‚‚ (injā‚‚ (inj₁ x))) = injā‚‚ (injā‚‚ (inj₁ (there x)))
    there′ (injā‚‚ (injā‚‚ (injā‚‚ (inj₁ (_ , _ , x))))) = injā‚‚ (injā‚‚ (injā‚‚ (inj₁ (_ , _ , there x))))
    there′ (injā‚‚ (injā‚‚ (injā‚‚ (injā‚‚ (inj₁ (_ , x)))))) = injā‚‚ (injā‚‚ (injā‚‚ (injā‚‚ (inj₁ (_ , there x)))))
    there′ (injā‚‚ (injā‚‚ (injā‚‚ (injā‚‚ (injā‚‚ x))))) = injā‚‚ (injā‚‚ (injā‚‚ (injā‚‚ (injā‚‚ (there x)))))
    here′ :
      q ≔ šŸ˜ → ∣ c āˆ£į¶œā‰” q →
      (∃ Ī» p → prodrec šŸ˜ , p ∈ (c āˆ™ S)) āŠŽ (unitrec šŸ˜ ∈ c āˆ™ S) āŠŽ (emptyrec šŸ˜ ∈ c āˆ™ S) āŠŽ
      (āˆƒā‚‚ Ī» p q → J p , q ∈ c āˆ™ S) āŠŽ (∃ Ī» p → K p ∈ c āˆ™ S) āŠŽ ([]-cong∈ c āˆ™ S)
    here′ q≔ āˆ˜ā‚‘ = ⊄-elim (non-trivial q≔)
    here′ q≔ fstā‚‘ = ⊄-elim (non-trivial q≔)
    here′ q≔ sndā‚‘ = ⊄-elim (non-trivial q≔)
    here′ refl prodrecā‚‘ = inj₁ (_ , here)
    here′ q≔ (natrecā‚‘ x) = ⊄-elim (∣nrāˆ£ā‰¢šŸ˜ x q≔)
    here′ refl unitrecā‚‘ = injā‚‚ (inj₁ here)
    here′ refl emptyrecā‚‘ = injā‚‚ (injā‚‚ (inj₁ here))
    here′ q≔ (Jā‚‘ x) = injā‚‚ (injā‚‚ (injā‚‚ (inj₁ (_ , _ , here))))
    here′ q≔ (Kā‚‘ x) = injā‚‚ (injā‚‚ (injā‚‚ (injā‚‚ (inj₁ (_ , here)))))
    here′ q≔ []-congā‚‘ = injā‚‚ (injā‚‚ (injā‚‚ (injā‚‚ (injā‚‚ here))))
    here′ q≔ sucā‚‘ = ⊄-elim (non-trivial q≔)
    lemma :
      q ≔ šŸ˜ → ∣ S āˆ£ā‰” q →
      (∃ Ī» p → prodrec šŸ˜ , p ∈ S) āŠŽ (unitrec šŸ˜ ∈ S) āŠŽ (emptyrec šŸ˜ ∈ S) āŠŽ
      (āˆƒā‚‚ Ī» p q → J p , q ∈ S) āŠŽ (∃ Ī» p → K p ∈ S) āŠŽ ([]-cong∈ S)
    lemma q≔ ε = ⊄-elim (non-trivial q≔)
    lemma q≔ (∣eāˆ£ā‰” āˆ™ ∣Sāˆ£ā‰”) =
      case zero-product q≔ of Ī» where
        (inj₁ x) → there′ (lemma x ∣Sāˆ£ā‰”)
        (injā‚‚ x) → here′ x ∣eāˆ£ā‰”

opaque

  -- If a certain greatest lower bound does not exist then the stack
  -- multiplicity does not necessarily exist.

  āˆ£āˆ£ā‰¢ :
    ⦃ no-nr : Nr-not-available-GLB ⦄ →
    ¬ (∃ Ī» q → Greatest-lower-bound q (nrįµ¢ r šŸ™ p)) →
    ∃ Ī» (S : Stack m) → āˆ€ q → ∣ S āˆ£ā‰” q → ⊄
  āˆ£āˆ£ā‰¢ {r} {p} ⦃ no-nr ⦄ ¬glb =
    (natrecā‚‘ p šŸ˜ r ā„• zero zero id āˆ™ ε) ,
    Ī» { _ (natrecā‚‘ (has-nrā‚‘ ⦃ has-nr ⦄) āˆ™ _) → ¬[Nr∧No-nr-glb] _ has-nr no-nr
      ; _ (natrecā‚‘ (no-nrā‚‘ x) āˆ™ _) → ¬glb (_ , x)}

opaque

  -- Stack concatenation

  ⦅⦆ˢ++ : āˆ€ S S′ → ⦅ S ++ S′ ⦆ˢ t ≔ ⦅ S′ ⦆ˢ (⦅ S ⦆ˢ t)
  ⦅⦆ˢ++ ε S′ = refl
  ⦅⦆ˢ++ (c āˆ™ S) S′ = ⦅⦆ˢ++ S S′

opaque

  -- Weakening of stack concatenation

  wk-++ : (ρ : Wk m n) (S : Stack n) → wkĖ¢ ρ (S ++ S′) ≔ wkĖ¢ ρ S ++ wkĖ¢ ρ S′
  wk-++ ρ ε = refl
  wk-++ ρ (c āˆ™ S) = cong (_ āˆ™_) (wk-++ ρ S)

opaque

  -- A specialized version of the property above

  wk-++sucā‚› : (ρ : Wk m n) (S : Stack n) → wkĖ¢ ρ (S ++ sucā‚› k) ≔ wkĖ¢ ρ S ++ sucā‚› k
  wk-++sucā‚› {k} ρ S = trans (wk-++ ρ S) (cong (_ ++_) (wk-sucā‚› ρ k))

opaque

  -- Concatenation of sucₛ k

  sucā‚›++sucā‚› : āˆ€ k k′ → sucā‚› {m} k ++ sucā‚› k′ ≔ sucā‚› (k +ⁿ k′)
  sucā‚›++sucā‚› 0 k′ = refl
  sucā‚›++sucā‚› (1+ k) k′ = cong (sucā‚‘ āˆ™_) (sucā‚›++sucā‚› k k′)

opaque

  -- Applying a term to a continuation becomes neutral only if the
  -- term is neutral.

  ā¦…ā¦†į¶œ-neutral : āˆ€ c → Neutral (⦅ c ā¦†į¶œ t) → Neutral t
  ā¦…ā¦†į¶œ-neutral (āˆ˜ā‚‘ p u ρ) (āˆ˜ā‚™ n) = n
  ā¦…ā¦†į¶œ-neutral (fstā‚‘ x) (fstā‚™ n) = n
  ā¦…ā¦†į¶œ-neutral (sndā‚‘ x) (sndā‚™ n) = n
  ā¦…ā¦†į¶œ-neutral (prodrecā‚‘ r p q A u ρ) (prodrecā‚™ n) = n
  ā¦…ā¦†į¶œ-neutral (natrecā‚‘ p q r A z s ρ) (natrecā‚™ n) = n
  ā¦…ā¦†į¶œ-neutral (unitrecā‚‘ l p q A u ρ) (unitrecā‚™ x n) = n
  ā¦…ā¦†į¶œ-neutral (emptyrecā‚‘ p A ρ) (emptyrecā‚™ n) = n
  ā¦…ā¦†į¶œ-neutral (Jā‚‘ p q A t B u v ρ) (Jā‚™ n) = n
  ā¦…ā¦†į¶œ-neutral (Kā‚‘ p A t B u ρ) (Kā‚™ n) = n
  ā¦…ā¦†į¶œ-neutral ([]-congā‚‘ s A t u ρ) ([]-congā‚™ n) = n
  ā¦…ā¦†į¶œ-neutral sucā‚‘ ()

opaque

  -- Injectivity of stacks

  stack-injective : {c : Cont m} {S : Stack m}
                  → c āˆ™ S ≔ c′ āˆ™ S′ → c ≔ c′ Ɨ S ≔ S′
  stack-injective refl = refl , refl

opaque

  -- Injectivity of the stack sucₛ k

  sucā‚›-injective : sucā‚› {m} n ≔ sucā‚› n′ → n ≔ n′
  sucā‚›-injective {n = 0}    {n′ = 0}    _ = refl
  sucā‚›-injective {n = 1+ _} {n′ = 1+ _} x =
    cong 1+ (sucā‚›-injective (stack-injective x .projā‚‚))
  sucā‚›-injective {n = 0}    {n′ = 1+ _} ()
  sucā‚›-injective {n = 1+ _} {n′ = 0}    ()

------------------------------------------------------------------------
-- Properties of heap equality

opaque

  -- Heap equality is reflective

  ~ʰ-refl : H ~ʰ H
  ~ʰ-refl {H = ε} = ε
  ~ʰ-refl {H = H āˆ™ c} = ~ʰ-refl āˆ™ _
  ~ʰ-refl {H = H āˆ™ā—} = ~ʰ-refl āˆ™ā—

opaque

  -- Heap equality is symmetric

  ~ʰ-sym : H ~ʰ H′ → H′ ~ʰ H
  ~ʰ-sym ε = ε
  ~ʰ-sym (H~H′ āˆ™ c) = ~ʰ-sym H~H′ āˆ™ c
  ~ʰ-sym (H~H′ āˆ™ā—) = ~ʰ-sym H~H′ āˆ™ā—

opaque

  -- Heap equality is transitive

  ~ʰ-trans : H ~ʰ H′ → H′ ~ʰ H″ → H ~ʰ H″
  ~ʰ-trans ε ε = ε
  ~ʰ-trans (H~H′ āˆ™ c) (H′~H″ āˆ™ .c) = ~ʰ-trans H~H′ H′~H″ āˆ™ c
  ~ʰ-trans (H~H′ āˆ™ā—) (H′~H″ āˆ™ā—) = ~ʰ-trans H~H′ H′~H″ āˆ™ā—

opaque

  -- Heap lookup without update behaves the same on equal heaps

  ~ʰ-lookup : H ~ʰ H′ → H ⊢ y ↦ e → H′ ⊢ y ↦ e
  ~ʰ-lookup ε ()
  ~ʰ-lookup (H~H′ āˆ™ _) here = here
  ~ʰ-lookup (H~H′ āˆ™ _) (there d) = there (~ʰ-lookup H~H′ d)
  ~ʰ-lookup (H~H′ āˆ™ā—) (thereā— d) = thereā— (~ʰ-lookup H~H′ d)

opaque

  -- Heap lookup to ā— behaves the same on equal heaps

  ~ʰ-lookupā— : H ~ʰ H′ → H ⊢ y ā†¦ā— → H′ ⊢ y ā†¦ā—
  ~ʰ-lookupā— ε ()
  ~ʰ-lookupā— (H~H′ āˆ™ā—) here = here
  ~ʰ-lookupā— (H~H′ āˆ™ _) (there d) = there (~ʰ-lookupā— H~H′ d)
  ~ʰ-lookupā— (H~H′ āˆ™ā—) (thereā— d) = thereā— (~ʰ-lookupā— H~H′ d)

opaque

  -- Equal heaps are equal as substitutions

  ~ʰ-subst : H ~ʰ H′ → toSubstā‚• H ≔ toSubstā‚• H′
  ~ʰ-subst ε = refl
  ~ʰ-subst (H~H′ āˆ™ (t , ρ)) =
    case ~ʰ-subst H~H′ of Ī»
      H≔H′ →
    congā‚‚ consSubst H≔H′ (cong (wk ρ t [_]) H≔H′)
  ~ʰ-subst (H~H′ āˆ™ā—) =
    cong liftSubst (~ʰ-subst H~H′)

opaque

  -- An updated heap is equal to the original one (up to grades)

  update-~ʰ : H ⊢ y ↦[ q ] e ⨾ H′ → H ~ʰ H′
  update-~ʰ (here _) = ~ʰ-refl āˆ™ _
  update-~ʰ (there d) = update-~ʰ d āˆ™ _
  update-~ʰ (thereā— d) = update-~ʰ d āˆ™ā—

------------------------------------------------------------------------
-- Properties of states in normal form

opaque

  wk1-Normal : Normal ⟨ H , t , ρ , S ⟩ → Normal ⟨ H āˆ™ (p , e) , t , step ρ , wk1Ė¢ S ⟩
  wk1-Normal (val x) = val x
  wk1-Normal (var d) = var (there d)

opaque

  wk1ā—-Normal : Normal ⟨ H , t , ρ , S ⟩ → Normal ⟨ H āˆ™ā— , t , step ρ , wk1Ė¢ S ⟩
  wk1ā—-Normal (val x) = val x
  wk1ā—-Normal (var d) = var (thereā— d)

opaque

  -- The stack of a normal state can be replaced to give a normal state

  Normal-stack : Normal ⟨ H , t , ρ , S ⟩ → Normal ⟨ H , t , ρ , S′ ⟩
  Normal-stack (val x) = val x
  Normal-stack (var x) = var x

opaque

  -- The heap of a normal state can be replaced by an equal heap and the
  -- stack can be replaced with any stack to give a normal state.

  ~ʰ-Normal : H ~ʰ H′ → Normal ⟨ H , t , ρ , S ⟩ → Normal ⟨ H′ , t , ρ , S′ ⟩
  ~ʰ-Normal H~H′ (val x) = val x
  ~ʰ-Normal H~H′ (var x) = var (~ʰ-lookupā— H~H′ x)

------------------------------------------------------------------------
-- Properties of heaps as substitutions

opaque

  -- Weakenings of heaps as substitutions

  wk-[]ā‚• : ρ ∷ H āŠ‡Ź° H′ → (t : Term n) → t [ H′ ]ā‚• ≔ wk ρ t [ H ]ā‚•
  wk-[]ā‚• {H} id t = cong (_[ H ]ā‚•) (sym (wk-id t))
  wk-[]ā‚• (step ρ) t = trans (wk-[]ā‚• ρ t) (sym (step-consSubst t))
  wk-[]ā‚• (lift {ρ} {H} {H′} {e = u , ρ′} [ρ]) t = begin
    t [ consSubst (toSubstā‚• H′) (wk ρ′ u [ H′ ]ā‚•) ]                     ā‰”Ė˜āŸØ singleSubstComp (wk ρ′ u [ H′ ]ā‚•) (toSubstā‚• H′) t ⟩
    t [ liftSubst (toSubstā‚• H′) ] [ wk ρ′ u [ H′ ]ā‚• ]ā‚€                  ā‰”Ė˜āŸØ singleSubstLift t (wk ρ′ u) ⟩
    t [ wk ρ′ u ]ā‚€ [ H′ ]ā‚•                                              ā‰”āŸØ wk-[]ā‚• [ρ] (t [ wk ρ′ u ]ā‚€) ⟩
    wk ρ (t [ wk ρ′ u ]ā‚€) [ H ]ā‚•                                        ā‰”āŸØ cong (_[ H ]ā‚•) (wk-β t) ⟩
    wk (lift ρ) t [ wk ρ (wk ρ′ u) ]ā‚€ [ H ]ā‚•                            ā‰”āŸØ cong (Ī» x → wk (lift ρ) t [ x ]ā‚€ [ H ]ā‚•) (wk-comp ρ ρ′ u) ⟩
    wk (lift ρ) t [ wk (ρ • ρ′) u ]ā‚€ [ H ]ā‚•                             ā‰”āŸØ singleSubstLift (wk (lift ρ) t) (wk (ρ • ρ′) u) ⟩
    wk (lift ρ) t [ liftSubst (toSubstā‚• H) ] [ wk (ρ • ρ′) u [ H ]ā‚• ]ā‚€  ā‰”āŸØ singleSubstComp (wk (ρ • ρ′) u [ H ]ā‚•) (toSubstā‚• H) (wk (lift ρ) t) ⟩
    wk (lift ρ) t [ consSubst (toSubstā‚• H) (wk (ρ • ρ′) u [ H ]ā‚•) ]     āˆŽ

opaque

  -- A heap updated by a pointer lookup gives the same substitution
  -- as the original heap.

  heapUpdateSubst : H ⊢ y ↦[ q ] e ⨾ H′ → toSubstā‚• H ≔ toSubstā‚• H′
  heapUpdateSubst d = ~ʰ-subst (update-~ʰ d)

opaque

  -- Erased heaps are identity substitutions

  erasedHeap≔idsubst : āˆ€ x → toSubstā‚• (erasedHeap n) x ≔ idSubst x
  erasedHeap≔idsubst x0 = refl
  erasedHeap≔idsubst (x +1) = cong wk1 (erasedHeap≔idsubst x)

opaque

  -- A collorary to the above property

  erasedHeap-subst : āˆ€ t → t [ erasedHeap n ]ā‚• ≔ t
  erasedHeap-subst t = trans (substVar-to-subst erasedHeap≔idsubst t) (subst-id t)

opaque

  -- The weakening toWkā‚•Ā H acts as an "inverse" to toSubstā‚•Ā H

  toWkā‚•-toSubstā‚•-var : (H : Heap k m) (x : Fin k)
        → toSubstā‚• H (wkVar (toWkā‚• H) x) ≔ idSubst x
  toWkā‚•-toSubstā‚•-var ε ()
  toWkā‚•-toSubstā‚•-var (H āˆ™ c) x = toWkā‚•-toSubstā‚•-var H x
  toWkā‚•-toSubstā‚•-var (H āˆ™ā—) x0 = refl
  toWkā‚•-toSubstā‚•-var (H āˆ™ā—) (x +1) = cong wk1 (toWkā‚•-toSubstā‚•-var H x)

opaque

  -- The weakening toWkā‚•Ā H acts as an "inverse" to toSubstā‚•Ā H

  toWkā‚•-toSubstā‚• : (H : Heap k m) (t : Term k)
                 → wk (toWkā‚• H) t [ H ]ā‚• ≔ t
  toWkā‚•-toSubstā‚• H t = begin
    wk (toWkā‚• H) t [ H ]ā‚•       ā‰”āŸØ subst-wk t ⟩
    t [ toSubstā‚• H ₛ• toWkā‚• H ] ā‰”āŸØ substVar-to-subst (toWkā‚•-toSubstā‚•-var H) t ⟩
    t [ idSubst ]               ā‰”āŸØ subst-id t ⟩
    t                           āˆŽ

opaque

  -- Substituting a variable corresponding to a dummy entry

  toSubstā‚•-erased : (H : Heap k m) (y : Fin m)
                  → H ⊢ y ā†¦ā— → ∃ Ī» y′ → toSubstā‚• H y ≔ var y′
  toSubstā‚•-erased ε () _
  toSubstā‚•-erased (H āˆ™ c) y0 ()
  toSubstā‚•-erased (H āˆ™ c) (y +1) (there d) = toSubstā‚•-erased H y d
  toSubstā‚•-erased (H āˆ™ā—) y0 d = y0 , refl
  toSubstā‚•-erased (H āˆ™ā—) (y +1) (thereā— d) =
    case toSubstā‚•-erased H y d of Ī»
      (y′ , ≔y′) →
    y′ +1 , cong wk1 ≔y′

opaque

  -- A term that is neutral at a variable with a dummy entry in the heap
  -- will still be neutral at the same variable after applying the heap
  -- substitution.

  toSubstā‚•-NeutralAt : (d : H ⊢ y ā†¦ā—)
                     → NeutralAt y t
                     → NeutralAt (toSubstā‚•-erased H y d .proj₁) (t [ H ]ā‚•)
  toSubstā‚•-NeutralAt d var with toSubstā‚•-erased _ _ d
  … | (x′ , ≔x′) =
    subst (NeutralAt _) (sym ≔x′) var
  toSubstā‚•-NeutralAt d (āˆ˜ā‚™ n) =
    āˆ˜ā‚™ (toSubstā‚•-NeutralAt d n)
  toSubstā‚•-NeutralAt d (fstā‚™ n) =
    fstā‚™ (toSubstā‚•-NeutralAt d n)
  toSubstā‚•-NeutralAt d (sndā‚™ n) =
    sndā‚™ (toSubstā‚•-NeutralAt d n)
  toSubstā‚•-NeutralAt d (natrecā‚™ n) =
    natrecā‚™ (toSubstā‚•-NeutralAt d n)
  toSubstā‚•-NeutralAt d (prodrecā‚™ n) =
    prodrecā‚™ (toSubstā‚•-NeutralAt d n)
  toSubstā‚•-NeutralAt d (emptyrecā‚™ n) =
    emptyrecā‚™ (toSubstā‚•-NeutralAt d n)
  toSubstā‚•-NeutralAt d (unitrecā‚™ x n) =
    unitrecā‚™ x (toSubstā‚•-NeutralAt d n)
  toSubstā‚•-NeutralAt d (Jā‚™ n) =
    Jā‚™ (toSubstā‚•-NeutralAt d n)
  toSubstā‚•-NeutralAt d (Kā‚™ n) =
    Kā‚™ (toSubstā‚•-NeutralAt d n)
  toSubstā‚•-NeutralAt d ([]-congā‚™ n) =
    []-congā‚™ (toSubstā‚•-NeutralAt d n)

opaque

  -- ⦅_⦆ is an inverse of initial.

  ⦅initial⦆≔ : ⦅ initial t ⦆ ≔ t
  ⦅initial⦆≔ = trans (erasedHeap-subst (wk id _)) (wk-id _)