open import Prelude

module Implicits.Semantics.Term where

open import Implicits.Syntax
open import Implicits.WellTyped
open import Implicits.Semantics.Type
open import Implicits.Semantics.Context
open import Implicits.Semantics.Lemmas
open import Implicits.Semantics.RewriteContext
open import SystemF.Everything as F using ()

module TermSemantics
  (_⊢ᵣ_ : ∀ {ν} → ICtx ν → Type ν → Set)
  (⟦_,_⟧r : ∀ {ν n} {K : Ktx ν n} {a} → (proj₂ K) ⊢ᵣ a → K# K →
            ∃ λ t → ⟦ proj₁ K ⟧ctx→ F.⊢ t ∈ ⟦ a ⟧tp→) where

  open TypingRules _⊢ᵣ_

  -- denotational semantics of well-typed terms
  ⟦_,_⟧ : ∀ {ν n} {K : Ktx ν n} {t} {a : Type ν} → K ⊢ t ∈ a → K# K → F.Term ν n
  ⟦_,_⟧ (var x) m = F.var x
  ⟦_,_⟧ (Λ t) m = F.Λ ⟦ t , #tvar m ⟧
  ⟦_,_⟧ (λ' a x) m = F.λ' ⟦ a ⟧tp→ ⟦ x , #var a m ⟧
  ⟦_,_⟧ (f [ b ]) m = F._[_] ⟦ f , m ⟧ ⟦ b ⟧tp→
  ⟦_,_⟧ (f · e) m = ⟦ f , m ⟧ F.· ⟦ e , m ⟧
  ⟦_,_⟧ (ρ {a = a} _ x) m = F.λ' ⟦ a ⟧tp→ ⟦ x , #ivar a m ⟧
  ⟦_,_⟧ (f ⟨ e∈Δ ⟩) m = ⟦ f , m ⟧ F.· (proj₁ ⟦ e∈Δ , m ⟧r)
  ⟦_,_⟧ (_with'_ r e) m = ⟦ r , m ⟧ F.· ⟦ e , m ⟧


{-# OPTIONS --without-K --safe #-}

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Substitution.Introductions.Prod {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.Untyped as U hiding (wk)
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.Typed.Weakening as T hiding (wk; wkTerm; wkEqTerm)
open import Definition.Typed.RedSteps
open import Definition.LogicalRelation
open import Definition.LogicalRelation.ShapeView
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Weakening
open import Definition.LogicalRelation.Properties
open import Definition.LogicalRelation.Application
open import Definition.LogicalRelation.Substitution
open import Definition.LogicalRelation.Substitution.Properties
open import Definition.LogicalRelation.Substitution.Reflexivity
open import Definition.LogicalRelation.Substitution.Introductions.Pi
open import Definition.LogicalRelation.Substitution.Introductions.SingleSubst

open import Tools.Product
import Tools.PropositionalEquality as PE

prod′ : ∀ {F G t u Γ l l′ l″}
       ([F] : Γ ⊩⟨ l ⟩ F)
       ([t] : Γ ⊩⟨ l ⟩ t ∷ F / [F])
       ([Gt] : Γ ⊩⟨ l″ ⟩ G [ t ])
       ([u] : Γ ⊩⟨ l″ ⟩ u ∷ G [ t ] / [Gt])
       ([ΣFG] : Γ ⊩⟨ l′ ⟩B⟨ BΣ ⟩ Σ F ▹ G)
     → Γ ⊩⟨ l′ ⟩ prod t u ∷ Σ F ▹ G / B-intr BΣ [ΣFG]
prod′ {F} {G} {t} {u} {Γ} {l} {l′} {l″} [F] [t] [Gt] [u]
      [ΣFG]@(noemb (Bᵣ F₁ G₁ D ⊢F ⊢G A≡A [F]₁ [G]₁ G-ext)) with
        B-PE-injectivity BΣ (whnfRed* (red D) Σₙ)
... | PE.refl , PE.refl =
  let ⊢t = escapeTerm [F] [t]
      ⊢u = escapeTerm [Gt] [u]
      ⊢Γ = wf ⊢F

      fst⇒t : Γ ⊢ fst (prod t u) ⇒ t ∷ F
      fst⇒t = Σ-β₁ ⊢F ⊢G ⊢t ⊢u
      [fstprod] , [fstprod≡t] = redSubstTerm fst⇒t [F] [t]
      [fstprod]′ = (irrelevanceTerm′ (PE.sym (wk-id F))
                                     [F] ([F]₁ id ⊢Γ)
                                     [fstprod])

      wkLiftIdEq = PE.cong (λ x → x [ fst (prod t u ) ]) (wk-lift-id G)
      [Gfst] = [G]₁ id ⊢Γ [fstprod]′
      [Gfst]′ = irrelevance′ wkLiftIdEq [Gfst]

      [t]′ = irrelevanceTerm′ (PE.sym (wk-id F))
                              [F] ([F]₁ id ⊢Γ)
                              [t]
      [fstprod≡t]′ = irrelevanceEqTerm′ (PE.sym (wk-id F))
                                        [F] ([F]₁ id ⊢Γ)
                                        [fstprod≡t]

      [Gfst≡Gt] = irrelevanceEq″ wkLiftIdEq (PE.cong (λ x → x [ t ]) (wk-lift-id G))
                                 [Gfst] [Gfst]′
                                 (G-ext id ⊢Γ [fstprod]′ [t]′ [fstprod≡t]′)
      [u]′ = convTerm₂ [Gfst]′ [Gt] [Gfst≡Gt] [u]

      snd⇒u : Γ ⊢ snd (prod t u) ⇒ u ∷ G [ fst (prod t u) ]
      snd⇒u = Σ-β₂ ⊢F ⊢G ⊢t ⊢u
      [sndprod] , [sndprod≡u] = redSubstTerm snd⇒u [Gfst]′ [u]′

      ⊢prod = prodⱼ ⊢F ⊢G ⊢t ⊢u

      [fstRefl] = transEqTerm [F] [fstprod≡t] (symEqTerm [F] [fstprod≡t])
      [sndRefl] = transEqTerm [Gfst]′ [sndprod≡u] (symEqTerm [Gfst]′ [sndprod≡u])
  in  Σₜ (prod t u)
         (idRedTerm:*: ⊢prod)
         prodₙ
         (≅-Σ-η ⊢F ⊢G ⊢prod ⊢prod prodₙ prodₙ
                (escapeTermEq [F] [fstRefl])
                (escapeTermEq [Gfst]′ [sndRefl]))
         [fstprod]′
         (irrelevanceTerm′ (PE.sym wkLiftIdEq)
                       [Gfst]′ [Gfst]
                       [sndprod])
prod′ {F} {G} {t} {u} {Γ} {l} {l′} [F] [t] [Gt] [u]
      [ΣFG]@(emb 0<1 x) = prod′ [F] [t] [Gt] [u] x

prod″ : ∀ {F G t u Γ l l′}
       ([F] : Γ ⊩⟨ l ⟩ F)
       ([t] : Γ ⊩⟨ l ⟩ t ∷ F / [F])
       ([Gt] : Γ ⊩⟨ l ⟩ G [ t ])
       ([u] : Γ ⊩⟨ l ⟩ u ∷ G [ t ] / [Gt])
       ([ΣFG] : Γ ⊩⟨ l′ ⟩ Σ F ▹ G)
     → Γ ⊩⟨ l′ ⟩ prod t u ∷ Σ F ▹ G / [ΣFG]
prod″ [F] [t] [Gt] [u] [ΣFG] =
      let [prod] = prod′ [F] [t] [Gt] [u] (B-elim BΣ [ΣFG])
      in  irrelevanceTerm (B-intr BΣ (B-elim BΣ [ΣFG])) [ΣFG] [prod]

prod-cong′ : ∀ {F G t t′ u u′ Γ l l′}
             ([F] : Γ ⊩⟨ l ⟩ F)
             ([t] : Γ ⊩⟨ l ⟩ t ∷ F / [F])
             ([t′] : Γ ⊩⟨ l ⟩ t′ ∷ F / [F])
             ([t≡t′] : Γ ⊩⟨ l ⟩ t ≡ t′ ∷ F / [F])
             ([Gt] : Γ ⊩⟨ l ⟩ G [ t ])
             ([u] : Γ ⊩⟨ l ⟩ u ∷ G [ t ] / [Gt])
             ([u′] : Γ ⊩⟨ l ⟩ u′ ∷ G [ t ] / [Gt])
             ([u≡u′] : Γ ⊩⟨ l ⟩ u ≡ u′ ∷ G [ t ] / [Gt])
             ([ΣFG] : Γ ⊩⟨ l′ ⟩B⟨ BΣ ⟩ Σ F ▹ G)
             → Γ ⊩⟨ l′ ⟩ prod t u ≡ prod t′ u′ ∷ Σ F ▹ G / B-intr BΣ [ΣFG]
prod-cong′ {F} {G} {t} {t′} {u} {u′} {Γ} {l} {l′}
           [F] [t] [t′] [t≡t′] [Gt] [u] [u′] [u≡u′]
           [ΣFG]@(noemb (Bᵣ F₁ G₁ D ⊢F ⊢G A≡A [F]₁ [G]₁ G-ext)) with
             B-PE-injectivity BΣ (whnfRed* (red D) Σₙ)
... | PE.refl , PE.refl =
  let [prod] = prod′ [F] [t] [Gt] [u] [ΣFG]

      ⊢Γ = wf ⊢F
      wk[F] = [F]₁ id ⊢Γ
      wk[t] = irrelevanceTerm′ (PE.sym (wk-id F)) [F] wk[F] [t]
      wk[t′] = irrelevanceTerm′ (PE.sym (wk-id F)) [F] wk[F] [t′]
      wk[t≡t′] = irrelevanceEqTerm′ (PE.sym (wk-id F)) [F] wk[F] [t≡t′]
      wk[Gt] = [G]₁ id ⊢Γ wk[t]
      wk[Gt′] = [G]₁ id ⊢Γ wk[t′]
      wk[Gt≡Gt′] = G-ext id ⊢Γ wk[t] wk[t′] wk[t≡t′]

      [Gt′] = irrelevance′ (PE.cong (λ x → x [ t′ ]) (wk-lift-id G)) wk[Gt′]
      [Gt≡Gt′] = irrelevanceEq″ (PE.cong (λ x → x [ t ]) (wk-lift-id G))
                                (PE.cong (λ x → x [ t′ ]) (wk-lift-id G))
                                wk[Gt] [Gt]
                                wk[Gt≡Gt′]

      [u′]Gt′ = convTerm₁ [Gt] [Gt′] [Gt≡Gt′] [u′]

      [prod′] = prod′ [F] [t′] [Gt′] [u′]Gt′ [ΣFG]

      ⊢t = escapeTerm [F] [t]
      ⊢t′ = escapeTerm [F] [t′]
      ⊢u = escapeTerm [Gt] [u]
      ⊢u′ = escapeTerm [Gt′] [u′]Gt′

      fst⇒t = Σ-β₁ ⊢F ⊢G ⊢t ⊢u
      [fst] , [fst≡t] = redSubstTerm fst⇒t [F] [t]
      fst⇒t′ = Σ-β₁ ⊢F ⊢G ⊢t′ ⊢u′
      [fst′] , [fst≡t′] = redSubstTerm fst⇒t′ [F] [t′]

      wk[fst≡t] = irrelevanceEqTerm′ (PE.sym (wk-id F))
                                      [F] wk[F]
                                      [fst≡t]
      wk[fst≡t′] = irrelevanceEqTerm′ (PE.sym (wk-id F))
                                      [F] wk[F]
                                      [fst≡t′]

      wk[fst] = irrelevanceTerm′ (PE.sym (wk-id F))
                                 [F] wk[F]
                                 [fst]
      wk[fst′] = irrelevanceTerm′ (PE.sym (wk-id F))
                                 [F] wk[F]
                                 [fst′]

      -- fst t ≡ fst t′ ∷ F
      [fst≡fst′] = transEqTerm [F] [fst≡t] (transEqTerm [F] [t≡t′] (symEqTerm [F] [fst≡t′]))
      wk[fst≡fst′] = irrelevanceEqTerm′ (PE.sym (wk-id F))
                                        [F] wk[F]
                                        [fst≡fst′]

      -- snd (prod t u) ≡ u ∷ G [ fst (prod t u) ]
      wkLiftIdEq = PE.cong (λ x → x [ fst (prod t u ) ]) (wk-lift-id G)
      wk[Gfst] = [G]₁ id ⊢Γ wk[fst]
      [Gfst] = irrelevance′ wkLiftIdEq wk[Gfst]
      [Gfst≡Gt] = irrelevanceEq″ wkLiftIdEq (PE.cong (λ x → x [ t ])
                                                     (wk-lift-id G))
                                 wk[Gfst] [Gfst]
                                 (G-ext id ⊢Γ wk[fst] wk[t] wk[fst≡t])

      [u]fst = convTerm₂ [Gfst] [Gt] [Gfst≡Gt] [u]
      snd⇒u = Σ-β₂ ⊢F ⊢G ⊢t ⊢u
      [snd] , [snd≡u] = redSubstTerm snd⇒u [Gfst] [u]fst

      -- u ≡ u′ ∷ G [ fst (prod t u) ]
      [u≡u′]Gfst = convEqTerm₂ [Gfst] [Gt] [Gfst≡Gt] [u≡u′]

      -- u′ ≡ snd (prod t′ u′) ∷ G [ fst (prod t u) ]
      wkLiftIdEq′ = PE.cong (λ x → x [ fst (prod t′ u′) ]) (wk-lift-id G)
      wk[Gfst′] = [G]₁ id ⊢Γ wk[fst′]
      [Gfst′] = irrelevance′ wkLiftIdEq′ wk[Gfst′]
      [Gfst′≡Gt′] = irrelevanceEq″ wkLiftIdEq′ (PE.cong (λ x → x [ t′ ])
                                                        (wk-lift-id G))
                                   wk[Gfst′] [Gfst′]
                                   (G-ext id ⊢Γ wk[fst′] wk[t′] wk[fst≡t′])

      snd⇒u′ = Σ-β₂ ⊢F ⊢G ⊢t′ ⊢u′

      [u′]Gfst′ = convTerm₂ [Gfst′] [Gt′] [Gfst′≡Gt′] [u′]Gt′
      [snd≡u′]Gfst′ = proj₂ (redSubstTerm snd⇒u′ [Gfst′] [u′]Gfst′)

      [Gfst≡Gfst′] = irrelevanceEq″ wkLiftIdEq wkLiftIdEq′
                                    wk[Gfst] [Gfst]
                                    (G-ext id ⊢Γ wk[fst] wk[fst′] wk[fst≡fst′])

      [snd≡u′]Gfst = convEqTerm₂ [Gfst] [Gfst′] [Gfst≡Gfst′] [snd≡u′]Gfst′

      [snd≡snd′] = transEqTerm [Gfst] [snd≡u]
                               (transEqTerm [Gfst] [u≡u′]Gfst
                                            (symEqTerm [Gfst] [snd≡u′]Gfst))
      wk[snd≡snd′] = irrelevanceEqTerm′ (PE.cong (λ x → x [ fst (prod t u) ])
                                                 (PE.sym (wk-lift-id G)))
                                        [Gfst] wk[Gfst]
                                        [snd≡snd′]

      ⊢prod = escapeTerm (B-intr BΣ [ΣFG]) [prod]
      ⊢prod′ = escapeTerm (B-intr BΣ [ΣFG]) [prod′]
  in Σₜ₌ (prod t u)
         (prod t′ u′)
         (idRedTerm:*: ⊢prod)
         (idRedTerm:*: ⊢prod′)
         prodₙ prodₙ
         (≅-Σ-η ⊢F ⊢G ⊢prod ⊢prod′ prodₙ prodₙ
                (escapeTermEq [F] [fst≡fst′])
                (escapeTermEq [Gfst] [snd≡snd′]))
         [prod] [prod′]
         wk[fst] wk[fst′]
         wk[fst≡fst′]
         wk[snd≡snd′]
prod-cong′ [F] [t] [t′] [t≡t′] [Gt] [u] [u′] [u≡u′] (emb 0<1 x) =
  prod-cong′ [F] [t] [t′] [t≡t′] [Gt] [u] [u′] [u≡u′] x

prod-cong″ : ∀ {F G t t′ u u′ Γ l l′}
             ([F] : Γ ⊩⟨ l ⟩ F)
             ([t] : Γ ⊩⟨ l ⟩ t ∷ F / [F])
             ([t′] : Γ ⊩⟨ l ⟩ t′ ∷ F / [F])
             ([t≡t′] : Γ ⊩⟨ l ⟩ t ≡ t′ ∷ F / [F])
             ([Gt] : Γ ⊩⟨ l ⟩ G [ t ])
             ([u] : Γ ⊩⟨ l ⟩ u ∷ G [ t ] / [Gt])
             ([u′] : Γ ⊩⟨ l ⟩ u′ ∷ G [ t ] / [Gt])
             ([u≡u′] : Γ ⊩⟨ l ⟩ u ≡ u′ ∷ G [ t ] / [Gt])
             ([ΣFG] : Γ ⊩⟨ l′ ⟩ Σ F ▹ G)
             → Γ ⊩⟨ l′ ⟩ prod t u ≡ prod t′ u′ ∷ Σ F ▹ G / [ΣFG]
prod-cong″ [F] [t] [t′] [t≡t′] [Gt] [u] [u′] [u≡u′] [ΣFG] =
  let [prod≡] = prod-cong′ [F] [t] [t′] [t≡t′] [Gt] [u] [u′] [u≡u′] (B-elim BΣ [ΣFG])
  in  irrelevanceEqTerm (B-intr BΣ (B-elim BΣ [ΣFG])) [ΣFG] [prod≡]

prod-congᵛ : ∀ {F G t t′ u u′ Γ l}
             ([Γ] : ⊩ᵛ Γ)
             ([F] : Γ ⊩ᵛ⟨ l ⟩ F / [Γ])
             ([G] : Γ ∙ F ⊩ᵛ⟨ l ⟩ G / [Γ] ∙ [F])
             ([t] : Γ ⊩ᵛ⟨ l ⟩ t ∷ F / [Γ] / [F])
             ([t′] : Γ ⊩ᵛ⟨ l ⟩ t′ ∷ F / [Γ] / [F])
             ([t≡t′] : Γ ⊩ᵛ⟨ l ⟩ t ≡ t′ ∷ F / [Γ] / [F])
             ([u] : Γ ⊩ᵛ⟨ l ⟩ u ∷ G [ t ] / [Γ] / substS {F} {G} [Γ] [F] [G] [t])
             ([u′] : Γ ⊩ᵛ⟨ l ⟩ u′ ∷ G [ t′ ] / [Γ] / substS {F} {G} [Γ] [F] [G] [t′])
             ([u≡u′] : Γ ⊩ᵛ⟨ l ⟩ u ≡ u′ ∷ G [ t ] / [Γ] / substS {F} {G} [Γ] [F] [G] [t])
             → Γ ⊩ᵛ⟨ l ⟩ prod t u ≡ prod t′ u′ ∷ Σ F ▹ G / [Γ] / Σᵛ {F} {G} [Γ] [F] [G]
prod-congᵛ {F} {G} {t} {t′} {u} {u′} {Γ} [Γ] [F] [G] [t] [t′] [t≡t′] [u] [u′] [u≡u′] {Δ} {σ} ⊢Δ [σ] =
  let ⊩σF = proj₁ ([F] ⊢Δ [σ])
      ⊩σt = proj₁ ([t] ⊢Δ [σ])
      ⊩σt′ = proj₁ ([t′] ⊢Δ [σ])
      σt≡σt′ = [t≡t′] ⊢Δ [σ]

      [Gt] = substS {F} {G} [Γ] [F] [G] [t]
      ⊩σGt₁ = proj₁ ([Gt] ⊢Δ [σ])
      ⊩σGt = irrelevance′ (singleSubstLift G t) ⊩σGt₁

      ⊩σu = irrelevanceTerm′ (singleSubstLift G t)
                             ⊩σGt₁ ⊩σGt
                             (proj₁ ([u] ⊢Δ [σ]))

      [Gt≡Gt′] = substSEq {F = F} {F′ = F} {G = G} {G′ = G} {t = t} {t′ = t′} [Γ]
                          [F] [F] (reflᵛ {A = F} [Γ] [F])
                          [G] [G] (reflᵛ {A = G} {Γ = Γ ∙ F} ([Γ] ∙ [F]) [G])
                          [t] [t′] [t≡t′]
      σGt≡σGt′ = [Gt≡Gt′] ⊢Δ [σ]

      ⊩σGt′ = proj₁ (substS {F} {G} [Γ] [F] [G] [t′] ⊢Δ [σ])
      ⊩σu′₂ = proj₁ ([u′] ⊢Δ [σ])
      ⊩σu′₁ = convTerm₂ ⊩σGt₁ ⊩σGt′ σGt≡σGt′ ⊩σu′₂
      ⊩σu′ = irrelevanceTerm′ (singleSubstLift G t) ⊩σGt₁ ⊩σGt ⊩σu′₁

      σu≡σu′ = irrelevanceEqTerm′ (singleSubstLift G t)
                                  ⊩σGt₁ ⊩σGt
                                  ([u≡u′] ⊢Δ [σ])

      ⊩σΣFG = proj₁ (Σᵛ {F} {G} [Γ] [F] [G] ⊢Δ [σ])
  in prod-cong″ ⊩σF ⊩σt ⊩σt′ σt≡σt′ ⊩σGt ⊩σu ⊩σu′ σu≡σu′ ⊩σΣFG

prodᵛ : ∀ {F G t u Γ l}
       ([Γ] : ⊩ᵛ Γ)
       ([F] : Γ ⊩ᵛ⟨ l ⟩ F / [Γ])
       ([G] : Γ ∙ F ⊩ᵛ⟨ l ⟩ G / [Γ] ∙ [F])
       ([t] : Γ ⊩ᵛ⟨ l ⟩ t ∷ F / [Γ] / [F])
       ([u] : Γ ⊩ᵛ⟨ l ⟩ u ∷ G [ t ] / [Γ] / substS {F} {G} [Γ] [F] [G] [t])
     → Γ ⊩ᵛ⟨ l ⟩ prod t u ∷ Σ F ▹ G / [Γ] / Σᵛ {F} {G} [Γ] [F] [G]
prodᵛ {F} {G} {t} {u} {Γ} {l} [Γ] [F] [G] [t] [u] {Δ = Δ} {σ = σ} ⊢Δ [σ] =
  let [Gt] = substS {F} {G} [Γ] [F] [G] [t]
      [ΣFG] = Σᵛ {F} {G} [Γ] [F] [G]

      ⊩σF = proj₁ ([F] ⊢Δ [σ])
      ⊢σF = escape ⊩σF
      ⊩σt = proj₁ ([t] ⊢Δ [σ])
      ⊩σGt′ : Δ ⊩⟨ l ⟩ subst σ (G [ t ])
      ⊩σGt′ = proj₁ ([Gt] ⊢Δ [σ])
      ⊩σGt : Δ ⊩⟨ l ⟩ subst (liftSubst σ) G [ subst σ t ]
      ⊩σGt = irrelevance′ (singleSubstLift G t) ⊩σGt′
      ⊩σu′ = proj₁ ([u] ⊢Δ [σ])
      ⊩σu : Δ ⊩⟨ l ⟩ subst σ u ∷ subst (liftSubst σ) G [ subst σ t ] / ⊩σGt
      ⊩σu = irrelevanceTerm′ (singleSubstLift G t) ⊩σGt′ ⊩σGt ⊩σu′
      ⊩σΣFG = proj₁ ([ΣFG] ⊢Δ [σ])

  in  prod″ ⊩σF ⊩σt ⊩σGt ⊩σu ⊩σΣFG ,
      (λ {σ′} [σ′] [σ≡σ′] →
        let ⊩σ′F = proj₁ ([F] ⊢Δ [σ′])
            σF≡σ′F = proj₂ ([F] ⊢Δ [σ]) [σ′] [σ≡σ′]

            ⊩σ′t = proj₁ ([t] ⊢Δ [σ′])
            ⊩σ′t = convTerm₂ ⊩σF ⊩σ′F σF≡σ′F ⊩σ′t
            σt≡σ′t = proj₂ ([t] ⊢Δ [σ]) [σ′] [σ≡σ′]

            ⊩σ′Gt′ = proj₁ ([Gt] ⊢Δ [σ′])
            ⊩σ′Gt = irrelevance′ (singleSubstLift G t) ⊩σ′Gt′

            σGt≡σ′Gt = proj₂ ([Gt] ⊢Δ [σ]) [σ′] [σ≡σ′]

            ⊩σ′u″ = proj₁ ([u] ⊢Δ [σ′])
            ⊩σ′u′ = convTerm₂ ⊩σGt′ ⊩σ′Gt′ σGt≡σ′Gt ⊩σ′u″
            ⊩σ′u = irrelevanceTerm′ (singleSubstLift G t) ⊩σGt′ ⊩σGt ⊩σ′u′

            σu≡σ′u : Δ ⊩⟨ l ⟩ subst σ u ≡ subst σ′ u ∷ subst (liftSubst σ) G [ subst σ t ] / ⊩σGt
            σu≡σ′u = irrelevanceEqTerm′ (singleSubstLift G t) ⊩σGt′ ⊩σGt (proj₂ ([u] ⊢Δ [σ]) [σ′] [σ≡σ′])
        in  prod-cong″ ⊩σF ⊩σt ⊩σ′t σt≡σ′t ⊩σGt ⊩σu ⊩σ′u σu≡σ′u ⊩σΣFG)


------------------------------------------------------------------------
-- Precedence-correct expressions
------------------------------------------------------------------------

module Mixfix.Expr where

open import Data.Vec  using (Vec)
open import Data.List using (List; []; _∷_)
open import Data.List.Membership.Propositional using (_∈_)
open import Data.List.Relation.Unary.Any using (here; there)
open import Data.Product using (∃; _,_)

open import Mixfix.Fixity
open import Mixfix.Operator

------------------------------------------------------------------------
-- An abstract definition of precedence graphs

-- The interface of precedence graphs.

record PrecedenceGraphInterface : Set₁ where
  field
    -- Precedence graphs.
    PrecedenceGraph : Set

    -- Precedence levels.
    Precedence : PrecedenceGraph → Set

    -- The operators of the given precedence.
    ops : (g : PrecedenceGraph) →
          Precedence g → (fix : Fixity) → List (∃ (Operator fix))

    -- The immediate successors of the precedence level.
    ↑ : (g : PrecedenceGraph) → Precedence g → List (Precedence g)

    -- All precedence levels in the graph.
    anyPrecedence : (g : PrecedenceGraph) → List (Precedence g)

-- When a precedence graph is given the following module may be
-- convenient to avoid having to write "g" all the time.

module PrecedenceGraph
         (i : PrecedenceGraphInterface)
         (g : PrecedenceGraphInterface.PrecedenceGraph i)
         where

  PrecedenceGraph : Set
  PrecedenceGraph = PrecedenceGraphInterface.PrecedenceGraph i

  Precedence : Set
  Precedence = PrecedenceGraphInterface.Precedence i g

  ops : Precedence → (fix : Fixity) → List (∃ (Operator fix))
  ops = PrecedenceGraphInterface.ops i g

  ↑ : Precedence → List Precedence
  ↑ = PrecedenceGraphInterface.↑ i g

  anyPrecedence : List Precedence
  anyPrecedence = PrecedenceGraphInterface.anyPrecedence i g

------------------------------------------------------------------------
-- Precedence-correct operator applications

-- Parameterised on a precedence graph.

module PrecedenceCorrect
         (i : PrecedenceGraphInterface)
         (g : PrecedenceGraphInterface.PrecedenceGraph i)
         where

  open PrecedenceGraph i g public

  mutual

    infixl 4 _⟨_⟩ˡ_
    infixr 4 _⟨_⟩ʳ_
    infix  4 _⟨_⟩_ _⟨_⟫ ⟪_⟩_ _∙_

    -- Expr ps contains expressions where the outermost operator has
    -- one of the precedences in ps.

    data Expr (ps : List Precedence) : Set where
      _∙_ : ∀ {p assoc} (p∈ps : p ∈ ps) (e : ExprIn p assoc) → Expr ps

    -- ExprIn p assoc contains expressions where the outermost
    -- operator has precedence p (is /in/ precedence level p) and the
    -- associativity assoc.

    data ExprIn (p : Precedence) : Associativity → Set where
      ⟪_⟫    :                    (op : Inner (ops p closed      ))                     → ExprIn p non
      _⟨_⟫   : (l : Outer p left) (op : Inner (ops p postfx      ))                     → ExprIn p left
      ⟪_⟩_   :                    (op : Inner (ops p prefx       )) (r : Outer p right) → ExprIn p right
      _⟨_⟩_  : (l : Expr (↑ p)  ) (op : Inner (ops p (infx non  ))) (r : Expr (↑ p)   ) → ExprIn p non
      _⟨_⟩ˡ_ : (l : Outer p left) (op : Inner (ops p (infx left ))) (r : Expr (↑ p)   ) → ExprIn p left
      _⟨_⟩ʳ_ : (l : Expr (↑ p)  ) (op : Inner (ops p (infx right))) (r : Outer p right) → ExprIn p right

    -- Outer p fix contains expressions where the head operator either
    --   ⑴ has precedence p and associativity assoc or
    --   ⑵ binds strictly tighter than p.

    data Outer (p : Precedence) (assoc : Associativity) : Set where
      similar : (e : ExprIn p assoc) → Outer p assoc
      tighter : (e : Expr (↑ p))     → Outer p assoc

    -- Inner ops contains the internal parts (operator plus
    -- internal arguments) of operator applications. The operators
    -- have to be members of ops.

    data Inner {fix} (ops : List (∃ (Operator fix))) : Set where
      _∙_ : ∀ {arity op}
            (op∈ops : (arity , op) ∈ ops)
            (args : Vec (Expr anyPrecedence) arity) →
            Inner ops

  -- "Weakening".

  weakenE : ∀ {p ps} → Expr ps → Expr (p ∷ ps)
  weakenE (p∈ps ∙ e) = there p∈ps ∙ e

  weakenI : ∀ {fix ops} {op : ∃ (Operator fix)} →
            Inner ops → Inner (op ∷ ops)
  weakenI (op∈ops ∙ args) = there op∈ops ∙ args


-- MIT License

-- Copyright (c) 2021 Luca Ciccone and Luca Padovani

-- Permission is hereby granted, free of charge, to any person
-- obtaining a copy of this software and associated documentation
-- files (the "Software"), to deal in the Software without
-- restriction, including without limitation the rights to use,
-- copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the
-- Software is furnished to do so, subject to the following
-- conditions:

-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-- OTHER DEALINGS IN THE SOFTWARE.

{-# OPTIONS --guardedness #-}

import Level

open import Data.Empty
open import Data.Product
open import Data.List using (_∷_; []; _++_)
open import Data.List.Properties using (∷-injectiveʳ)

open import Relation.Unary using (Pred; _⊆_; _∈_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; subst)

open import Common

module TraceSet {ℙ : Set} (message : Message ℙ)
  where

open import Trace message
open import SessionType message
open import Transitions message
open import HasTrace message

TraceSet : Set₁
TraceSet = Pred Trace Level.zero

⟦_⟧ : SessionType -> TraceSet
⟦_⟧ = _HasTrace_

_⊆⊇_ : TraceSet -> TraceSet -> Set
X ⊆⊇ Y = X ⊆ Y × Y ⊆ X

PrefixClosed : TraceSet -> Set
PrefixClosed X = ∀{φ ψ} -> ψ ⊑ φ -> X φ -> X ψ

Coherent : TraceSet -> Set
Coherent X = ∀{φ ψ₁ ψ₂ x y} -> X (φ ++ I x ∷ ψ₁) -> X (φ ++ O y ∷ ψ₂) -> ⊥

_∂_ : TraceSet -> Action -> TraceSet
(X ∂ α) φ = X (α ∷ φ)

--| CO-SETS |--

CoSet : TraceSet -> TraceSet
CoSet X φ = X (co-trace φ)

co-set-involution : ∀{X} -> CoSet (CoSet X) ≡ X
co-set-involution {X} =
  extensionality λ φ -> subst (λ z -> X z ≡ X φ) (sym (co-trace-involution φ)) refl

co-set-⊆ : ∀{X Y} -> X ⊆ Y -> CoSet X ⊆ CoSet Y
co-set-⊆ sub xcφ = sub xcφ

co-set-right->left : ∀{X Y} -> X ⊆ CoSet Y -> CoSet X ⊆ Y
co-set-right->left {X} {Y} sub xcφ with co-set-⊆ {X} {CoSet Y} sub
... | sub' = subst Y (co-trace-involution _) (sub' xcφ)

co-definition : ∀{X φ} -> φ ∈ X -> co-trace φ ∈ CoSet X
co-definition {_} {φ} xφ rewrite co-trace-involution φ = xφ

co-inversion : ∀{X φ} -> co-trace φ ∈ CoSet X -> φ ∈ X
co-inversion {_} {φ} coφ rewrite co-trace-involution φ = coφ

--| MAXIMAL TRACES |--

data Maximal : TraceSet -> Trace -> Set where
  maximal : ∀{X φ} (wit : φ ∈ X) (F : ∀{ψ} -> φ ⊑ ψ -> ψ ∈ X -> ψ ≡ φ) -> Maximal X φ

maximal-sub : ∀{X} -> Maximal X ⊆ X
maximal-sub (maximal x _) = x

derive-maximal : ∀{α φ X} -> α ∷ φ ∈ Maximal X -> φ ∈ Maximal (X ∂ α)
derive-maximal (maximal wit F) = maximal wit λ le p -> ∷-injectiveʳ (F (some le) p)

input-maximal : ∀{x φ f} -> I x ∷ φ ∈ Maximal ⟦ inp f ⟧ -> φ ∈ Maximal ⟦ f x .force ⟧
input-maximal (maximal (_ , def , step inp tr) F) =
  maximal (_ , def , tr) λ { le tψ -> ∷-injectiveʳ (F (some le) (inp-has-trace tψ)) }

output-maximal : ∀{x φ f} -> O x ∷ φ ∈ Maximal ⟦ out f ⟧ -> φ ∈ Maximal ⟦ f x .force ⟧
output-maximal (maximal (_ , def , step (out _) tr) F) =
  maximal (_ , def , tr) λ { le tψ -> ∷-injectiveʳ (F (some le) (out-has-trace tψ)) }


module PiQ.Properties where
open import Data.Empty
open import Data.Unit hiding (_≟_)
open import Data.Sum
open import Data.Product
open import Data.Maybe
open import Data.Maybe.Properties
open import Data.Nat hiding (_≟_)
open import Data.Nat.Induction
open import Data.Nat.Properties hiding (_≟_)
open import Function using (_∘_)
open import Relation.Binary.Core
open import Relation.Binary
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality
open import PiQ.Syntax
open import PiQ.Opsem
open import PiQ.AuxLemmas
open import PiQ.NoRepeat
open import PiQ.Invariants
open import PiQ.Eval
open import PiQ.Interp

-- Change the direction of the given state
flip : (st : State) → st ≢ ⊠ → State
flip ⟨ c ∣ v ∣ κ ⟩▷ _ = ⟨ c ∣ v ∣ κ ⟩◁
flip ［ c ∣ v ∣ κ ］▷ _ = ［ c ∣ v ∣ κ ］◁
flip ⟨ c ∣ v ∣ κ ⟩◁ _ = ⟨ c ∣ v ∣ κ ⟩▷
flip ［ c ∣ v ∣ κ ］◁ _ = ［ c ∣ v ∣ κ ］▷
flip ⊠ st≢⊠ = ⊥-elim (st≢⊠ refl)

Rev : ∀ {st st'} → st ↦ st' → (nf : st ≢ ⊠) (nf' : st' ≢ ⊠)
    → flip st' nf' ↦ flip st nf
Rev ↦⃗₁ _ _ = ↦⃖₁
Rev ↦⃗₂ _ _ = ↦⃖₂
Rev ↦⃗₃ _ _ = ↦⃖₃
Rev ↦⃗₄ _ _ = ↦⃖₄
Rev ↦⃗₅ _ _ = ↦⃖₅
Rev ↦⃗₆ _ _ = ↦⃖₆
Rev ↦⃗₇ _ _ = ↦⃖₇
Rev ↦⃗₈ _ _ = ↦⃖₈
Rev ↦⃗₉ _ _ = ↦⃖₉
Rev ↦⃗₁₀ _ _ = ↦⃖₁₀
Rev ↦⃗₁₁ _ _ = ↦⃖₁₁
Rev ↦⃗₁₂ _ _ = ↦⃖₁₂
Rev ↦⃖₁ _ _ = ↦⃗₁
Rev ↦⃖₂ _ _ = ↦⃗₂
Rev ↦⃖₃ _ _ = ↦⃗₃
Rev ↦⃖₄ _ _ = ↦⃗₄
Rev ↦⃖₅ _ _ = ↦⃗₅
Rev ↦⃖₆ _ _ = ↦⃗₆
Rev ↦⃖₇ _ _ = ↦⃗₇
Rev ↦⃖₈ _ _ = ↦⃗₈
Rev ↦⃖₉ _ _ = ↦⃗₉
Rev ↦⃖₁₀ _ _ = ↦⃗₁₀
Rev ↦⃖₁₁ _ _ = ↦⃗₁₁
Rev ↦⃖₁₂ _ _ = ↦⃗₁₂
Rev ↦η₊₁ _ _ = ↦η₊₂
Rev ↦η₊₂ _ _ = ↦η₊₁
Rev ↦ε₊₁ _ _ = ↦ε₊₂
Rev ↦ε₊₂ _ _ = ↦ε₊₁
Rev ↦⃗ηₓ _ _ = ↦⃖ηₓ₁ {eq = refl}
Rev (↦⃖ηₓ₁ {eq = refl}) _ _ = ↦⃗ηₓ
Rev ↦⃖ηₓ₂ _ nf' = ⊥-elim (nf' refl)
Rev (↦⃗εₓ₁ {eq = refl}) _ _ = ↦⃖εₓ
Rev ↦⃗εₓ₂ _ nf' = ⊥-elim (nf' refl)
Rev ↦⃖εₓ _ _ = ↦⃗εₓ₁ {eq = refl}

Rev* : ∀ {st st'} → st ↦* st' → (nf : st ≢ ⊠) → (nf' : st' ≢ ⊠) → flip st' nf' ↦* flip st nf
Rev* {⟨ _ ∣ _ ∣ _ ⟩▷} ◾ _ _ = ◾
Rev* {［ _ ∣ _ ∣ _ ］▷} ◾ _ _ = ◾
Rev* {⟨ _ ∣ _ ∣ _ ⟩◁} ◾ _ _ = ◾
Rev* {［ _ ∣ _ ∣ _ ］◁} ◾ _ _ = ◾
Rev* {⊠} ◾ nf _ = ⊥-elim (nf refl)
Rev* (r ∷ ◾) nf nf' = Rev r nf nf' ∷ ◾
Rev* (r ∷ (r' ∷ rs)) nf nf' = Rev* (r' ∷ rs) nf'' nf' ++↦ (Rev r nf nf'' ∷ ◾)
  where
  nf'' = λ eq → ⊠-is-stuck eq (_ , r')

-- Helper functions
inspect⊎ : ∀ {ℓ ℓ₁ ℓ₂ ℓ₃} {P : Set ℓ} {Q : Set ℓ₁} {R : Set ℓ₂} {S : Set ℓ₃}
         → (f : P → Q ⊎ R ⊎ S) → (p : P) → (∃[ q ] (inj₁ q ≡ f p)) ⊎ (∃[ r ] (inj₂ (inj₁ r) ≡ f p)) ⊎ (∃[ s ] (inj₂ (inj₂ s) ≡ f p))
inspect⊎ f p with f p
... | inj₁ q = inj₁ (q , refl)
... | inj₂ (inj₁ r) = inj₂ (inj₁ (r , refl))
... | inj₂ (inj₂ r) = inj₂ (inj₂ (r , refl))

Val≢ : ∀ {A B v₁ v₂} → _⃗ {A} {B} v₁ ≢ v₂ ⃖
Val≢ ()

Maybe≢ : ∀ {ℓ} {A : Set ℓ} {a : A} → just a ≢ nothing
Maybe≢ ()

toState : ∀ {A B} → (A ↔ B) → Maybe (Val B A) → State
toState c (just (b ⃗)) = ［ c ∣ b ∣ ☐ ］▷
toState c (just (a ⃖)) = ⟨ c ∣ a ∣ ☐ ⟩◁
toState c nothing = ⊠

is-stuck-toState : ∀ {A B} → (c : A ↔ B) → (v : Maybe (Val B A)) → is-stuck (toState c v)
is-stuck-toState c (just (b ⃗)) = λ ()
is-stuck-toState c (just (a ⃖)) = λ ()
is-stuck-toState c nothing = λ ()

toStateEq₁ : ∀ {A B b} → (c : A ↔ B) → (x : Maybe (Val B A)) → ［ c ∣ b ∣ ☐ ］▷ ≡ toState c x → just (b ⃗) ≡ x
toStateEq₁ c (just (x ⃗)) refl = refl

toStateEq₂ : ∀ {A B a} → (c : A ↔ B) → (x : Maybe (Val B A)) → ⟨ c ∣ a ∣ ☐ ⟩◁ ≡ toState c x → just (a ⃖) ≡ x
toStateEq₂ c (just (x ⃖)) refl = refl

toStateEq₃ : ∀ {A B} → (c : A ↔ B) → (x : Maybe (Val B A)) → ⊠ ≡ toState c x → nothing ≡ x
toStateEq₃ c nothing refl = refl
toStateEq₃ c (just (x ⃗)) ()
toStateEq₃ c (just (x ⃖)) ()

toState≡₁ : ∀ {A B b x} {c : A ↔ B} → toState c x ≡ ［ c ∣ b ∣ ☐ ］▷ → x ≡ just (b ⃗)
toState≡₁ {x = just (x ⃗)} refl = refl

toState≡₂ : ∀ {A B a x} {c : A ↔ B} → toState c x ≡ ⟨ c ∣ a ∣ ☐ ⟩◁ → x ≡ just (a ⃖)
toState≡₂ {x = just (x ⃖)} refl = refl

toState≡₃ : ∀ {A B x} {c : A ↔ B} → toState c x ≡ ⊠ → x ≡ nothing
toState≡₃ {x = nothing} refl = refl
toState≡₃ {x = just (x ⃗)} ()
toState≡₃ {x = just (x ⃖)} ()

eval-toState₁ : ∀ {A B a x} {c : A ↔ B} → ⟨ c ∣ a ∣ ☐ ⟩▷ ↦* (toState c x) → eval c (a ⃗) ≡ x
eval-toState₁ {a = a} {just (b ⃗)} {c} rs with inspect⊎ (run ⟨ c ∣ a ∣ ☐ ⟩▷) (λ ())
eval-toState₁ {a = a} {just (b ⃗)} {c} rs | inj₁ ((a' , rs') , eq) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₁ {a = a} {just (b ⃗)} {c} rs | inj₂ (inj₁ ((b' , rs') , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | refl = subst (λ x → [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (b ⃗)) eq refl
eval-toState₁ {a = a} {just (b ⃗)} {c} rs | inj₂ (inj₂ (rs' , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₁ {a = a} {just (a' ⃖)} {c} rs with inspect⊎ (run ⟨ c ∣ a ∣ ☐ ⟩▷) (λ ())
eval-toState₁ {a = a} {just (a' ⃖)} {c} rs | inj₁ ((a'' , rs') , eq) with deterministic* rs rs' (λ ()) (λ ())
... | refl = subst (λ x → [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (a' ⃖)) eq refl
eval-toState₁ {a = a} {just (a' ⃖)} {c} rs | inj₂ (inj₁ ((b' , rs') , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₁ {a = a} {just (a' ⃖)} {c} rs | inj₂ (inj₂ (rs' , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₁ {a = a} {nothing} {c} rs with inspect⊎ (run ⟨ c ∣ a ∣ ☐ ⟩▷) (λ ())
eval-toState₁ {a = a} {nothing} {c} rs | inj₁ ((a' , rs') , eq) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₁ {a = a} {nothing} {c} rs | inj₂ (inj₁ ((b' , rs') , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₁ {a = a} {nothing} {c} rs | inj₂ (inj₂ (rs' , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | refl = subst (λ x → [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ nothing) eq refl

eval-toState₂ : ∀ {A B b x} {c : A ↔ B} → ［ c ∣ b ∣ ☐ ］◁ ↦* (toState c x) → eval c (b ⃖) ≡ x
eval-toState₂ {b = b} {just (b' ⃗)} {c} rs with inspect⊎ (run ［ c ∣ b ∣ ☐ ］◁) (λ ())
eval-toState₂ {b = b} {just (b' ⃗)} {c} rs | inj₁ ((a' , rs') , eq) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₂ {b = b} {just (b' ⃗)} {c} rs | inj₂ (inj₁ ((b'' , rs') , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | refl = subst (λ x → [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (b' ⃗)) eq refl
eval-toState₂ {b = b} {just (b' ⃗)} {c} rs | inj₂ (inj₂ (rs' , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₂ {b = b} {just (a ⃖)} {c} rs with inspect⊎ (run ［ c ∣ b ∣ ☐ ］◁) (λ ())
eval-toState₂ {b = b} {just (a ⃖)} {c} rs | inj₁ ((a'' , rs') , eq) with deterministic* rs rs' (λ ()) (λ ())
... | refl = subst (λ x → [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (a ⃖)) eq refl
eval-toState₂ {b = b} {just (a ⃖)} {c} rs | inj₂ (inj₁ ((b' , rs') , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₂ {b = b} {just (a ⃖)} {c} rs | inj₂ (inj₂ (rs' , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₂ {b = b} {nothing} {c} rs with inspect⊎ (run ［ c ∣ b ∣ ☐ ］◁) (λ ())
eval-toState₂ {b = b} {nothing} {c} rs | inj₁ ((a' , rs') , eq) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₂ {b = b} {nothing} {c} rs | inj₂ (inj₁ ((b' , rs') , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | ()
eval-toState₂ {b = b} {nothing} {c} rs | inj₂ (inj₂ (rs' , eq)) with deterministic* rs rs' (λ ()) (λ ())
... | refl = subst (λ x → [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ nothing) eq refl

getₜᵣ⃗ : ∀ {A B} → (c : A ↔ B) → {v : ⟦ A ⟧} {v' : Maybe (Val B A)} → eval c (v ⃗) ≡ v'
       → ⟨ c ∣ v ∣ ☐ ⟩▷ ↦* toState c v'
getₜᵣ⃗ c {v} {v'} eq with inspect⊎ (run ⟨ c ∣ v ∣ ☐ ⟩▷) (λ ())
getₜᵣ⃗ c {v} {nothing} eq | inj₁ ((x₁ , rs₁) , eq₁) with trans (subst (λ x → just (x₁ ⃖) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()
getₜᵣ⃗ c {v} {just (x ⃗)} eq | inj₁ ((x₁ , rs₁) , eq₁) with trans (subst (λ x → just (x₁ ⃖) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()
getₜᵣ⃗ c {v} {just (x ⃖)} eq | inj₁ ((x₁ , rs₁) , eq₁) with trans (subst (λ x → just (x₁ ⃖) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | refl = rs₁
getₜᵣ⃗ c {v} {nothing} eq | inj₂ (inj₁ ((x₁ , rs₁) , eq₁)) with trans (subst (λ x → just (x₁ ⃗) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()
getₜᵣ⃗ c {v} {just (x ⃗)} eq | inj₂ (inj₁ ((x₁ , rs₁) , eq₁)) with trans (subst (λ x → just (x₁ ⃗) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | refl = rs₁
getₜᵣ⃗ c {v} {just (x ⃖)} eq | inj₂ (inj₁ ((x₁ , rs₁) , eq₁)) with trans (subst (λ x → just (x₁ ⃗) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()
getₜᵣ⃗ c {v} {nothing} eq | inj₂ (inj₂ (rs₁ , eq₁)) with trans (subst (λ x → nothing ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | refl = rs₁
getₜᵣ⃗ c {v} {just (x ⃗)} eq | inj₂ (inj₂ (rs₁ , eq₁)) with trans (subst (λ x → nothing ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()
getₜᵣ⃗ c {v} {just (x ⃖)} eq | inj₂ (inj₂ (rs₁ , eq₁)) with trans (subst (λ x → nothing ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()

getₜᵣ⃖ : ∀ {A B} → (c : A ↔ B) → {v : ⟦ B ⟧} {v' : Maybe (Val B A)} → eval c (v ⃖) ≡ v'
       → ［ c ∣ v ∣ ☐ ］◁ ↦* toState c v'
getₜᵣ⃖ c {v} {v'} eq with inspect⊎ (run ［ c ∣ v ∣ ☐ ］◁) (λ ())
getₜᵣ⃖ c {v} {nothing} eq | inj₁ ((x₁ , rs₁) , eq₁) with trans (subst (λ x → just (x₁ ⃖) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()
getₜᵣ⃖ c {v} {just (x ⃗)} eq | inj₁ ((x₁ , rs₁) , eq₁) with trans (subst (λ x → just (x₁ ⃖) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()
getₜᵣ⃖ c {v} {just (x ⃖)} eq | inj₁ ((x₁ , rs₁) , eq₁) with trans (subst (λ x → just (x₁ ⃖) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | refl = rs₁
getₜᵣ⃖ c {v} {nothing} eq | inj₂ (inj₁ ((x₁ , rs₁) , eq₁)) with trans (subst (λ x → just (x₁ ⃗) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()
getₜᵣ⃖ c {v} {just (x ⃗)} eq | inj₂ (inj₁ ((x₁ , rs₁) , eq₁)) with trans (subst (λ x → just (x₁ ⃗) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | refl = rs₁
getₜᵣ⃖ c {v} {just (x ⃖)} eq | inj₂ (inj₁ ((x₁ , rs₁) , eq₁)) with trans (subst (λ x → just (x₁ ⃗) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()
getₜᵣ⃖ c {v} {nothing} eq | inj₂ (inj₂ (rs₁ , eq₁)) with trans (subst (λ x → nothing ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | refl = rs₁
getₜᵣ⃖ c {v} {just (x ⃗)} eq | inj₂ (inj₂ (rs₁ , eq₁)) with trans (subst (λ x → nothing ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()
getₜᵣ⃖ c {v} {just (x ⃖)} eq | inj₂ (inj₂ (rs₁ , eq₁)) with trans (subst (λ x → nothing ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq₁ refl) eq
... | ()

-- Forward evaluator is reversible
evalIsRev : ∀ {A B} → (c : A ↔ B) (v₁ : Val A B) (v₂ : Val B A)
          → eval c v₁ ≡ (just v₂) → evalᵣₑᵥ c v₂ ≡ (just v₁)
evalIsRev c (v₁ ⃗) (v₂ ⃗) eq with inspect⊎ (run ⟨ c ∣ v₁ ∣ ☐ ⟩▷) (λ ())
evalIsRev c (v₁ ⃗) (v₂ ⃗) eq | inj₁ ((u , rs) , eq') = ⊥-elim (Val≢ (sym (just-injective contr)))
  where
  contr : just (u ⃖) ≡ just (v₂ ⃗)
  contr = trans (subst (λ x → just (u ⃖) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalIsRev c (v₁ ⃗) (v₂ ⃗) eq | inj₂ (inj₁ ((u , rs) , eq')) = subst (λ x → evalᵣₑᵥ c x ≡ just (v₁ ⃗)) u≡v₂ rsinv
  where
  u≡v₂ : (u ⃗) ≡ (v₂ ⃗)
  u≡v₂ = just-injective (trans (subst (λ x → just (u ⃗) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq)

  rsinv : evalᵣₑᵥ c (u ⃗) ≡ just (v₁ ⃗)
  rsinv with u≡v₂ | inspect⊎ (run ［ c ∣ v₂ ∣ ☐ ］◁) (λ ())
  rsinv | refl | inj₁ ((w , rs') , eq'') with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | refl = subst (λ x → [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (v₁ ⃗)) eq'' refl
  rsinv | refl | inj₂ (inj₁ ((w , rs') , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
  rsinv | refl | inj₂ (inj₂ (rs' , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
evalIsRev c (v₁ ⃗) (v₂ ⃗) eq | inj₂ (inj₂ (rs , eq')) = ⊥-elim (Maybe≢ (sym contr))
  where
  contr : nothing ≡ just (v₂ ⃗)
  contr = trans (subst (λ x → nothing ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalIsRev c (v₁ ⃗) (v₂ ⃖) eq with inspect⊎ (run ⟨ c ∣ v₁ ∣ ☐ ⟩▷) (λ ())
evalIsRev c (v₁ ⃗) (v₂ ⃖) eq | inj₁ ((u , rs) , eq') = subst (λ x → evalᵣₑᵥ c x ≡ just (v₁ ⃗)) u≡v₂ rsinv
  where
  u≡v₂ : (u ⃖) ≡ (v₂ ⃖)
  u≡v₂ = just-injective (trans (subst (λ x → just (u ⃖) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq)

  rsinv : evalᵣₑᵥ c (u ⃖) ≡ just (v₁ ⃗)
  rsinv with u≡v₂ | inspect⊎ (run ⟨ c ∣ u ∣ ☐ ⟩▷) (λ ())
  rsinv | refl | inj₁ ((w , rs') , eq'') with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | refl = subst (λ x → [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (v₁ ⃗)) eq'' refl
  rsinv | refl | inj₂ (inj₁ ((w , rs') , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
  rsinv | refl | inj₂ (inj₂ (rs' , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
evalIsRev c (v₁ ⃗) (v₂ ⃖) eq | inj₂ (inj₁ ((u , rs) , eq')) = ⊥-elim (Val≢ (just-injective contr))
  where
  contr : just (u ⃗) ≡ just (v₂ ⃖)
  contr = trans (subst (λ x → just (u ⃗) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalIsRev c (v₁ ⃗) (v₂ ⃖) eq | inj₂ (inj₂ (rs , eq')) = ⊥-elim (Maybe≢ (sym contr))
  where
  contr : nothing ≡ just (v₂ ⃖)
  contr = trans (subst (λ x → nothing ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalIsRev c (v₁ ⃖) (v₂ ⃗) eq with inspect⊎ (run ［ c ∣ v₁ ∣ ☐ ］◁) (λ ())
evalIsRev c (v₁ ⃖) (v₂ ⃗) eq | inj₁ ((u , rs) , eq') = ⊥-elim (Val≢ (just-injective (sym contr)))
  where
  contr : just (u ⃖) ≡ just (v₂ ⃗)
  contr = trans (subst (λ x → just (u ⃖) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalIsRev c (v₁ ⃖) (v₂ ⃗) eq | inj₂ (inj₁ ((u , rs) , eq')) = subst (λ x → evalᵣₑᵥ c x ≡ just (v₁ ⃖)) u≡v₂ rsinv
  where
  u≡v₂ : (u ⃗) ≡ (v₂ ⃗)
  u≡v₂ = just-injective (trans (subst (λ x → just (u ⃗) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq)

  rsinv : evalᵣₑᵥ c (u ⃗) ≡ just (v₁ ⃖)
  rsinv with u≡v₂ | inspect⊎ (run ［ c ∣ u ∣ ☐ ］◁) (λ ())
  rsinv | refl | inj₁ ((w , rs') , eq'') with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
  rsinv | refl | inj₂ (inj₁ ((w , rs') , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | refl = subst (λ x → [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (v₁ ⃖)) eq'' refl
  rsinv | refl | inj₂ (inj₂ (rs' , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
evalIsRev c (v₁ ⃖) (v₂ ⃗) eq | inj₂ (inj₂ (rs , eq')) = ⊥-elim (Maybe≢ (sym contr))
  where
  contr : nothing ≡ just (v₂ ⃗)
  contr = trans (subst (λ x → nothing ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalIsRev c (v₁ ⃖) (v₂ ⃖) eq with inspect⊎ (run ［ c ∣ v₁ ∣ ☐ ］◁) (λ ())
evalIsRev c (v₁ ⃖) (v₂ ⃖) eq | inj₁ ((u , rs) , eq') = subst (λ x → evalᵣₑᵥ c x ≡ just (v₁ ⃖)) u≡v₂ rsinv
  where
  u≡v₂ : (u ⃖) ≡ (v₂ ⃖)
  u≡v₂ = just-injective (trans (subst (λ x → just (u ⃖) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq)

  rsinv : evalᵣₑᵥ c (u ⃖) ≡ just (v₁ ⃖)
  rsinv with u≡v₂ | inspect⊎ (run ⟨ c ∣ u ∣ ☐ ⟩▷) (λ ())
  rsinv | refl | inj₁ ((w , rs') , eq'') with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
  rsinv | refl | inj₂ (inj₁ ((w , rs') , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | refl = subst (λ x → [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (v₁ ⃖)) eq'' refl
  rsinv | refl | inj₂ (inj₂ (rs' , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
evalIsRev c (v₁ ⃖) (v₂ ⃖) eq | inj₂ (inj₁ ((u , rs) , eq')) = ⊥-elim (Val≢ (just-injective contr))
  where
  contr : just (u ⃗) ≡ just (v₂ ⃖)
  contr = trans (subst (λ x → just (u ⃗) ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalIsRev c (v₁ ⃖) (v₂ ⃖) eq | inj₂ (inj₂ (rs , eq')) = ⊥-elim (Maybe≢ (sym contr))
  where
  contr : nothing ≡ just (v₂ ⃖)
  contr = trans (subst (λ x → nothing ≡ [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq

-- Backward evaluator is reversible
evalᵣₑᵥIsRev : ∀ {A B} → (c : A ↔ B) (v₁ : Val B A) (v₂ : Val A B)
             → evalᵣₑᵥ c v₁ ≡ (just v₂) → eval c v₂ ≡ (just v₁)
evalᵣₑᵥIsRev c (v₁ ⃗) (v₂ ⃗) eq with inspect⊎ (run ［ c ∣ v₁ ∣ ☐ ］◁) (λ ())
evalᵣₑᵥIsRev c (v₁ ⃗) (v₂ ⃗) eq | inj₁ ((u , rs) , eq') = subst (λ x → eval c x ≡ just (v₁ ⃗)) u≡v₂ rsinv
  where
  u≡v₂ : (u ⃗) ≡ (v₂ ⃗)
  u≡v₂ = just-injective (trans (subst (λ x → just (u ⃗) ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq)

  rsinv : eval c (u ⃗) ≡ just (v₁ ⃗)
  rsinv with inspect⊎ (run ⟨ c ∣ u ∣ ☐ ⟩▷) (λ ())
  rsinv | inj₁ ((w , rs') , eq'') with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
  rsinv | inj₂ (inj₁ ((w , rs') , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | refl = subst (λ x → [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (v₁ ⃗)) eq'' refl
  rsinv | inj₂ (inj₂ (rs' , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
evalᵣₑᵥIsRev c (v₁ ⃗) (v₂ ⃗) eq | inj₂ (inj₁ ((u , rs) , eq')) = ⊥-elim (Val≢ (sym (just-injective contr)))
  where
  contr : just (u ⃖) ≡ just (v₂ ⃗)
  contr = trans (subst (λ x → just (u ⃖) ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalᵣₑᵥIsRev c (v₁ ⃗) (v₂ ⃗) eq | inj₂ (inj₂ (rs' , eq')) = ⊥-elim (Maybe≢ (sym contr))
  where
  contr : nothing ≡ just (v₂ ⃗)
  contr = trans (subst (λ x → nothing ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalᵣₑᵥIsRev c (v₁ ⃗) (v₂ ⃖) eq with inspect⊎ (run ［ c ∣ v₁ ∣ ☐ ］◁) (λ ())
evalᵣₑᵥIsRev c (v₁ ⃗) (v₂ ⃖) eq | inj₁ ((u , rs) , eq') = ⊥-elim (Val≢ (just-injective contr))
  where
  contr : just (u ⃗) ≡ just (v₂ ⃖)
  contr = trans (subst (λ x → just (u ⃗) ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalᵣₑᵥIsRev c (v₁ ⃗) (v₂ ⃖) eq | inj₂ (inj₁ ((u , rs) , eq')) = subst (λ x → eval c x ≡ just (v₁ ⃗)) u≡v₂ rsinv
  where
  u≡v₂ : (u ⃖) ≡ (v₂ ⃖)
  u≡v₂ = just-injective (trans (subst (λ x → just (u ⃖) ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq)

  rsinv : eval c (u ⃖) ≡ just (v₁ ⃗)
  rsinv with inspect⊎ (run ［ c ∣ u ∣ ☐ ］◁) (λ ())
  rsinv | inj₁ ((w , rs') , eq'') with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
  rsinv | inj₂ (inj₁ ((w , rs') , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | refl = subst (λ x → [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (v₁ ⃗)) eq'' refl
  rsinv | inj₂ (inj₂ (rs' , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
evalᵣₑᵥIsRev c (v₁ ⃗) (v₂ ⃖) eq | inj₂ (inj₂ (rs' , eq')) = ⊥-elim (Maybe≢ (sym contr))
  where
  contr : nothing ≡ just (v₂ ⃖)
  contr = trans (subst (λ x → nothing ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalᵣₑᵥIsRev c (v₁ ⃖) (v₂ ⃗) eq with inspect⊎ (run ⟨ c ∣ v₁ ∣ ☐ ⟩▷) (λ ())
evalᵣₑᵥIsRev c (v₁ ⃖) (v₂ ⃗) eq | inj₁ ((u , rs) , eq') = subst (λ x → eval c x ≡ just (v₁ ⃖)) u≡v₂ rsinv
  where
  u≡v₂ : (u ⃗) ≡ (v₂ ⃗)
  u≡v₂ = just-injective (trans (subst (λ x → just (u ⃗) ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq)

  rsinv : eval c (u ⃗) ≡ just (v₁ ⃖)
  rsinv with inspect⊎ (run ⟨ c ∣ u ∣ ☐ ⟩▷) (λ ())
  rsinv | inj₁ ((w , rs') , eq'') with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | refl = subst (λ x → [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (v₁ ⃖)) eq'' refl
  rsinv | inj₂ (inj₁ ((w , rs') , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
  rsinv | inj₂ (inj₂ (rs' , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
evalᵣₑᵥIsRev c (v₁ ⃖) (v₂ ⃗) eq | inj₂ (inj₁ ((u , rs) , eq')) = ⊥-elim (Val≢ (sym (just-injective contr)))
  where
  contr : just (u ⃖) ≡ just (v₂ ⃗)
  contr = trans (subst (λ x → just (u ⃖) ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalᵣₑᵥIsRev c (v₁ ⃖) (v₂ ⃗) eq | inj₂ (inj₂ (rs' , eq')) = ⊥-elim (Maybe≢ (sym contr))
  where
  contr : nothing ≡ just (v₂ ⃗)
  contr = trans (subst (λ x → nothing ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalᵣₑᵥIsRev c (v₁ ⃖) (v₂ ⃖) eq with inspect⊎ (run ⟨ c ∣ v₁ ∣ ☐ ⟩▷) (λ ())
evalᵣₑᵥIsRev c (v₁ ⃖) (v₂ ⃖) eq | inj₁ ((u , rs) , eq') = ⊥-elim (Val≢ (just-injective contr))
  where
  contr : just (u ⃗) ≡ just (v₂ ⃖)
  contr = trans (subst (λ x → just (u ⃗) ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq
evalᵣₑᵥIsRev c (v₁ ⃖) (v₂ ⃖) eq | inj₂ (inj₁ ((u , rs) , eq')) = subst (λ x → eval c x ≡ just (v₁ ⃖)) u≡v₂ rsinv
  where
  u≡v₂ : (u ⃖) ≡ (v₂ ⃖)
  u≡v₂ = just-injective (trans (subst (λ x → just (u ⃖) ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq)

  rsinv : eval c (u ⃖) ≡ just (v₁ ⃖)
  rsinv with inspect⊎ (run ［ c ∣ u ∣ ☐ ］◁) (λ ())
  rsinv | inj₁ ((w , rs') , eq'') with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | refl = subst (λ x → [ just ∘ _⃖ ∘ proj₁ , [ just ∘ _⃗ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x ≡ just (v₁ ⃖)) eq'' refl
  rsinv | inj₂ (inj₁ ((w , rs') , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
  rsinv | inj₂ (inj₂ (rs' , eq'')) with deterministic* rs' (Rev* rs (λ ()) (λ ())) (λ ()) (λ ())
  ... | ()
evalᵣₑᵥIsRev c (v₁ ⃖) (v₂ ⃖) eq | inj₂ (inj₂ (rs' , eq')) = ⊥-elim (Maybe≢ (sym contr))
  where
  contr : nothing ≡ just (v₂ ⃖)
  contr = trans (subst (λ x → nothing ≡ [ just ∘ _⃗ ∘ proj₁ , [ just ∘ _⃖ ∘ proj₁ , (λ _ → nothing) ]′ ]′ x) eq' refl) eq

-- The abstract machine semantics is equivalent to the big-step semantics
mutual
  eval≡interp : ∀ {A B} → (c : A ↔ B) → (v : Val A B) → eval c v ≡ interp c v
  eval≡interp unite₊l (inj₂ v ⃗) = refl
  eval≡interp unite₊l (v ⃖) = refl
  eval≡interp uniti₊l (v ⃗) = refl
  eval≡interp uniti₊l (inj₂ v ⃖) = refl
  eval≡interp swap₊ (inj₁ x ⃗) = refl
  eval≡interp swap₊ (inj₂ y ⃗) = refl
  eval≡interp swap₊ (inj₁ x ⃖) = refl
  eval≡interp swap₊ (inj₂ y ⃖) = refl
  eval≡interp assocl₊ (inj₁ x ⃗) = refl
  eval≡interp assocl₊ (inj₂ (inj₁ y) ⃗) = refl
  eval≡interp assocl₊ (inj₂ (inj₂ z) ⃗) = refl
  eval≡interp assocl₊ (inj₁ (inj₁ x) ⃖) = refl
  eval≡interp assocl₊ (inj₁ (inj₂ y) ⃖) = refl
  eval≡interp assocl₊ (inj₂ z ⃖) = refl
  eval≡interp assocr₊ (inj₁ (inj₁ x) ⃗) = refl
  eval≡interp assocr₊ (inj₁ (inj₂ y) ⃗) = refl
  eval≡interp assocr₊ (inj₂ z ⃗) = refl
  eval≡interp assocr₊ (inj₁ x ⃖) = refl
  eval≡interp assocr₊ (inj₂ (inj₁ y) ⃖) = refl
  eval≡interp assocr₊ (inj₂ (inj₂ z) ⃖) = refl
  eval≡interp unite⋆l ((tt , v) ⃗) = refl
  eval≡interp unite⋆l (v ⃖) = refl
  eval≡interp uniti⋆l (v ⃗) = refl
  eval≡interp uniti⋆l ((tt , v) ⃖) = refl
  eval≡interp swap⋆ ((x , y) ⃗) = refl
  eval≡interp swap⋆ ((y , x) ⃖) = refl
  eval≡interp assocl⋆ ((x , (y , z)) ⃗) = refl
  eval≡interp assocl⋆ (((x , y) , z) ⃖) = refl
  eval≡interp assocr⋆ (((x , y) , z) ⃗) = refl
  eval≡interp assocr⋆ ((x , (y , z)) ⃖) = refl
  eval≡interp absorbr (() ⃗)
  eval≡interp absorbr (() ⃖)
  eval≡interp factorzl (() ⃗)
  eval≡interp factorzl (() ⃖)
  eval≡interp dist ((inj₁ x , z) ⃗) = refl
  eval≡interp dist ((inj₂ y , z) ⃗) = refl
  eval≡interp dist (inj₁ (x , z) ⃖) = refl
  eval≡interp dist (inj₂ (y , z) ⃖) = refl
  eval≡interp factor (inj₁ (x , z) ⃗) = refl
  eval≡interp factor (inj₂ (y , z) ⃗) = refl
  eval≡interp factor ((inj₁ x , z) ⃖) = refl
  eval≡interp factor ((inj₂ y , z) ⃖) = refl
  eval≡interp id↔ (v ⃗) = refl
  eval≡interp id↔ (v ⃖) = refl
  eval≡interp η₊ (inj₁ x ⃖) = refl
  eval≡interp η₊ (inj₂ (- x) ⃖) = refl
  eval≡interp ε₊ (inj₁ x ⃗) = refl
  eval≡interp ε₊ (inj₂ (- x) ⃗) = refl
  eval≡interp (ηₓ v) (tt ⃗) = refl
  eval≡interp (ηₓ v) ((v' , ↻) ⃖) with v ≟ v'
  ... | yes refl = refl
  ... | no  neq  = refl
  eval≡interp (εₓ v) ((v' , ↻) ⃗) with v ≟ v'
  ... | yes refl = refl
  ... | no  neq  = refl
  eval≡interp (εₓ v) (tt ⃖) = refl
  eval≡interp (c₁ ⨾ c₂) (a ⃗) with interp c₁ (a ⃗) | inspect (interp c₁) (a ⃗)
  eval≡interp (c₁ ⨾ c₂) (a ⃗) | just (b ⃗)  | [ eq₁ ] = lem
    where
    rs : ⟨ c₁ ⨾ c₂ ∣ a ∣ ☐ ⟩▷ ↦* ［ c₁ ∣ b ∣ ☐⨾ c₂ • ☐ ］▷
    rs = ↦⃗₃ ∷ appendκ↦* ((getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (a ⃗)) eq₁))) (λ ()) (λ ()) refl (☐⨾ c₂ • ☐)

    rs' : ［ c₁ ∣ b ∣ ☐⨾ c₂ • ☐ ］▷ ↦* (toState (c₁ ⨾ c₂) (eval (c₁ ⨾ c₂) (a ⃗)))
    rs' = proj₁ (deterministic*' rs (getₜᵣ⃗ (c₁ ⨾ c₂) refl) (is-stuck-toState _ _))

    lem : eval (c₁ ⨾ c₂) (a ⃗) ≡ (c₁ ⨾[ b ⃗]⨾ c₂)
    lem = proj₁ (loop (len↦ rs') b) rs' refl
  eval≡interp (c₁ ⨾ c₂) (a ⃗) | just (a₁ ⃖) | [ eq₁ ] = lem
    where
    rs : ⟨ c₁ ⨾ c₂ ∣ a ∣ ☐ ⟩▷ ↦* ⟨ c₁ ⨾ c₂ ∣ a₁ ∣ ☐ ⟩◁
    rs = ↦⃗₃ ∷ appendκ↦* ((getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (a ⃗)) eq₁))) (λ ()) (λ ()) refl (☐⨾ c₂ • ☐) ++↦ ↦⃖₃ ∷ ◾

    lem : eval (c₁ ⨾ c₂) (a ⃗) ≡ just (a₁ ⃖)
    lem = eval-toState₁ rs
  eval≡interp (c₁ ⨾ c₂) (a ⃗) | nothing     | [ eq₁ ] = lem
    where
    rs' : ⟨ c₁ ⨾ c₂ ∣ a ∣ ☐ ⟩▷ ↦* ⊠
    rs' = ↦⃗₃ ∷ appendκ↦*⊠ ((getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (a ⃗)) eq₁))) (λ ()) (☐⨾ c₂ • ☐)

    lem : eval (c₁ ⨾ c₂) (a ⃗) ≡ nothing
    lem = eval-toState₁ rs'
  eval≡interp (c₁ ⨾ c₂) (c ⃖) with interp c₂ (c ⃖) | inspect (interp c₂) (c ⃖)
  eval≡interp (c₁ ⨾ c₂) (c ⃖) | just (c' ⃗) | [ eq' ] = lem
    where
    rs' : ［ c₁ ⨾ c₂ ∣ c ∣ ☐ ］◁ ↦* ［ c₁ ⨾ c₂ ∣ c' ∣ ☐ ］▷
    rs' = ↦⃖₁₀ ∷ appendκ↦* ((getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (c ⃖)) eq'))) (λ ()) (λ ()) refl (c₁ ⨾☐• ☐) ++↦ ↦⃗₁₀ ∷ ◾

    lem : eval (c₁ ⨾ c₂) (c ⃖) ≡ just (c' ⃗)
    lem = eval-toState₂ rs'
  eval≡interp (c₁ ⨾ c₂) (c ⃖) | just (b ⃖)  | [ eq' ] = lem
    where
    rs : ［ c₁ ⨾ c₂ ∣ c ∣ ☐ ］◁ ↦* ⟨ c₂ ∣ b ∣ c₁ ⨾☐• ☐ ⟩◁
    rs = ↦⃖₁₀ ∷ appendκ↦* ((getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (c ⃖)) eq'))) (λ ()) (λ ()) refl (c₁ ⨾☐• ☐)

    rs' : ⟨ c₂ ∣ b ∣ c₁ ⨾☐• ☐ ⟩◁ ↦* (toState (c₁ ⨾ c₂) (eval (c₁ ⨾ c₂) (c ⃖)))
    rs' = proj₁ (deterministic*' rs (getₜᵣ⃖ (c₁ ⨾ c₂) refl) (is-stuck-toState _ _))

    lem : eval (c₁ ⨾ c₂) (c ⃖) ≡ (c₁ ⨾[ b ⃖]⨾ c₂)
    lem = proj₂ (loop (len↦ rs') b) rs' refl
  eval≡interp (c₁ ⨾ c₂) (c ⃖) | nothing     | [ eq' ] = lem
    where
    rs' : ［ c₁ ⨾ c₂ ∣ c ∣ ☐ ］◁ ↦* ⊠
    rs' = ↦⃖₁₀ ∷ appendκ↦*⊠ ((getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (c ⃖)) eq'))) (λ ()) (c₁ ⨾☐• ☐)

    lem : eval (c₁ ⨾ c₂) (c ⃖) ≡ nothing
    lem = eval-toState₂ rs'
  eval≡interp (c₁ ⊕ c₂) (inj₁ x ⃗) with interp c₁ (x ⃗) | inspect (interp c₁) (x ⃗)
  eval≡interp (c₁ ⊕ c₂) (inj₁ x ⃗) | just (x₁ ⃗) | [ eq₁ ] = eval-toState₁ rs'
    where
    rs' : ⟨ c₁ ⊕ c₂ ∣ inj₁ x ∣ ☐ ⟩▷ ↦* ［ c₁ ⊕ c₂ ∣ inj₁ x₁ ∣ ☐ ］▷
    rs' = ↦⃗₄ ∷ appendκ↦* (getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (x ⃗)) eq₁)) (λ ()) (λ ()) refl (☐⊕ c₂ • ☐) ++↦ ↦⃗₁₁ ∷ ◾
  eval≡interp (c₁ ⊕ c₂) (inj₁ x ⃗) | just (x₁ ⃖) | [ eq₁ ] = eval-toState₁ rs'
    where
    rs' : ⟨ c₁ ⊕ c₂ ∣ inj₁ x ∣ ☐ ⟩▷ ↦* ⟨ c₁ ⊕ c₂ ∣ inj₁ x₁ ∣ ☐ ⟩◁
    rs' = ↦⃗₄ ∷ appendκ↦* (getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (x ⃗)) eq₁)) (λ ()) (λ ()) refl (☐⊕ c₂ • ☐) ++↦ ↦⃖₄ ∷ ◾
  eval≡interp (c₁ ⊕ c₂) (inj₁ x ⃗) | nothing      | [ eq₁ ] = eval-toState₁ rs'
    where
    rs' : ⟨ c₁ ⊕ c₂ ∣ inj₁ x ∣ ☐ ⟩▷ ↦* ⊠
    rs' = ↦⃗₄ ∷ appendκ↦*⊠ (getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (x ⃗)) eq₁)) (λ ()) (☐⊕ c₂ • ☐)
  eval≡interp (c₁ ⊕ c₂) (inj₂ y ⃗) with interp c₂ (y ⃗) | inspect (interp c₂) (y ⃗)
  eval≡interp (c₁ ⊕ c₂) (inj₂ y ⃗) | just (y₁ ⃗) | [ eq₁ ] = eval-toState₁ rs'
    where
    rs' : ⟨ c₁ ⊕ c₂ ∣ inj₂ y ∣ ☐ ⟩▷ ↦* ［ c₁ ⊕ c₂ ∣ inj₂ y₁ ∣ ☐ ］▷
    rs' = ↦⃗₅ ∷ appendκ↦* (getₜᵣ⃗ c₂ (trans (eval≡interp c₂ (y ⃗)) eq₁)) (λ ()) (λ ()) refl (c₁ ⊕☐• ☐) ++↦ ↦⃗₁₂ ∷ ◾
  eval≡interp (c₁ ⊕ c₂) (inj₂ y ⃗) | just (y₁ ⃖) | [ eq₁ ] = eval-toState₁ rs'
    where
    rs' : ⟨ c₁ ⊕ c₂ ∣ inj₂ y ∣ ☐ ⟩▷ ↦* ⟨ c₁ ⊕ c₂ ∣ inj₂ y₁ ∣ ☐ ⟩◁
    rs' = ↦⃗₅ ∷ appendκ↦* (getₜᵣ⃗ c₂ (trans (eval≡interp c₂ (y ⃗)) eq₁)) (λ ()) (λ ()) refl (c₁ ⊕☐• ☐) ++↦ ↦⃖₅ ∷ ◾
  eval≡interp (c₁ ⊕ c₂) (inj₂ y ⃗) | nothing      | [ eq₁ ] = eval-toState₁ rs'
    where
    rs' : ⟨ c₁ ⊕ c₂ ∣ inj₂ y ∣ ☐ ⟩▷ ↦* ⊠
    rs' = ↦⃗₅ ∷ appendκ↦*⊠ (getₜᵣ⃗ c₂ (trans (eval≡interp c₂ (y ⃗)) eq₁)) (λ ()) (c₁ ⊕☐• ☐)
  eval≡interp (c₁ ⊕ c₂) (inj₁ x ⃖) with interp c₁ (x ⃖) | inspect (interp c₁) (x ⃖)
  eval≡interp (c₁ ⊕ c₂) (inj₁ x ⃖) | just (x₁ ⃗) | [ eq₁ ] = eval-toState₂ rs'
    where
    rs' : ［ c₁ ⊕ c₂ ∣ inj₁ x ∣ ☐ ］◁ ↦* ［ c₁ ⊕ c₂ ∣ inj₁ x₁ ∣ ☐ ］▷
    rs' = ↦⃖₁₁ ∷ appendκ↦* (getₜᵣ⃖ c₁ (trans (eval≡interp c₁ (x ⃖)) eq₁)) (λ ()) (λ ()) refl (☐⊕ c₂ • ☐) ++↦ ↦⃗₁₁ ∷ ◾
  eval≡interp (c₁ ⊕ c₂) (inj₁ x ⃖) | just (x₁ ⃖) | [ eq₁ ] = eval-toState₂ rs'
    where
    rs' : ［ c₁ ⊕ c₂ ∣ inj₁ x ∣ ☐ ］◁ ↦* ⟨ c₁ ⊕ c₂ ∣ inj₁ x₁ ∣ ☐ ⟩◁
    rs' = ↦⃖₁₁ ∷ appendκ↦* (getₜᵣ⃖ c₁ (trans (eval≡interp c₁ (x ⃖)) eq₁)) (λ ()) (λ ()) refl (☐⊕ c₂ • ☐) ++↦ ↦⃖₄ ∷ ◾
  eval≡interp (c₁ ⊕ c₂) (inj₁ x ⃖) | nothing     | [ eq₁ ] = eval-toState₂ rs'
    where
    rs' : ［ c₁ ⊕ c₂ ∣ inj₁ x ∣ ☐ ］◁ ↦* ⊠
    rs' = ↦⃖₁₁ ∷ appendκ↦*⊠ (getₜᵣ⃖ c₁ (trans (eval≡interp c₁ (x ⃖)) eq₁)) (λ ()) (☐⊕ c₂ • ☐)
  eval≡interp (c₁ ⊕ c₂) (inj₂ y ⃖) with interp c₂ (y ⃖) | inspect (interp c₂) (y ⃖)
  eval≡interp (c₁ ⊕ c₂) (inj₂ y ⃖) | just (y₁ ⃗) | [ eq₁ ] = eval-toState₂ rs'
    where
    rs' : ［ c₁ ⊕ c₂ ∣ inj₂ y ∣ ☐ ］◁ ↦* ［ c₁ ⊕ c₂ ∣ inj₂ y₁ ∣ ☐ ］▷
    rs' = ↦⃖₁₂ ∷ appendκ↦* (getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (y ⃖)) eq₁)) (λ ()) (λ ()) refl (c₁ ⊕☐• ☐) ++↦ ↦⃗₁₂ ∷ ◾
  eval≡interp (c₁ ⊕ c₂) (inj₂ y ⃖) | just (y₁ ⃖) | [ eq₁ ] = eval-toState₂ rs'
    where
    rs' : ［ c₁ ⊕ c₂ ∣ inj₂ y ∣ ☐ ］◁ ↦* ⟨ c₁ ⊕ c₂ ∣ inj₂ y₁ ∣ ☐ ⟩◁
    rs' = ↦⃖₁₂ ∷ appendκ↦* (getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (y ⃖)) eq₁)) (λ ()) (λ ()) refl (c₁ ⊕☐• ☐) ++↦ ↦⃖₅ ∷ ◾
  eval≡interp (c₁ ⊕ c₂) (inj₂ y ⃖) | nothing     | [ eq₁ ] = eval-toState₂ rs'
    where
    rs' : ［ c₁ ⊕ c₂ ∣ inj₂ y ∣ ☐ ］◁ ↦* ⊠
    rs' = ↦⃖₁₂ ∷ appendκ↦*⊠ (getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (y ⃖)) eq₁)) (λ ()) (c₁ ⊕☐• ☐)
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃗) with interp c₁ (x ⃗) | inspect (interp c₁) (x ⃗)
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃗) | just (x₁ ⃗) | [ eq₁ ] with interp c₂ (y ⃗) | inspect (interp c₂) (y ⃗)
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃗) | just (x₁ ⃗) | [ eq₁ ] | just (y₁ ⃗) | [ eq₂ ] = eval-toState₁ rs'
    where
    rs' : ⟨ c₁ ⊗ c₂ ∣ (x , y) ∣ ☐ ⟩▷ ↦* ［ c₁ ⊗ c₂ ∣ (x₁ , y₁) ∣ ☐ ］▷
    rs' = ↦⃗₆ ∷ appendκ↦* (getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (x ⃗)) eq₁)) (λ ()) (λ ()) refl (☐⊗[ c₂ , y ]• ☐) ++↦
          ↦⃗₈ ∷ appendκ↦* (getₜᵣ⃗ c₂ (trans (eval≡interp c₂ (y ⃗)) eq₂)) (λ ()) (λ ()) refl ([ c₁ , x₁ ]⊗☐• ☐) ++↦ ↦⃗₉ ∷ ◾
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃗) | just (x₁ ⃗) | [ eq₁ ] | just (y₁ ⃖) | [ eq₂ ] = eval-toState₁ rs'
    where
    rs' : ⟨ c₁ ⊗ c₂ ∣ (x , y) ∣ ☐ ⟩▷ ↦* ⟨ c₁ ⊗ c₂ ∣ (x , y₁) ∣ ☐ ⟩◁
    rs' = ↦⃗₆ ∷ appendκ↦* (getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (x ⃗)) eq₁)) (λ ()) (λ ()) refl (☐⊗[ c₂ , y ]• ☐) ++↦
          ↦⃗₈ ∷ appendκ↦* (getₜᵣ⃗ c₂ (trans (eval≡interp c₂ (y ⃗)) eq₂)) (λ ()) (λ ()) refl ([ c₁ , x₁ ]⊗☐• ☐) ++↦
          ↦⃖₈ ∷ Rev* (appendκ↦* (getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (x ⃗)) eq₁)) (λ ()) (λ ()) refl (☐⊗[ c₂ , y₁ ]• ☐)) (λ ()) (λ ()) ++↦ ↦⃖₆ ∷ ◾
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃗) | just (x₁ ⃗) | [ eq₁ ] | nothing     | [ eq₂ ] = eval-toState₁ rs'
    where
    rs' : ⟨ c₁ ⊗ c₂ ∣ (x , y) ∣ ☐ ⟩▷ ↦* ⊠
    rs' = ↦⃗₆ ∷ appendκ↦* (getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (x ⃗)) eq₁)) (λ ()) (λ ()) refl (☐⊗[ c₂ , y ]• ☐) ++↦
          ↦⃗₈ ∷ appendκ↦*⊠ (getₜᵣ⃗ c₂ (trans (eval≡interp c₂ (y ⃗)) eq₂)) (λ ()) ([ c₁ , x₁ ]⊗☐• ☐)
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃗) | just (x₁ ⃖) | [ eq₁ ] = eval-toState₁ rs'
    where
    rs' : ⟨ c₁ ⊗ c₂ ∣ (x , y) ∣ ☐ ⟩▷ ↦* ⟨ c₁ ⊗ c₂ ∣ (x₁ , y) ∣ ☐ ⟩◁
    rs' = ↦⃗₆ ∷ appendκ↦* (getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (x ⃗)) eq₁)) (λ ()) (λ ()) refl (☐⊗[ c₂ , y ]• ☐) ++↦ ↦⃖₆ ∷ ◾
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃗) | nothing     | [ eq₁ ] = eval-toState₁ rs'
    where
    rs' : ⟨ c₁ ⊗ c₂ ∣ (x , y) ∣ ☐ ⟩▷ ↦* ⊠
    rs' = ↦⃗₆ ∷ appendκ↦*⊠ (getₜᵣ⃗ c₁ (trans (eval≡interp c₁ (x ⃗)) eq₁)) (λ ()) (☐⊗[ c₂ , y ]• ☐)
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃖) with interp c₂ (y ⃖) | inspect (interp c₂) (y ⃖)
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃖) | just (y₁ ⃗) | [ eq₂ ] = eval-toState₂ rs'
    where
    rs' : ［ c₁ ⊗ c₂ ∣ (x , y) ∣ ☐ ］◁ ↦* ［ c₁ ⊗ c₂ ∣ (x , y₁) ∣ ☐ ］▷
    rs' = ↦⃖₉ ∷ appendκ↦* (getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (y ⃖)) eq₂)) (λ ()) (λ ()) refl ([ c₁ , x ]⊗☐• ☐) ++↦ ↦⃗₉ ∷ ◾
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃖) | just (y₁ ⃖) | [ eq₂ ] with interp c₁ (x ⃖) | inspect (interp c₁) (x ⃖)
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃖) | just (y₁ ⃖) | [ eq₂ ] | just (x₁ ⃗) | [ eq₁ ] = eval-toState₂ rs'
    where
    rs' : ［ c₁ ⊗ c₂ ∣ (x , y) ∣ ☐ ］◁ ↦* ［ c₁ ⊗ c₂ ∣ (x₁ , y) ∣ ☐ ］▷
    rs' = ↦⃖₉ ∷ appendκ↦* (getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (y ⃖)) eq₂)) (λ ()) (λ ()) refl ([ c₁ , x ]⊗☐• ☐) ++↦
          ↦⃖₈ ∷ appendκ↦* (getₜᵣ⃖ c₁ (trans (eval≡interp c₁ (x ⃖)) eq₁)) (λ ()) (λ ()) refl (☐⊗[ c₂ , y₁ ]• ☐) ++↦
          ↦⃗₈ ∷ Rev* (appendκ↦* (getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (y ⃖)) eq₂)) (λ ()) (λ ()) refl ([ c₁ , x₁ ]⊗☐• ☐)) (λ ()) (λ ()) ++↦ ↦⃗₉ ∷ ◾
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃖) | just (y₁ ⃖) | [ eq₂ ] | just (x₁ ⃖) | [ eq₁ ] = eval-toState₂ rs'
    where
    rs' : ［ c₁ ⊗ c₂ ∣ (x , y) ∣ ☐ ］◁ ↦* ⟨ c₁ ⊗ c₂ ∣ (x₁ , y₁) ∣ ☐ ⟩◁
    rs' = ↦⃖₉ ∷ appendκ↦* (getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (y ⃖)) eq₂)) (λ ()) (λ ()) refl ([ c₁ , x ]⊗☐• ☐) ++↦
          ↦⃖₈ ∷ appendκ↦* (getₜᵣ⃖ c₁ (trans (eval≡interp c₁ (x ⃖)) eq₁)) (λ ()) (λ ()) refl (☐⊗[ c₂ , y₁ ]• ☐) ++↦ ↦⃖₆ ∷ ◾
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃖) | just (y₁ ⃖) | [ eq₂ ] | nothing     | [ eq₁ ] = eval-toState₂ rs'
    where
    rs' : ［ c₁ ⊗ c₂ ∣ (x , y) ∣ ☐ ］◁ ↦* ⊠
    rs' = ↦⃖₉ ∷ appendκ↦* (getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (y ⃖)) eq₂)) (λ ()) (λ ()) refl ([ c₁ , x ]⊗☐• ☐) ++↦
          ↦⃖₈ ∷ appendκ↦*⊠ (getₜᵣ⃖ c₁ (trans (eval≡interp c₁ (x ⃖)) eq₁)) (λ ()) (☐⊗[ c₂ , y₁ ]• ☐)
  eval≡interp (c₁ ⊗ c₂) ((x , y) ⃖) | nothing     | [ eq₂ ] = eval-toState₂ rs'
    where
    rs' : ［ c₁ ⊗ c₂ ∣ (x , y) ∣ ☐ ］◁ ↦* ⊠
    rs' = ↦⃖₉ ∷ appendκ↦*⊠ (getₜᵣ⃖ c₂ (trans (eval≡interp c₂ (y ⃖)) eq₂)) (λ ()) ([ c₁ , x ]⊗☐• ☐)

  private
    loop : ∀ {A B C x} {c₁ : A ↔ B} {c₂ : B ↔ C} (n : ℕ)
         → ∀ b → ((rs : ［ c₁ ∣ b ∣ ☐⨾ c₂ • ☐ ］▷ ↦* toState (c₁ ⨾ c₂) x) → len↦ rs ≡ n → x ≡ c₁ ⨾[ b ⃗]⨾ c₂)
               × ((rs : ⟨ c₂ ∣ b ∣ c₁ ⨾☐• ☐ ⟩◁ ↦* toState (c₁ ⨾ c₂) x) → len↦ rs ≡ n → x ≡ c₁ ⨾[ b ⃖]⨾ c₂)
    loop {A} {B} {C} {x} {c₁} {c₂} = <′-rec (λ n → _) loop-rec
      where
      loop-rec : (n : ℕ) → (∀ m → m <′ n → _) → _
      loop-rec n R b = loop₁ , loop₂
        where
        loop₁ : (rs : ［ c₁ ∣ b ∣ ☐⨾ c₂ • ☐ ］▷ ↦* toState (c₁ ⨾ c₂) x) → len↦ rs ≡ n → x ≡ c₁ ⨾[ b ⃗]⨾ c₂
        loop₁ rs refl with interp c₂ (b ⃗) | inspect (interp c₂) (b ⃗)
        loop₁ rs refl | just (c ⃗)  | [ eq ] = toState≡₁ (deterministic* rs rs' (is-stuck-toState _ _) (λ ()))
          where
          rs' : ［ c₁ ∣ b ∣ ☐⨾ c₂ • ☐ ］▷ ↦* ［ c₁ ⨾ c₂ ∣ c ∣ ☐ ］▷
          rs' = ↦⃗₇ ∷ appendκ↦* (getₜᵣ⃗ c₂ (trans (eval≡interp c₂ (b ⃗)) eq)) (λ ()) (λ ()) refl (c₁ ⨾☐• ☐) ++↦ ↦⃗₁₀ ∷ ◾
        loop₁ rs refl | just (b' ⃖) | [ eq ] = proj₂ (R (len↦ rs'') le b') rs'' refl
          where
          rs' : ［ c₁ ∣ b ∣ ☐⨾ c₂ • ☐ ］▷ ↦* ⟨ c₂ ∣ b' ∣ c₁ ⨾☐• ☐ ⟩◁
          rs' = ↦⃗₇ ∷ appendκ↦* (getₜᵣ⃗ c₂ (trans (eval≡interp c₂ (b ⃗)) eq)) (λ ()) (λ ()) refl (c₁ ⨾☐• ☐)

          rs'' : ⟨ c₂ ∣ b' ∣ c₁ ⨾☐• ☐ ⟩◁ ↦* toState (c₁ ⨾ c₂) x
          rs'' = proj₁ (deterministic*' rs' rs (is-stuck-toState _ _))

          req : len↦ rs ≡ len↦ rs' + len↦ rs''
          req = proj₂ (deterministic*' rs' rs (is-stuck-toState _ _))

          le : len↦ rs'' <′ len↦ rs
          le rewrite req = s≤′s (n≤′m+n _ _)
        loop₁ rs refl | nothing     | [ eq ] = toState≡₃ (deterministic* rs rs' (is-stuck-toState _ _) (λ ()))
          where
          rs' : ［ c₁ ∣ b ∣ ☐⨾ c₂ • ☐ ］▷ ↦* ⊠
          rs' = ↦⃗₇ ∷ appendκ↦*⊠ (getₜᵣ⃗ c₂ (trans (eval≡interp c₂ (b ⃗)) eq)) (λ ()) (c₁ ⨾☐• ☐)
        loop₂ : (rs : ⟨ c₂ ∣ b ∣ c₁ ⨾☐• ☐ ⟩◁ ↦* toState (c₁ ⨾ c₂) x) → len↦ rs ≡ n → x ≡ c₁ ⨾[ b ⃖]⨾ c₂
        loop₂ rs refl with interp c₁ (b ⃖) | inspect (interp c₁) (b ⃖)
        loop₂ rs refl | just (b' ⃗) | [ eq ] = proj₁ (R (len↦ rs'') le b') rs'' refl
          where
          rs' : ⟨ c₂ ∣ b ∣ c₁ ⨾☐• ☐ ⟩◁ ↦* ［ c₁ ∣ b' ∣ ☐⨾ c₂ • ☐ ］▷
          rs' = ↦⃖₇ ∷ appendκ↦* (getₜᵣ⃖ c₁ (trans (eval≡interp c₁ (b ⃖)) eq)) (λ ()) (λ ()) refl (☐⨾ c₂ • ☐)

          rs'' : ［ c₁ ∣ b' ∣ ☐⨾ c₂ • ☐ ］▷ ↦* toState (c₁ ⨾ c₂) x
          rs'' = proj₁ (deterministic*' rs' rs (is-stuck-toState _ _))

          req : len↦ rs ≡ len↦ rs' + len↦ rs''
          req = proj₂ (deterministic*' rs' rs (is-stuck-toState _ _))

          le : len↦ rs'' <′ len↦ rs
          le rewrite req = s≤′s (n≤′m+n _ _)
        loop₂ rs refl | just (a ⃖)  | [ eq ] = toState≡₂ (deterministic* rs rs' (is-stuck-toState _ _) (λ ()))
          where
          rs' : ⟨ c₂ ∣ b ∣ c₁ ⨾☐• ☐ ⟩◁ ↦* ⟨ c₁ ⨾ c₂ ∣ a ∣ ☐ ⟩◁
          rs' = ↦⃖₇ ∷ appendκ↦* (getₜᵣ⃖ c₁ (trans (eval≡interp c₁ (b ⃖)) eq)) (λ ()) (λ ()) refl (☐⨾ c₂ • ☐) ++↦ ↦⃖₃ ∷ ◾
        loop₂ rs refl | nothing     | [ eq ] = toState≡₃ (deterministic* rs rs' (is-stuck-toState _ _) (λ ()))
          where
          rs' : ⟨ c₂ ∣ b ∣ c₁ ⨾☐• ☐ ⟩◁ ↦* ⊠
          rs' = ↦⃖₇ ∷ appendκ↦*⊠ (getₜᵣ⃖ c₁ (trans (eval≡interp c₁ (b ⃖)) eq)) (λ ()) (☐⨾ c₂ • ☐)


open import Data.Empty using (⊥-elim)
open import Data.Fin using (Fin; _≟_)
open import Data.Nat using (ℕ)
open import Data.Vec using (lookup; _[_]≔_)
open import Data.Vec.Properties using (lookup∘update; lookup∘update′; []≔-commutes; []≔-idempotent; []≔-lookup)
open import Relation.Nullary using (yes; no; ¬_)
open import Relation.Binary.PropositionalEquality using (sym; trans; _≡_; refl; cong; _≢_; module ≡-Reasoning)
open import Data.Product using (∃-syntax; _,_; proj₁; proj₂; _×_)
open ≡-Reasoning

open import Common
open import Global
open import Local
open import Projection

soundness :
  ∀ { n : ℕ } { act : Action n } { c g g′ }
  -> g ↔ c
  -> g - act →g g′
  -> ∃[ c′ ] c - act →c c′ × g′ ↔ c′
soundness
  {n = n}
  {act = act@(action .p .q p≢q .l)}
  {c = c}
  {g = g@(msgSingle p q p≢q-gt l g′)}
  {g′ = .g′}
  assoc
  →g-prefix
  = c′ , (→c-comm c p≢q refl refl refl lpReduce lqReduce , g′↔c′)
  where
    config-without-prefix = config-gt-remove-prefix g c assoc refl
    c′ = proj₁ config-without-prefix
    g′↔c′ : g′ ↔ c′
    g′↔c′ = proj₂ (proj₂ config-without-prefix)
    lpReduce : (p , lookup c p) - act →l (p , project g′ p)
    lpReduce rewrite _↔_.isProj assoc p
      = →l-send p (proj-prefix-send p q g′ p≢q-gt) p≢q
    lqReduce : (q , lookup c q) - act →l (q , project g′ q)
    lqReduce rewrite _↔_.isProj assoc q
      = →l-recv q (proj-prefix-recv p q g′ p≢q-gt) p≢q
soundness
  {n = n}
  {act = act@(.action p q p≢q l)}
  {c = c}
  {g = g@(msgSingle r s r≢s l′ gSub)}
  {g′ = g′@(.msgSingle r s r≢s l′ gSub′)}
  assoc
  (→g-cont gReduce p≢r q≢r p≢s q≢s)
  = c′ , cReduce , assoc′
  where
    config-without-prefix = config-gt-remove-prefix g c assoc refl
    cSub = proj₁ config-without-prefix
    gSub↔cSub : gSub ↔ cSub
    gSub↔cSub = proj₂ (proj₂ config-without-prefix)
    soundness-gSub : ∃[ cSub′ ] cSub - act →c cSub′ × gSub′ ↔ cSub′
    soundness-gSub = soundness gSub↔cSub gReduce
    c′ : Configuration n
    c′ with soundness-gSub
    ... | cSub′ , _ , _ = (cSub′ [ r ]≔ lr′) [ s ]≔ ls′
      where
        lr′ : Local n
        lr′ with soundness-gSub
        ...   | cSub′ , _ , _ = sendSingle s l′ (lookup cSub′ r)
        ls′ : Local n
        ls′ with soundness-gSub
        ...   | cSub′ , _ , _ = recvSingle r l′ (lookup cSub′ s)
    isProj-g′ : ∀(t : Fin n) -> lookup c′ t ≡ project g′ t
    isProj-g′ t with soundness-gSub
    ...   | cSub′ , _ , gSub′↔cSub′
        with r ≟ t   | s ≟ t
    ...   | yes r≡t  | no _
        rewrite sym r≡t
        rewrite lookup∘update′ r≢s (cSub′ [ r ]≔ sendSingle s l′ (lookup cSub′ r)) (recvSingle r l′ (lookup cSub′ s))
        rewrite lookup∘update r cSub′ (sendSingle s l′ (lookup cSub′ r))
        rewrite _↔_.isProj gSub′↔cSub′ r = refl
    ...   | no _     | yes s≡t
        rewrite sym s≡t
        rewrite lookup∘update s (cSub′ [ r ]≔ sendSingle s l′ (lookup cSub′ r)) (recvSingle r l′ (lookup cSub′ s))
        rewrite _↔_.isProj gSub′↔cSub′ s = refl
    ...   | no r≢t   | no s≢t
        rewrite lookup∘update′ (¬≡-flip s≢t) (cSub′ [ r ]≔ sendSingle s l′ (lookup cSub′ r)) (recvSingle r l′ (lookup cSub′ s))
        rewrite lookup∘update′ (¬≡-flip r≢t) cSub′ (sendSingle s l′ (lookup cSub′ r))
        rewrite _↔_.isProj gSub′↔cSub′ t = refl
    ...   | yes refl | yes refl = ⊥-elim (r≢s refl)
    assoc′ : g′ ↔ c′
    assoc′ = record { isProj = isProj-g′ }
    cReduce : c - act →c c′
    cReduce with soundness-gSub
    ...   | cSub′ , →c-comm {lp = lp} {lp′ = lp′} {lq = lq} {lq′ = lq′} .cSub .p≢q refl refl refl lpReduce lqReduce , gSub′↔cSub′
            = →c-comm c p≢q lp≡c[p] lq≡c[q] c→c′ lpReduce lqReduce
      where
        lr′ = sendSingle s l′ (lookup cSub′ r)
        ls′ = recvSingle r l′ (lookup cSub′ s)
        lp≡c[p] : lp ≡ lookup c p
        lp≡c[p]
          rewrite lookup∘update′ p≢s (c [ r ]≔ project gSub r) (project gSub s)
          rewrite lookup∘update′ p≢r c (project gSub r) = refl
        lq≡c[q] : lq ≡ lookup c q
        lq≡c[q]
          rewrite lookup∘update′ q≢s (c [ r ]≔ project gSub r) (project gSub s)
          rewrite lookup∘update′ q≢r c (project gSub r) = refl
        lr′≡c[r] : lr′ ≡ lookup c r
        lr′≡c[r]
          rewrite lookup∘update′ (¬≡-flip q≢r) (cSub [ p ]≔ lp′) lq′
          rewrite lookup∘update′ (¬≡-flip p≢r) cSub lp′
          rewrite _↔_.isProj assoc r
          rewrite proj-prefix-send {l = l′} r s gSub r≢s
          rewrite lookup∘update′ r≢s (c [ r ]≔ project gSub r) (project gSub s)
          rewrite lookup∘update r c (project gSub r)
          = refl
        ls′≡c[s] : ls′ ≡ lookup c s
        ls′≡c[s]
          rewrite lookup∘update′ (¬≡-flip q≢s) (cSub [ p ]≔ lp′) lq′
          rewrite lookup∘update′ (¬≡-flip p≢s) cSub lp′
          rewrite _↔_.isProj assoc s
          rewrite proj-prefix-recv {l = l′} r s gSub r≢s
          rewrite lookup∘update s (c [ r ]≔ project gSub r) (project gSub s)
          = refl
        c→c′ : (cSub′ [ r ]≔ lr′) [ s ]≔ ls′ ≡ (c [ p ]≔ lp′) [ q ]≔ lq′
        c→c′
          rewrite []≔-commutes ((((c [ r ]≔ project gSub r) [ s ]≔ project gSub s) [ p ]≔ lp′) [ q ]≔ lq′) r s {lr′} {ls′} r≢s
          rewrite []≔-commutes (((c [ r ]≔ project gSub r) [ s ]≔ project gSub s) [ p ]≔ lp′) q s {lq′} {ls′} q≢s
          rewrite []≔-commutes ((c [ r ]≔ project gSub r) [ s ]≔ project gSub s) p s {lp′} {ls′} p≢s
          rewrite []≔-idempotent (c [ r ]≔ project gSub r) s {project gSub s} {ls′}
          rewrite []≔-commutes (((c [ r ]≔ project gSub r) [ s ]≔ ls′) [ p ]≔ lp′) q r {lq′} {lr′} q≢r
          rewrite []≔-commutes ((c [ r ]≔ project gSub r) [ s ]≔ ls′) p r {lp′} {lr′} p≢r
          rewrite []≔-commutes (c [ r ]≔ project gSub r) s r {ls′} {lr′} (¬≡-flip r≢s)
          rewrite []≔-idempotent c r {project gSub r} {lr′}
          rewrite lr′≡c[r]
          rewrite ls′≡c[s]
          rewrite []≔-lookup c r
          rewrite []≔-lookup c s
          = refl


open import Prelude hiding (begin_; step-≡; _∎)

module MJ.Classtable.Core (c : ℕ) where

open import Data.Product.Relation.Binary.Pointwise.NonDependent using (_×-≟_)
open import Data.List
open import Data.List.Membership.Propositional
open import Data.List.Relation.Binary.Pointwise using (decidable-≡)
open import Relation.Binary
open import Data.Vec
open import Data.Maybe
open import Data.String

open import Relation.Binary using (Decidable)

open import MJ.Types as Types

{-
To model that a class may define both field and methods in a uniform manner, we
choose to introduce two namespaces.
-}
data NS : Set where
  METHOD : NS
  FIELD  : NS

typing : NS → Set
typing METHOD = Sig c
typing FIELD = Ty c

-- decidable equality on typings
_typing-≟_ : ∀ {ns} → Decidable (_≡_ {A = typing ns})
_typing-≟_ {METHOD} = (decidable-≡ Types._≟_) ×-≟ Types._≟_
_typing-≟_ {FIELD}  = Types._≟_

{-
We choose to make parent declarations obligatory and have a distinguished class
identifier Object in which inheritance chains find their foundation.

The field constr of this record provides access to the constructor parameter types.

The field decls returns a list of named members for each namespace, where the
type of the member is namespace-dependent. The uniformity of member definitions
permits a notion of membership that works for both fields and methods:
-}
record Class : Set where
  constructor class
  field
    parent  : Cid c
    constr  : List (Ty c) -- argument types
    decls   : (ns : NS) → List (String × typing ns)

ObjectClass = class Object [] (λ _ → [])

{-
Conceptually a class table stores the types of all members of all classes.
It is modeled by a total function from class identifiers to instances of
a record Class.
-}
PreClasstable = Cid c → Class

{-
We can define inheritance as the transitive closure of a step relation
between class identifiers under a given classtable Σ:
-}
data _⊢_<:ₛ_ (Σ : PreClasstable) : Cid c → Cid c → Set where
  super : ∀ {cid} → Σ ⊢ cls cid <:ₛ (Class.parent (Σ (cls cid)))

open import Data.Star
_⊢_<:_ : (Σ : PreClasstable)(cid : Cid c) → (pid : Cid c) → Set
_⊢_<:_ Σ = Star (_⊢_<:ₛ_ Σ)

{-
We'll restrict classtables to those that satisfy three properties:

- founded: says that Object is at the base of sub-typing
- rooted: says that every class inherits from Object
- Σ-object: gives meaning to the distinguished Object class identifier
-}
record Classtable : Set where
  field
    Σ        : PreClasstable
    founded  : Class.parent (Σ Object) ≡ Object
    rooted   : ∀ C → Σ ⊢ C <: Object
    Σ-Object : Σ Object ≡ ObjectClass

  _<∶_ : ∀ (cid pid : Cid c) → Set
  a <∶ b = Σ ⊢ a <: b

  open import Relation.Binary
  open import Data.Nat.Properties

  {-
  We can show that the subtyping relation is indeed reflexive and transitive
  -}
  <:-reflexive  : Reflexive (_⊢_<:_ Σ)
  <:-reflexive  = ε

  <:-transitive : Transitive (_⊢_<:_ Σ)
  <:-transitive = _◅◅_

  {-
  From the axioms of the classtable we can derive the important properties
  that cyclic inheritance is impossible and that consequently inheritance
  proofs are unique.

  The proof is slightly involved because we have to make sure that Agda
  believes that it is total.
  -}
  private
    len : ∀ {c p} → Σ ⊢ c <: p → ℕ
    len ε = 0
    len (x ◅ px) = suc $ len px

    len-◅◅ : ∀ {c p p'}(s : Σ ⊢ c <: p)(s' : Σ ⊢ p <: p') → len (s ◅◅ s') ≡ len s + len s'
    len-◅◅ ε _ = refl
    len-◅◅ (x ◅ s) s' rewrite len-◅◅ s s' = refl

    lem-len : ∀ {c p}(px : Σ ⊢ c <: p)(qx : Σ ⊢ c <: Object) → len px ≤ len qx
    lem-len ε ε = z≤n
    lem-len ε (x ◅ qx) = z≤n
    lem-len (() ◅ px) ε
    lem-len (super ◅ px) (super ◅ qx) = s≤s (lem-len px qx)

    open ≤-Reasoning

    -- generate an inheritance proof of arbitrary length,
    -- given an inheritance proof from a parent to a child
    lem-inf : ∀ c → Σ ⊢ Class.parent (Σ (cls c)) <: (cls c) → ∀ n → ∃ λ (px : Σ ⊢ (cls c) <: (cls c)) → len px > n
    lem-inf c px n with helper px n
      where
        helper : (p : Σ ⊢ Class.parent (Σ (cls c)) <: (cls c)) → ∀ gap → ∃ λ (px : Σ ⊢ (cls c) <: (cls c)) → len px ≥ gap + len p
        helper p zero = (super ◅ p) , ≤-step ≤-refl
        helper p (suc gap) with gap ≤? len p
        ... | yes q = (super ◅ p ◅◅ (super ◅ p)) , s≤s (begin
            (gap + len p)
              ≤⟨ +-mono-≤ q (≤-step ≤-refl) ⟩
            len p + (suc (len p))
              ≡⟨ sym (len-◅◅ p (super ◅ p)) ⟩
            len (p ◅◅ super ◅ p)
          ∎)
        ... | no ¬q with helper (p ◅◅ (super ◅ p)) gap
        ... | z , q = z , (begin
            suc (gap + len p)
              ≡⟨ sym $ m+n+o≡n+m+o gap 1 (len p) ⟩
            gap + (len (super ◅ p))
              ≤⟨ +-mono-≤ { gap } ≤-refl (≤-stepsˡ (len p) ≤-refl) ⟩
            gap + (len p + len (super ◅ p))
              ≡⟨ cong (_+_ gap) (sym $ len-◅◅ p (super ◅ p)) ⟩
            gap + (len (p ◅◅ (super ◅ p)))
              ≤⟨ q ⟩
            len z ∎)
          where open import Data.Nat.Properties.Extra
    ... | py , z = super ◅ px ◅◅ py , s≤s (subst (_≤_ n) (sym $ len-◅◅ px py) (≤-stepsˡ (len px) (m+n≤o⇒m≤o n z)))

  -- Proves that a parent can't inherit from it's children,
  Σ-acyclic : ∀ c → ¬ Σ ⊢ Class.parent (Σ (cls c)) <: (cls c)
  Σ-acyclic c px with lem-inf c px (len $ rooted (cls c))
  ... | qx , impossible with lem-len qx (rooted (cls c))
  ... | z = ⊥-elim (<⇒≱ impossible z)

  -- Trivially we have that Object cannot inherit from any class
  ¬Object<:x : ∀ {cid} → ¬ Σ ⊢ Object <: cls cid
  ¬Object<:x (() ◅ p)

  -- Using acyclicity we can show that inheritance proofs are unique
  <:-unique : ∀ {c p} → (l r : Σ ⊢ c <: p) → l ≡ r
  <:-unique ε ε = refl
  <:-unique ε ch@(super ◅ r) = ⊥-elim (Σ-acyclic _ r)
  <:-unique ch@(super ◅ l) ε = ⊥-elim (Σ-acyclic _ l)
  <:-unique (super ◅ l) (super ◅ r) with <:-unique l r
  ... | refl = refl


{-# OPTIONS --without-K #-}

open import Base
open import Homotopy.Pushout
open import Homotopy.VanKampen.Guide

module Homotopy.VanKampen.Code.LemmaPackA {i} (d : pushout-diag i)
  (l : legend i (pushout-diag.C d)) where

  open import Homotopy.Truncation
  open import Homotopy.PathTruncation

  private
    module PackA1 (d : pushout-diag i)
      (l : legend i (pushout-diag.C d)) (n : legend.name l) where
      open pushout-diag d
      open legend l

      -- Code from A.
      open import Homotopy.VanKampen.SplitCode d l (f $ loc n)
      -- Code from B. F for `flipped'.
      import Homotopy.VanKampen.SplitCode (pushout-diag-flip d) l (g $ loc n) as F

      private
        ap⇒bp-split : (∀ {a₂} → code-a a₂ → F.code-b a₂)
                    × (∀ {b₂} → code-b b₂ → F.code-a b₂)
        ap⇒bp-split = code-rec-nondep
          F.code-b ⦃ λ _ → F.code-b-is-set _ ⦄
          F.code-a ⦃ λ _ → F.code-a-is-set _ ⦄
          (λ p → F.⟧a refl₀ F.a⟦ n ⟧b p)
          (λ n₁ pco p → pco F.a⟦ n₁ ⟧b p)
          (λ n₁ pco p → pco F.b⟦ n₁ ⟧a p)
          (λ n₁ pco → F.code-b-refl-refl n₁ pco)
          (λ n₁ pco → F.code-a-refl-refl n₁ pco)
          (λ n₁ pco n₂ r → F.code-ba-swap n₁ pco n₂ r)

      aa⇒ba : ∀ {a₂} → code-a a₂ → F.code-b a₂
      aa⇒ba = π₁ ap⇒bp-split
      ab⇒bb : ∀ {b₂} → code-b b₂ → F.code-a b₂
      ab⇒bb = π₂ ap⇒bp-split

  private
    module PackA2 (d : pushout-diag i)
      (l : legend i (pushout-diag.C d)) (n : legend.name l) where
      open pushout-diag d
      open legend l

      -- Code from A.
      open import Homotopy.VanKampen.SplitCode d l (f $ loc n)
      open PackA1 d l n public
      -- Code from B. F for `flipped'.
      private
        module F where
          open import Homotopy.VanKampen.SplitCode (pushout-diag-flip d) l (g $ loc n) public
          open PackA1 (pushout-diag-flip d) l n public

      private
        aba-glue-code-split : (∀ {a₂} (co : code-a a₂) → F.ab⇒bb (aa⇒ba co) ≡ co)
                            × (∀ {b₂} (co : code-b b₂) → F.aa⇒ba (ab⇒bb co) ≡ co)
      abstract
        aba-glue-code-split = code-rec
          (λ {a} co → F.ab⇒bb (aa⇒ba co) ≡ co)
          ⦃ λ _ → ≡-is-set $ code-a-is-set _ ⦄
          (λ {b} co → F.aa⇒ba (ab⇒bb co) ≡ co)
          ⦃ λ _ → ≡-is-set $ code-b-is-set _ ⦄
          (λ p →
            ⟧a refl₀ a⟦ n ⟧b refl₀ b⟦ n ⟧a p
                ≡⟨ code-a-merge n refl₀ p ⟩
            ⟧a refl₀ ∘₀ p
                ≡⟨ ap (λ x → ⟧a x) $ refl₀-left-unit p ⟩∎
            ⟧a p
                ∎)
          (λ n₁ {co} eq p → ap (λ x → x b⟦ n₁ ⟧a p) eq)
          (λ n₁ {co} eq p → ap (λ x → x a⟦ n₁ ⟧b p) eq)
          (λ _ _ → code-has-all-cells₂-a _ _)
          (λ _ _ → code-has-all-cells₂-b _ _)
          (λ _ _ _ _ → code-has-all-cells₂-a _ _)

      aba-glue-code-a : ∀ {a₂} (co : code-a a₂) → F.ab⇒bb (aa⇒ba co) ≡ co
      aba-glue-code-a = π₁ aba-glue-code-split
      aba-glue-code-b : ∀ {b₂} (co : code-b b₂) → F.aa⇒ba (ab⇒bb co) ≡ co
      aba-glue-code-b = π₂ aba-glue-code-split

  private
    module PackA3 (d : pushout-diag i)
      (l : legend i (pushout-diag.C d)) (n : legend.name l) where
      open pushout-diag d
      open legend l

      -- Code from A.
      open import Homotopy.VanKampen.SplitCode d l (f $ loc n)
      open PackA2 d l n public
      -- Code from B. F for `flipped'.
      private
        module F where
          open import Homotopy.VanKampen.SplitCode (pushout-diag-flip d) l (g $ loc n) public
          open PackA2 (pushout-diag-flip d) l n public

      flipped-code : P → Set i
      flipped-code = F.code ◯ pushout-flip

      private
        ap⇒bp-glue : ∀ c → transport (λ p → code p → flipped-code p) (glue c) aa⇒ba ≡ ab⇒bb
      abstract
        ap⇒bp-glue = loc-fiber-rec l
          (λ c → transport (λ p → code p → flipped-code p) (glue c) aa⇒ba ≡ ab⇒bb)
          ⦃ λ _ → Π-is-set (λ _ → F.code-a-is-set _) _ _ ⦄
          (λ n → funext λ co →
            transport (λ p → code p → flipped-code p) (glue $ loc n) aa⇒ba co
                ≡⟨ trans-→ code flipped-code (glue $ loc n) aa⇒ba co ⟩
            transport flipped-code (glue $ loc n)
              (aa⇒ba $ transport code (! $ glue $ loc n) co)
                ≡⟨ ap (λ x → transport flipped-code (glue $ loc n) $ aa⇒ba x)
                    $ trans-code-!glue-loc n co ⟩
            transport flipped-code (glue $ loc n) (aa⇒ba $ b⇒a n co)
                ≡⟨ refl ⟩
            transport flipped-code (glue $ loc n) (F.a⇒b n $ ab⇒bb co)
                ≡⟨ ! $ trans-ap F.code pushout-flip (glue $ loc n)
                     $ F.a⇒b n $ ab⇒bb co ⟩
            transport F.code (ap pushout-flip $ glue $ loc n) (F.a⇒b n $ ab⇒bb co)
                ≡⟨ ap (λ x → transport F.code x $ F.a⇒b n $ ab⇒bb co)
                      $ pushout-β-glue-nondep _ right left (! ◯ glue) $ loc n ⟩
            transport F.code (! $ glue $ loc n) (F.a⇒b n $ ab⇒bb co)
                ≡⟨ F.trans-code-!glue-loc n (F.a⇒b n $ ab⇒bb co) ⟩
            F.b⇒a n (F.a⇒b n $ ab⇒bb co)
                ≡⟨ F.code-a-refl-refl n $ ab⇒bb co ⟩∎
            ab⇒bb co
                ∎)

      ap⇒bp : ∀ {p} → code p → flipped-code p
      ap⇒bp {p} = pushout-rec
        (λ x → code x → flipped-code x)
        (λ _ → aa⇒ba)
        (λ _ → ab⇒bb)
        ap⇒bp-glue
        p

  open PackA3 d l public


{-# OPTIONS --without-K --safe #-}

module Definition.Typed.Weakening where

open import Definition.Untyped as U hiding (wk)
open import Definition.Untyped.Properties
open import Definition.Typed

import Tools.PropositionalEquality as PE

-- Weakening type

data _∷_⊆_ : Wk → Con Term → Con Term → Set where
  id   : ∀ {Γ}       → id ∷ Γ ⊆ Γ
  step : ∀ {Γ Δ A ρ} → ρ  ∷ Δ ⊆ Γ → step ρ ∷ Δ ∙ A        ⊆ Γ
  lift : ∀ {Γ Δ A ρ} → ρ  ∷ Δ ⊆ Γ → lift ρ ∷ Δ ∙ U.wk ρ A ⊆ Γ ∙ A

-- -- Weakening composition

_•ₜ_ : ∀ {ρ ρ′ Γ Δ Δ′} → ρ ∷ Γ ⊆ Δ → ρ′ ∷ Δ ⊆ Δ′ → ρ • ρ′ ∷ Γ ⊆ Δ′
id     •ₜ η′ = η′
step η •ₜ η′ = step (η •ₜ η′)
lift η •ₜ id = lift η
lift η •ₜ step η′ = step (η •ₜ η′)
_•ₜ_ {lift ρ} {lift ρ′} {Δ′ = Δ′ ∙ A} (lift η) (lift η′) =
  PE.subst (λ x → lift (ρ • ρ′) ∷ x ⊆ Δ′ ∙ A)
           (PE.cong₂ _∙_ PE.refl (PE.sym (wk-comp ρ ρ′ A)))
           (lift (η •ₜ η′))

-- Weakening of judgements

wkIndex : ∀ {Γ Δ n A ρ} → ρ ∷ Δ ⊆ Γ →
        let ρA = U.wk ρ A
            ρn = wkVar ρ n
        in  ⊢ Δ → n ∷ A ∈ Γ → ρn ∷ ρA ∈ Δ
wkIndex id ⊢Δ i = PE.subst (λ x → _ ∷ x ∈ _) (PE.sym (wk-id _)) i
wkIndex (step ρ) (⊢Δ ∙ A) i = PE.subst (λ x → _ ∷ x ∈ _)
                                       (wk1-wk _ _)
                                       (there (wkIndex ρ ⊢Δ i))
wkIndex (lift ρ) (⊢Δ ∙ A) (there i) = PE.subst (λ x → _ ∷ x ∈ _)
                                               (wk1-wk≡lift-wk1 _ _)
                                               (there (wkIndex ρ ⊢Δ i))
wkIndex (lift ρ) ⊢Δ here =
  let G = _
      n = _
  in  PE.subst (λ x → n ∷ x ∈ G)
               (wk1-wk≡lift-wk1 _ _)
               here

mutual
  wk : ∀ {Γ Δ A ρ} → ρ ∷ Δ ⊆ Γ →
     let ρA = U.wk ρ A
     in  ⊢ Δ → Γ ⊢ A → Δ ⊢ ρA
  wk ρ ⊢Δ (ℕⱼ ⊢Γ) = ℕⱼ ⊢Δ
  wk ρ ⊢Δ (Uⱼ ⊢Γ) = Uⱼ ⊢Δ
  wk ρ ⊢Δ (Πⱼ F ▹ G) = let ρF = wk ρ ⊢Δ F
                       in  Πⱼ ρF ▹ (wk (lift ρ) (⊢Δ ∙ ρF) G)
  wk ρ ⊢Δ (univ A) = univ (wkTerm ρ ⊢Δ A)

  wkTerm : ∀ {Γ Δ A t ρ} → ρ ∷ Δ ⊆ Γ →
         let ρA = U.wk ρ A
             ρt = U.wk ρ t
         in ⊢ Δ → Γ ⊢ t ∷ A → Δ ⊢ ρt ∷ ρA
  wkTerm ρ ⊢Δ (ℕⱼ ⊢Γ) = ℕⱼ ⊢Δ
  wkTerm ρ ⊢Δ (Πⱼ F ▹ G) = let ρF = wkTerm ρ ⊢Δ F
                          in  Πⱼ ρF ▹ (wkTerm (lift ρ) (⊢Δ ∙ univ ρF) G)
  wkTerm ρ ⊢Δ (var ⊢Γ x) = var ⊢Δ (wkIndex ρ ⊢Δ x)
  wkTerm ρ ⊢Δ (lamⱼ F t) = let ρF = wk ρ ⊢Δ F
                          in lamⱼ ρF (wkTerm (lift ρ) (⊢Δ ∙ ρF) t)
  wkTerm ρ ⊢Δ (_∘ⱼ_ {G = G} g a) = PE.subst (λ x → _ ⊢ _ ∷ x)
                                           (PE.sym (wk-β G))
                                           (wkTerm ρ ⊢Δ g ∘ⱼ wkTerm ρ ⊢Δ a)
  wkTerm ρ ⊢Δ (zeroⱼ ⊢Γ) = zeroⱼ ⊢Δ
  wkTerm ρ ⊢Δ (sucⱼ n) = sucⱼ (wkTerm ρ ⊢Δ n)
  wkTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrecⱼ {G = G} {s = s} ⊢G ⊢z ⊢s ⊢n) =
    PE.subst (λ x → _ ⊢ natrec _ _ _ _ ∷ x) (PE.sym (wk-β G))
             (natrecⱼ (wk (lift [ρ]) (⊢Δ ∙ ℕⱼ ⊢Δ) ⊢G)
                      (PE.subst (λ x → _ ⊢ _ ∷ x) (wk-β G) (wkTerm [ρ] ⊢Δ ⊢z))
                      (PE.subst (λ x → Δ ⊢ U.wk ρ s ∷ x)
                                (wk-β-natrec ρ G)
                                (wkTerm [ρ] ⊢Δ ⊢s))
                      (wkTerm [ρ] ⊢Δ ⊢n))
  wkTerm ρ ⊢Δ (conv t A≡B) = conv (wkTerm ρ ⊢Δ t) (wkEq ρ ⊢Δ A≡B)

  wkEq : ∀ {Γ Δ A B ρ} → ρ ∷ Δ ⊆ Γ →
       let ρA = U.wk ρ A
           ρB = U.wk ρ B
       in ⊢ Δ → Γ ⊢ A ≡ B → Δ ⊢ ρA ≡ ρB
  wkEq ρ ⊢Δ (univ A≡B) = univ (wkEqTerm ρ ⊢Δ A≡B)
  wkEq ρ ⊢Δ (refl A) = refl (wk ρ ⊢Δ A)
  wkEq ρ ⊢Δ (sym A≡B) = sym (wkEq ρ ⊢Δ A≡B)
  wkEq ρ ⊢Δ (trans A≡B B≡C) = trans (wkEq ρ ⊢Δ A≡B) (wkEq ρ ⊢Δ B≡C)
  wkEq ρ ⊢Δ (Π-cong F F≡H G≡E) = let ρF = wk ρ ⊢Δ F
                                 in  Π-cong ρF (wkEq ρ ⊢Δ F≡H)
                                               (wkEq (lift ρ) (⊢Δ ∙ ρF) G≡E)

  wkEqTerm : ∀ {Γ Δ A t u ρ} → ρ ∷ Δ ⊆ Γ →
           let ρA = U.wk ρ A
               ρt = U.wk ρ t
               ρu = U.wk ρ u
           in ⊢ Δ → Γ ⊢ t ≡ u ∷ A → Δ ⊢ ρt ≡ ρu ∷ ρA
  wkEqTerm ρ ⊢Δ (refl t) = refl (wkTerm ρ ⊢Δ t)
  wkEqTerm ρ ⊢Δ (sym t≡u) = sym (wkEqTerm ρ ⊢Δ t≡u)
  wkEqTerm ρ ⊢Δ (trans t≡u u≡r) = trans (wkEqTerm ρ ⊢Δ t≡u) (wkEqTerm ρ ⊢Δ u≡r)
  wkEqTerm ρ ⊢Δ (conv t≡u A≡B) = conv (wkEqTerm ρ ⊢Δ t≡u) (wkEq ρ ⊢Δ A≡B)
  wkEqTerm ρ ⊢Δ (Π-cong F F≡H G≡E) =
    let ρF = wk ρ ⊢Δ F
    in  Π-cong ρF (wkEqTerm ρ ⊢Δ F≡H)
                  (wkEqTerm (lift ρ) (⊢Δ ∙ ρF) G≡E)
  wkEqTerm ρ ⊢Δ (app-cong {G = G} f≡g a≡b) =
    PE.subst (λ x → _ ⊢ _ ≡ _ ∷ x)
             (PE.sym (wk-β G))
             (app-cong (wkEqTerm ρ ⊢Δ f≡g) (wkEqTerm ρ ⊢Δ a≡b))
  wkEqTerm ρ ⊢Δ (β-red {a = a} {t = t} {G = G} F ⊢t ⊢a) =
    let ρF = wk ρ ⊢Δ F
    in  PE.subst (λ x → _ ⊢ _ ≡ _ ∷ x)
                 (PE.sym (wk-β G))
                 (PE.subst (λ x → _ ⊢ U.wk _ ((lam t) ∘ a) ≡ x ∷ _)
                           (PE.sym (wk-β t))
                           (β-red ρF (wkTerm (lift ρ) (⊢Δ ∙ ρF) ⊢t)
                                     (wkTerm ρ ⊢Δ ⊢a)))
  wkEqTerm ρ ⊢Δ (η-eq F f g f0≡g0) =
    let ρF = wk ρ ⊢Δ F
    in  η-eq ρF (wkTerm ρ ⊢Δ f)
                (wkTerm ρ ⊢Δ g)
                (PE.subst (λ t → _ ⊢ t ∘ _ ≡ _ ∷ _)
                          (PE.sym (wk1-wk≡lift-wk1 _ _))
                          (PE.subst (λ t → _ ⊢ _ ≡ t ∘ _ ∷ _)
                                    (PE.sym (wk1-wk≡lift-wk1 _ _))
                                    (wkEqTerm (lift ρ) (⊢Δ ∙ ρF) f0≡g0)))
  wkEqTerm ρ ⊢Δ (suc-cong m≡n) = suc-cong (wkEqTerm ρ ⊢Δ m≡n)
  wkEqTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-cong {s = s} {s′ = s′} {F = F}
                                     F≡F′ z≡z′ s≡s′ n≡n′) =
    PE.subst (λ x → Δ ⊢ natrec _ _ _ _ ≡ _ ∷ x) (PE.sym (wk-β F))
             (natrec-cong (wkEq (lift [ρ]) (⊢Δ ∙ ℕⱼ ⊢Δ) F≡F′)
                          (PE.subst (λ x → Δ ⊢ _ ≡ _ ∷ x) (wk-β F)
                                    (wkEqTerm [ρ] ⊢Δ z≡z′))
                          (PE.subst (λ x → Δ ⊢ U.wk ρ s
                                             ≡ U.wk ρ s′ ∷ x)
                                    (wk-β-natrec _ F)
                                    (wkEqTerm [ρ] ⊢Δ s≡s′))
                          (wkEqTerm [ρ] ⊢Δ n≡n′))
  wkEqTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-zero {z} {s} {F} ⊢F ⊢z ⊢s) =
    PE.subst (λ x → Δ ⊢ natrec (U.wk (lift _) F) _ _ _ ≡ _ ∷ x)
             (PE.sym (wk-β F))
             (natrec-zero (wk (lift [ρ]) (⊢Δ ∙ ℕⱼ ⊢Δ) ⊢F)
                          (PE.subst (λ x → Δ ⊢ U.wk ρ z ∷ x)
                                    (wk-β F)
                                    (wkTerm [ρ] ⊢Δ ⊢z))
                          (PE.subst (λ x → Δ ⊢ U.wk ρ s ∷ x)
                                    (wk-β-natrec _ F)
                                    (wkTerm [ρ] ⊢Δ ⊢s)))
  wkEqTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-suc {n} {z} {s} {F} ⊢n ⊢F ⊢z ⊢s) =
    PE.subst (λ x → Δ ⊢ natrec (U.wk (lift _) F) _ _ _
                      ≡ _ ∘ (natrec _ _ _ _) ∷ x)
             (PE.sym (wk-β F))
             (natrec-suc (wkTerm [ρ] ⊢Δ ⊢n)
                         (wk (lift [ρ]) (⊢Δ ∙ ℕⱼ ⊢Δ) ⊢F)
                         (PE.subst (λ x → Δ ⊢ U.wk ρ z ∷ x)
                                   (wk-β F)
                                   (wkTerm [ρ] ⊢Δ ⊢z))
                         (PE.subst (λ x → Δ ⊢ U.wk ρ s ∷ x)
                                   (wk-β-natrec _ F)
                                   (wkTerm [ρ] ⊢Δ ⊢s)))

mutual
  wkRed : ∀ {Γ Δ A B ρ} → ρ ∷ Δ ⊆ Γ →
           let ρA = U.wk ρ A
               ρB = U.wk ρ B
           in ⊢ Δ → Γ ⊢ A ⇒ B → Δ ⊢ ρA ⇒ ρB
  wkRed ρ ⊢Δ (univ A⇒B) = univ (wkRedTerm ρ ⊢Δ A⇒B)

  wkRedTerm : ∀ {Γ Δ A t u ρ} → ρ ∷ Δ ⊆ Γ →
           let ρA = U.wk ρ A
               ρt = U.wk ρ t
               ρu = U.wk ρ u
           in ⊢ Δ → Γ ⊢ t ⇒ u ∷ A → Δ ⊢ ρt ⇒ ρu ∷ ρA
  wkRedTerm ρ ⊢Δ (conv t⇒u A≡B) = conv (wkRedTerm ρ ⊢Δ t⇒u) (wkEq ρ ⊢Δ A≡B)
  wkRedTerm ρ ⊢Δ (app-subst {B = B} t⇒u a) =
    PE.subst (λ x → _ ⊢ _ ⇒ _ ∷ x) (PE.sym (wk-β B))
             (app-subst (wkRedTerm ρ ⊢Δ t⇒u) (wkTerm ρ ⊢Δ a))
  wkRedTerm ρ ⊢Δ (β-red {A} {B} {a} {t} ⊢A ⊢t ⊢a) =
    let ⊢ρA = wk ρ ⊢Δ ⊢A
    in  PE.subst (λ x → _ ⊢ _ ⇒ _ ∷ x) (PE.sym (wk-β B))
                 (PE.subst (λ x → _ ⊢ U.wk _ ((lam t) ∘ a) ⇒ x ∷ _)
                           (PE.sym (wk-β t))
                           (β-red ⊢ρA (wkTerm (lift ρ) (⊢Δ ∙ ⊢ρA) ⊢t)
                                      (wkTerm ρ ⊢Δ ⊢a)))
  wkRedTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-subst {s = s} {F = F} ⊢F ⊢z ⊢s n⇒n′) =
    PE.subst (λ x → _ ⊢ natrec _ _ _ _ ⇒ _ ∷ x) (PE.sym (wk-β F))
             (natrec-subst (wk (lift [ρ]) (⊢Δ ∙ ℕⱼ ⊢Δ) ⊢F)
                           (PE.subst (λ x → _ ⊢ _ ∷ x) (wk-β F)
                                     (wkTerm [ρ] ⊢Δ ⊢z))
                           (PE.subst (λ x → Δ ⊢ U.wk ρ s ∷ x)
                                     (wk-β-natrec _ F)
                                     (wkTerm [ρ] ⊢Δ ⊢s))
                           (wkRedTerm [ρ] ⊢Δ n⇒n′))
  wkRedTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-zero {s = s} {F = F} ⊢F ⊢z ⊢s) =
    PE.subst (λ x → _ ⊢ natrec (U.wk (lift ρ) F) _ _ _ ⇒ _ ∷ x)
             (PE.sym (wk-β F))
             (natrec-zero (wk (lift [ρ]) (⊢Δ ∙ ℕⱼ ⊢Δ) ⊢F)
                          (PE.subst (λ x → _ ⊢ _ ∷ x)
                                    (wk-β F)
                                    (wkTerm [ρ] ⊢Δ ⊢z))
                          (PE.subst (λ x → Δ ⊢ U.wk ρ s ∷ x)
                                    (wk-β-natrec ρ F)
                                    (wkTerm [ρ] ⊢Δ ⊢s)))
  wkRedTerm {Δ = Δ} {ρ = ρ} [ρ] ⊢Δ (natrec-suc {s = s} {F = F} ⊢n ⊢F ⊢z ⊢s) =
    PE.subst (λ x → _ ⊢ natrec _ _ _ _ ⇒ _ ∘ natrec _ _ _ _ ∷ x)
             (PE.sym (wk-β F))
             (natrec-suc (wkTerm [ρ] ⊢Δ ⊢n)
                         (wk (lift [ρ]) (⊢Δ ∙ ℕⱼ ⊢Δ) ⊢F)
                         (PE.subst (λ x → _ ⊢ _ ∷ x)
                                   (wk-β F)
                                   (wkTerm [ρ] ⊢Δ ⊢z))
                         (PE.subst (λ x → Δ ⊢ U.wk ρ s ∷ x)
                                   (wk-β-natrec ρ F)
                                   (wkTerm [ρ] ⊢Δ ⊢s)))

wkRed* : ∀ {Γ Δ A B ρ} → ρ ∷ Δ ⊆ Γ →
           let ρA = U.wk ρ A
               ρB = U.wk ρ B
           in ⊢ Δ → Γ ⊢ A ⇒* B → Δ ⊢ ρA ⇒* ρB
wkRed* ρ ⊢Δ (id A) = id (wk ρ ⊢Δ A)
wkRed* ρ ⊢Δ (A⇒A′ ⇨ A′⇒*B) = wkRed ρ ⊢Δ A⇒A′ ⇨ wkRed* ρ ⊢Δ A′⇒*B

wkRed*Term : ∀ {Γ Δ A t u ρ} → ρ ∷ Δ ⊆ Γ →
           let ρA = U.wk ρ A
               ρt = U.wk ρ t
               ρu = U.wk ρ u
           in ⊢ Δ → Γ ⊢ t ⇒* u ∷ A → Δ ⊢ ρt ⇒* ρu ∷ ρA
wkRed*Term ρ ⊢Δ (id t) = id (wkTerm ρ ⊢Δ t)
wkRed*Term ρ ⊢Δ (t⇒t′ ⇨ t′⇒*u) = wkRedTerm ρ ⊢Δ t⇒t′ ⇨ wkRed*Term ρ ⊢Δ t′⇒*u

wkRed:*: : ∀ {Γ Δ A B ρ} → ρ ∷ Δ ⊆ Γ →
         let ρA = U.wk ρ A
             ρB = U.wk ρ B
         in ⊢ Δ → Γ ⊢ A :⇒*: B → Δ ⊢ ρA :⇒*: ρB
wkRed:*: ρ ⊢Δ [ ⊢A , ⊢B , D ] = [ wk ρ ⊢Δ ⊢A , wk ρ ⊢Δ ⊢B , wkRed* ρ ⊢Δ D ]

wkRed:*:Term : ∀ {Γ Δ A t u ρ} → ρ ∷ Δ ⊆ Γ →
             let ρA = U.wk ρ A
                 ρt = U.wk ρ t
                 ρu = U.wk ρ u
             in ⊢ Δ → Γ ⊢ t :⇒*: u ∷ A → Δ ⊢ ρt :⇒*: ρu ∷ ρA
wkRed:*:Term ρ ⊢Δ [ ⊢t , ⊢u , d ] =
  [ wkTerm ρ ⊢Δ ⊢t , wkTerm ρ ⊢Δ ⊢u , wkRed*Term ρ ⊢Δ d ]


open import SOAS.Common
open import SOAS.Families.Core
open import Categories.Object.Initial
open import SOAS.Coalgebraic.Strength
import SOAS.Metatheory.MetaAlgebra

-- Traversals parametrised by a pointed coalgebra
module SOAS.Metatheory.Traversal {T : Set}
  (⅀F : Functor 𝔽amiliesₛ 𝔽amiliesₛ) (⅀:Str : Strength ⅀F)
  (𝔛 : Familyₛ) (open SOAS.Metatheory.MetaAlgebra ⅀F 𝔛)
  (𝕋:Init : Initial 𝕄etaAlgebras)
  where

open import SOAS.Context
open import SOAS.Variable
open import SOAS.Abstract.Hom
import SOAS.Abstract.Coalgebra as →□ ; open →□.Sorted
import SOAS.Abstract.Box as □ ; open □.Sorted

open import SOAS.Metatheory.Algebra ⅀F
open import SOAS.Metatheory.Semantics ⅀F ⅀:Str 𝔛 𝕋:Init

open Strength ⅀:Str

private
  variable
    Γ Δ Θ Π : Ctx
    α β : T
    𝒫 𝒬 𝒜 : Familyₛ

-- Parametrised interpretation into an internal hom
module Traversal (𝒫ᴮ : Coalgₚ 𝒫)(𝑎𝑙𝑔 : ⅀ 𝒜 ⇾̣ 𝒜)
                 (φ : 𝒫 ⇾̣ 𝒜)(χ : 𝔛 ⇾̣ 〖 𝒜 , 𝒜 〗) where

  open Coalgₚ 𝒫ᴮ

  -- Under the assumptions 𝒜 and 〖 𝒫 , 𝒜 〗 are both meta-algebras
  𝒜ᵃ : MetaAlg 𝒜
  𝒜ᵃ = record { 𝑎𝑙𝑔 = 𝑎𝑙𝑔 ; 𝑣𝑎𝑟 = λ x → φ (η x) ; 𝑚𝑣𝑎𝑟 = χ }

  Travᵃ : MetaAlg 〖 𝒫 , 𝒜 〗
  Travᵃ = record
    { 𝑎𝑙𝑔  = λ t σ → 𝑎𝑙𝑔 (str 𝒫ᴮ 𝒜 t σ)
    ; 𝑣𝑎𝑟  = λ x σ → φ (σ x)
    ; 𝑚𝑣𝑎𝑟 = λ 𝔪 ε σ → χ 𝔪 (λ 𝔫 → ε 𝔫 σ)
    }

  -- 〖 𝒫 , 𝒜 〗 is also a pointed □-coalgebra
  Travᵇ : Coalg 〖 𝒫 , 𝒜 〗
  Travᵇ = record { r = λ h ρ σ → h (σ ∘ ρ) ; counit = refl ; comult = refl }

  Travᴮ : Coalgₚ 〖 𝒫 , 𝒜 〗
  Travᴮ = record { ᵇ = Travᵇ ; η = λ x σ → φ (σ x) ; r∘η = refl }

  open Semantics Travᵃ public renaming (𝕤𝕖𝕞 to 𝕥𝕣𝕒𝕧)

  -- Traversal-specific homomorphism properties that incorporate a substitution
  𝕥⟨𝕧⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{x : ℐ α Γ} → 𝕥𝕣𝕒𝕧 (𝕧𝕒𝕣 x) σ ≡ φ (σ x)
  𝕥⟨𝕧⟩ {σ = σ} = cong (λ - → - σ) ⟨𝕧⟩

  𝕥⟨𝕞⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{𝔪 : 𝔛 α Π}{ε : Π ~[ 𝕋 ]↝ Γ}
       → 𝕥𝕣𝕒𝕧 (𝕞𝕧𝕒𝕣 𝔪 ε) σ ≡ χ 𝔪 (λ p → 𝕥𝕣𝕒𝕧 (ε p) σ)
  𝕥⟨𝕞⟩ {σ = σ} = cong (λ - → - σ) ⟨𝕞⟩

  𝕥⟨𝕒⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{t : ⅀ 𝕋 α Γ}
       → 𝕥𝕣𝕒𝕧 (𝕒𝕝𝕘 t) σ ≡ 𝑎𝑙𝑔 (str 𝒫ᴮ 𝒜 (⅀₁ 𝕥𝕣𝕒𝕧 t) σ)
  𝕥⟨𝕒⟩ {σ = σ} = cong (λ - → - σ) ⟨𝕒⟩

  -- Congruence in the two arguments of 𝕥𝕣𝕒𝕧
  𝕥≈₁ : {σ : Γ ~[ 𝒫 ]↝ Δ}{t₁ t₂ : 𝕋 α Γ} → t₁ ≡ t₂ → 𝕥𝕣𝕒𝕧 t₁ σ ≡ 𝕥𝕣𝕒𝕧 t₂ σ
  𝕥≈₁ {σ = σ} p = cong (λ - → 𝕥𝕣𝕒𝕧 - σ) p

  𝕥≈₂ : {σ₁ σ₂ : Γ ~[ 𝒫 ]↝ Δ}{t : 𝕋 α Γ}
      → ({τ : T}{x : ℐ τ Γ} → σ₁ x ≡ σ₂ x)
      → 𝕥𝕣𝕒𝕧 t σ₁ ≡ 𝕥𝕣𝕒𝕧 t σ₂
  𝕥≈₂ {t = t} p = cong (𝕥𝕣𝕒𝕧 t) (dext′ p)

-- A pointed meta-Λ-algebra induces 𝕒𝕝𝕘 traversal into □ 𝒜
module □Traversal {𝒜} (𝒜ᵃ : MetaAlg 𝒜) =
  Traversal ℐᴮ (MetaAlg.𝑎𝑙𝑔 𝒜ᵃ) (MetaAlg.𝑣𝑎𝑟 𝒜ᵃ) (MetaAlg.𝑚𝑣𝑎𝑟 𝒜ᵃ)

-- Corollary: □ lifts to an endofunctor on pointed meta-Λ-algebras
□ᵃ : (𝒜ᵃ : MetaAlg 𝒜) → MetaAlg (□ 𝒜)
□ᵃ = □Traversal.Travᵃ

-- Helper records for proving equality of maps f, g out of 𝕋,
-- with 0, 1 or 2 parameters
record MapEq₀ (𝒜 : Familyₛ)(f g : 𝕋 ⇾̣ 𝒜) : Set where
  field
    ᵃ : MetaAlg 𝒜
  open Semantics ᵃ
  open 𝒜

  field
    f⟨𝑣⟩ : {x : ℐ α Γ}
         → f (𝕧𝕒𝕣 x) ≡ 𝑣𝑎𝑟 x
    f⟨𝑚⟩ : {𝔪 : 𝔛 α Π}{ε : Π ~[ 𝕋 ]↝ Γ}
         → f (𝕞𝕧𝕒𝕣 𝔪 ε) ≡ 𝑚𝑣𝑎𝑟 𝔪 (f ∘ ε)
    f⟨𝑎⟩ : {t : ⅀ 𝕋 α Γ}
         → f (𝕒𝕝𝕘 t) ≡ 𝑎𝑙𝑔 (⅀₁ f t)
    g⟨𝑣⟩ : {x : ℐ α Γ}
         → g (𝕧𝕒𝕣 x) ≡ 𝑣𝑎𝑟 x
    g⟨𝑚⟩ : {𝔪 : 𝔛 α Π}{ε : Π ~[ 𝕋 ]↝ Γ}
         → g (𝕞𝕧𝕒𝕣 𝔪 ε) ≡ 𝑚𝑣𝑎𝑟 𝔪 (g ∘ ε)
    g⟨𝑎⟩ : {t : ⅀ 𝕋 α Γ}
         → g (𝕒𝕝𝕘 t) ≡ 𝑎𝑙𝑔 (⅀₁ g t)

  fᵃ : MetaAlg⇒ 𝕋ᵃ ᵃ f
  fᵃ = record { ⟨𝑎𝑙𝑔⟩ =  f⟨𝑎⟩ ; ⟨𝑣𝑎𝑟⟩ =  f⟨𝑣⟩ ; ⟨𝑚𝑣𝑎𝑟⟩ =  f⟨𝑚⟩ }
  gᵃ : MetaAlg⇒ 𝕋ᵃ ᵃ g
  gᵃ = record { ⟨𝑎𝑙𝑔⟩ =  g⟨𝑎⟩ ; ⟨𝑣𝑎𝑟⟩ =  g⟨𝑣⟩ ; ⟨𝑚𝑣𝑎𝑟⟩ =  g⟨𝑚⟩ }

  ≈ : (t : 𝕋 α Γ) → f t ≡ g t
  ≈ t = eq fᵃ gᵃ t

record MapEq₁ (𝒫ᴮ : Coalgₚ 𝒫)(𝑎𝑙𝑔 : ⅀ 𝒜 ⇾̣ 𝒜)
              (f g : 𝕋 ⇾̣ 〖 𝒫 , 𝒜 〗) : Set where
  field
    φ : 𝒫 ⇾̣ 𝒜
    χ : 𝔛 ⇾̣ 〖 𝒜 , 𝒜 〗

  open Traversal 𝒫ᴮ 𝑎𝑙𝑔 φ χ

  field
    f⟨𝑣⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{x : ℐ α Γ}
         → f (𝕧𝕒𝕣 x) σ ≡ φ (σ x)
    f⟨𝑚⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{𝔪 : 𝔛 α Π}{ε : Π ~[ 𝕋 ]↝ Γ}
         → f (𝕞𝕧𝕒𝕣 𝔪 ε) σ ≡ χ 𝔪 (λ p → f (ε p) σ)
    f⟨𝑎⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{t : ⅀ 𝕋 α Γ}
         → f (𝕒𝕝𝕘 t) σ ≡ 𝑎𝑙𝑔 (str 𝒫ᴮ 𝒜 (⅀₁ f t) σ)
    g⟨𝑣⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{x : ℐ α Γ}
         → g (𝕧𝕒𝕣 x) σ ≡ φ (σ x)
    g⟨𝑚⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{𝔪 : 𝔛 α Π}{ε : Π ~[ 𝕋 ]↝ Γ}
         → g (𝕞𝕧𝕒𝕣 𝔪 ε) σ ≡ χ 𝔪 (λ p → g (ε p) σ)
    g⟨𝑎⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{t : ⅀ 𝕋 α Γ}
         → g (𝕒𝕝𝕘 t) σ ≡ 𝑎𝑙𝑔 (str 𝒫ᴮ 𝒜 (⅀₁ g t) σ)

  fᵃ : MetaAlg⇒ 𝕋ᵃ Travᵃ f
  fᵃ = record { ⟨𝑎𝑙𝑔⟩ = dext′ f⟨𝑎⟩ ; ⟨𝑣𝑎𝑟⟩ = dext′ f⟨𝑣⟩ ; ⟨𝑚𝑣𝑎𝑟⟩ = dext′ f⟨𝑚⟩ }
  gᵃ : MetaAlg⇒ 𝕋ᵃ Travᵃ g
  gᵃ = record { ⟨𝑎𝑙𝑔⟩ = dext′ g⟨𝑎⟩ ; ⟨𝑣𝑎𝑟⟩ = dext′ g⟨𝑣⟩ ; ⟨𝑚𝑣𝑎𝑟⟩ = dext′ g⟨𝑚⟩ }

  ≈ : {σ : Γ ~[ 𝒫 ]↝ Δ}(t : 𝕋 α Γ) → f t σ ≡ g t σ
  ≈ {σ = σ} t = cong (λ - → - σ) (eq fᵃ gᵃ t)

record MapEq₂ (𝒫ᴮ : Coalgₚ 𝒫)(𝒬ᴮ : Coalgₚ 𝒬)(𝑎𝑙𝑔 : ⅀ 𝒜 ⇾̣ 𝒜)
                (f g : 𝕋 ⇾̣ 〖 𝒫 , 〖 𝒬 , 𝒜 〗 〗) : Set where
  field
    φ : 𝒬 ⇾̣ 𝒜
    ϕ : 𝒫 ⇾̣ 〖 𝒬 , 𝒜 〗
    χ : 𝔛 ⇾̣ 〖 𝒜 , 𝒜 〗

  open Traversal 𝒫ᴮ (Traversal.𝒜.𝑎𝑙𝑔 𝒬ᴮ 𝑎𝑙𝑔 φ χ) ϕ (λ 𝔪 ε σ → χ 𝔪 (λ 𝔫 → ε 𝔫 σ))

  field
    f⟨𝑣⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{ς : Δ ~[ 𝒬 ]↝ Θ}{x : ℐ α Γ}
         → f (𝕧𝕒𝕣 x) σ ς ≡ ϕ (σ x) ς
    f⟨𝑚⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{ς : Δ ~[ 𝒬 ]↝ Θ}{𝔪 : 𝔛 α Π}{ε : Π ~[ 𝕋 ]↝ Γ}
         → f (𝕞𝕧𝕒𝕣 𝔪 ε) σ ς ≡ χ 𝔪 (λ 𝔫 → f (ε 𝔫) σ ς)
    f⟨𝑎⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{ς : Δ ~[ 𝒬 ]↝ Θ}{t : ⅀ 𝕋 α Γ}
         → f (𝕒𝕝𝕘 t) σ ς ≡ 𝑎𝑙𝑔 (str 𝒬ᴮ 𝒜 (str 𝒫ᴮ 〖 𝒬 , 𝒜 〗 (⅀₁ f t) σ) ς)
    g⟨𝑣⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{ς : Δ ~[ 𝒬 ]↝ Θ}{x : ℐ α Γ}
         → g (𝕧𝕒𝕣 x) σ ς ≡ ϕ (σ x) ς
    g⟨𝑚⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{ς : Δ ~[ 𝒬 ]↝ Θ}{𝔪 : 𝔛 α Π}{ε : Π ~[ 𝕋 ]↝ Γ}
         → g (𝕞𝕧𝕒𝕣 𝔪 ε) σ ς ≡ χ 𝔪 (λ 𝔫 → g (ε 𝔫) σ ς)
    g⟨𝑎⟩ : {σ : Γ ~[ 𝒫 ]↝ Δ}{ς : Δ ~[ 𝒬 ]↝ Θ}{t : ⅀ 𝕋 α Γ}
         → g (𝕒𝕝𝕘 t) σ ς ≡ 𝑎𝑙𝑔 (str 𝒬ᴮ 𝒜 (str 𝒫ᴮ 〖 𝒬 , 𝒜 〗 (⅀₁ g t) σ) ς)

  fᵃ : MetaAlg⇒ 𝕋ᵃ Travᵃ f
  fᵃ = record { ⟨𝑎𝑙𝑔⟩ = dext′ (dext′ f⟨𝑎⟩) ; ⟨𝑣𝑎𝑟⟩ = dext′ (dext′ f⟨𝑣⟩)
              ; ⟨𝑚𝑣𝑎𝑟⟩ = dext′ (dext′ f⟨𝑚⟩) }
  gᵃ : MetaAlg⇒ 𝕋ᵃ Travᵃ g
  gᵃ = record { ⟨𝑎𝑙𝑔⟩ = dext′ (dext′ g⟨𝑎⟩) ; ⟨𝑣𝑎𝑟⟩ = dext′ (dext′ g⟨𝑣⟩)
              ; ⟨𝑚𝑣𝑎𝑟⟩ = dext′ (dext′ g⟨𝑚⟩) }

  ≈ : {σ : Γ ~[ 𝒫 ]↝ Δ}{ς : Δ ~[ 𝒬 ]↝ Θ}(t : 𝕋 α Γ) → f t σ ς ≡ g t σ ς
  ≈ {σ = σ}{ς} t = cong (λ - → - σ ς) (eq fᵃ gᵃ t)

-- Interaction of traversal and interpretation
module _ (𝒫ᴮ : Coalgₚ 𝒫)(𝒜ᵃ : MetaAlg 𝒜)(φ : 𝒫 ⇾̣ 𝒜) where
  open MetaAlg 𝒜ᵃ
  open Coalgₚ 𝒫ᴮ
  open Semantics 𝒜ᵃ
  open Traversal 𝒫ᴮ 𝑎𝑙𝑔 φ 𝑚𝑣𝑎𝑟 using (𝕥𝕣𝕒𝕧 ; 𝕥⟨𝕒⟩ ; 𝕥⟨𝕧⟩ ; 𝕥⟨𝕞⟩)
  open ≡-Reasoning

  -- Traversal with the point of 𝒫 is the same as direct interpretation
  𝕥𝕣𝕒𝕧-η≈𝕤𝕖𝕞 : (φ∘η≈𝑣𝑎𝑟 : ∀{α Γ}{v : ℐ α Γ} → φ (η v) ≡ 𝑣𝑎𝑟 v){t : 𝕋 α Γ}
        → 𝕥𝕣𝕒𝕧 t η ≡ 𝕤𝕖𝕞 t
  𝕥𝕣𝕒𝕧-η≈𝕤𝕖𝕞 φ∘η≈𝑣𝑎𝑟 {t = t} = Semantics.eq 𝒜ᵃ (record
    { ⟨𝑎𝑙𝑔⟩ = λ{ {t = t} → trans 𝕥⟨𝕒⟩ (cong 𝑎𝑙𝑔 (begin
          str 𝒫ᴮ 𝒜 (⅀₁ 𝕥𝕣𝕒𝕧 t) η
      ≡⟨ str-nat₁ (ηᴮ⇒ 𝒫ᴮ) (⅀₁ 𝕥𝕣𝕒𝕧 t) id ⟩
          str ℐᴮ 𝒜 (⅀₁ (λ{ h ρ → h (η ∘ ρ)}) ((⅀₁ 𝕥𝕣𝕒𝕧 t))) id
      ≡⟨ str-unit 𝒜 ((⅀₁ (λ{ h ρ → h (η ∘ ρ)}) ((⅀₁ 𝕥𝕣𝕒𝕧 t)))) ⟩
          ⅀₁ (i 𝒜) (⅀₁ (λ { h ρ → h (η ∘ ρ) }) (⅀₁ 𝕥𝕣𝕒𝕧 t))
      ≡˘⟨ trans ⅀.homomorphism ⅀.homomorphism ⟩
          ⅀₁ (λ t → 𝕥𝕣𝕒𝕧 t η) t
      ∎))}
    ; ⟨𝑣𝑎𝑟⟩ = λ{ {v = v} → trans 𝕥⟨𝕧⟩ φ∘η≈𝑣𝑎𝑟 }
    ; ⟨𝑚𝑣𝑎𝑟⟩ = λ{ {𝔪}{ε} → 𝕥⟨𝕞⟩ } }) 𝕤𝕖𝕞ᵃ⇒ t

-- Traversal with the point of 𝒫 into 𝕋  is the identity
𝕥𝕣𝕒𝕧-η≈id : (𝒫ᴮ : Coalgₚ 𝒫)(open Coalgₚ 𝒫ᴮ)(φ : 𝒫 ⇾̣ 𝕋)
          (φ∘η≈𝑣𝑎𝑟 : ∀{α Γ}{v : ℐ α Γ} → φ (η v) ≡ 𝕧𝕒𝕣 v){t : 𝕋 α Γ}
         → Traversal.𝕥𝕣𝕒𝕧 𝒫ᴮ 𝕒𝕝𝕘 φ 𝕞𝕧𝕒𝕣 t η ≡ t
𝕥𝕣𝕒𝕧-η≈id 𝒫ᴮ φ φ∘η≈𝑣𝑎𝑟 = trans (𝕥𝕣𝕒𝕧-η≈𝕤𝕖𝕞 𝒫ᴮ 𝕋ᵃ φ φ∘η≈𝑣𝑎𝑟) 𝕤𝕖𝕞-id

-- Corollaries for ℐ-parametrised traversals
□𝕥𝕣𝕒𝕧-id≈𝕤𝕖𝕞 : (𝒜ᵃ : MetaAlg 𝒜){t : 𝕋 α Γ}
            → □Traversal.𝕥𝕣𝕒𝕧 𝒜ᵃ t id ≡ Semantics.𝕤𝕖𝕞 𝒜ᵃ t
□𝕥𝕣𝕒𝕧-id≈𝕤𝕖𝕞 𝒜ᵃ {t} = 𝕥𝕣𝕒𝕧-η≈𝕤𝕖𝕞 ℐᴮ 𝒜ᵃ (MetaAlg.𝑣𝑎𝑟 𝒜ᵃ) refl

□𝕥𝕣𝕒𝕧-id≈id : {t : 𝕋 α Γ} → □Traversal.𝕥𝕣𝕒𝕧 𝕋ᵃ t id ≡ t
□𝕥𝕣𝕒𝕧-id≈id = 𝕥𝕣𝕒𝕧-η≈id ℐᴮ 𝕧𝕒𝕣 refl


module DSyntax where

open import Data.List
open import Data.List.All
open import Data.Nat

open import Typing

data DExpr φ : Type → Set where
  var : ∀ {t}
      → (x : t ∈ φ)
      → DExpr φ t

  nat : (unr-φ : All Unr φ)
      → (i : ℕ)
      → DExpr φ TInt

  unit : (unr-φ : All Unr φ)
      → DExpr φ TUnit

  pair : ∀ {φ₁ φ₂ t₁ t₂}
    → (sp : Split φ φ₁ φ₂)
    → (e₁ : DExpr φ₁ t₁)
    → (e₂ : DExpr φ₂ t₂)
    → DExpr φ (TPair t₁ t₂)

  letpair : ∀ {φ₁ φ₂ t₁ t₂ t}
    → (sp : Split φ φ₁ φ₂)
    → (epair : DExpr φ₁ (TPair t₁ t₂))
    → (ebody : DExpr (t₁ ∷ t₂ ∷ φ₂) t)
    → DExpr φ t

  fork : (e : DExpr φ TUnit)
    → DExpr φ TUnit

  new : (unr-φ : All Unr φ)
      → (s : SType)
      → DExpr φ (TPair (TChan (SType.force s)) (TChan (SType.force (dual s))))

  send : ∀ {φ₁ φ₂ s t}
      → (sp : Split φ φ₁ φ₂)
      → (ech : DExpr φ₁ (TChan (send t s)))
      → (earg : DExpr φ₂ t)
      → DExpr φ (TChan (SType.force s))

  recv : ∀ {s t}
      → (ech : DExpr φ (TChan (recv t s)))
      → DExpr φ (TPair (TChan (SType.force s)) t)

  close : (ech : DExpr φ (TChan send!))
      → DExpr φ TUnit

  wait : (ech : DExpr φ (TChan send?))
      → DExpr φ TUnit

  select : ∀ {s₁ s₂}
      → (lab : Selector)
      → (ech : DExpr φ (TChan (sintern s₁ s₂)))
      → DExpr φ (TChan (selection lab (SType.force s₁) (SType.force s₂)))

  branch : ∀ {s₁ s₂ φ₁ φ₂ t}
      → (sp : Split φ φ₁ φ₂)
      → (ech : DExpr φ₁ (TChan (sextern s₁ s₂)))
      → (eleft : DExpr (TChan (SType.force s₁) ∷ φ₂) t)
      → (erght : DExpr (TChan (SType.force s₂) ∷ φ₂) t)
      → DExpr φ t

  ulambda : ∀ {t₁ t₂}
      → (unr-φ : All Unr φ)
      → (ebody : DExpr (t₁ ∷ φ) t₂)
      → DExpr φ (TFun UU t₁ t₂)

  llambda : ∀ {t₁ t₂}
      → (ebody : DExpr (t₁ ∷ φ) t₂)
      → DExpr φ (TFun LL t₁ t₂)

  app : ∀ {φ₁ φ₂ lu t₁ t₂}
      → (sp : Split φ φ₁ φ₂)
      → (efun : DExpr φ₁ (TFun lu t₁ t₂))
      → (earg : DExpr φ₂ t₁)
      → DExpr φ t₂

  subsume : ∀ {t₁ t₂}
      → (e : DExpr φ t₁)
      → (t≤t' : SubT t₁ t₂)
      → DExpr φ t₂