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

open import HoTT
open import homotopy.HSpace renaming (HSpaceStructure to HSS)
open import homotopy.Freudenthal
open import homotopy.IterSuspensionStable
import homotopy.Pi2HSusp as Pi2HSusp
open import homotopy.EM1HSpace
open import homotopy.EilenbergMacLane1

module homotopy.EilenbergMacLane where

-- EM(G,n) when G is π₁(A,a₀)
module EMImplicit {i} {X : Ptd i} {{_ : is-connected 0 (de⊙ X)}}
  {{_ : has-level 1 (de⊙ X)}} (H-X : HSS X) where

  private
    A = de⊙ X
    a₀ = pt X

  ⊙EM : (n : ℕ) → Ptd i
  ⊙EM O = ⊙Ω X
  ⊙EM (S n) = ⊙Trunc ⟨ S n ⟩ (⊙Susp^ n X)

  module _ (n : ℕ) where
    EM = de⊙ (⊙EM n)

  EM-level : (n : ℕ) → has-level ⟨ n ⟩ (EM n)
  EM-level O = ⟨⟩
  EM-level (S n) = ⟨⟩

  instance
    EM-conn : (n : ℕ) → is-connected ⟨ n ⟩ (EM (S n))
    EM-conn n = Trunc-preserves-conn (⊙Susp^-conn' n)

  {-
  π (S k) (EM (S n)) (embase (S n)) == π k (EM n) (embase n)
  where k > 0 and n = S (S n')
  -}
  module Stable (k n : ℕ) (indexing : S k ≤ S (S n))
    where

    private
      SSn : ℕ
      SSn = S (S n)

      lte : ⟨ S k ⟩ ≤T ⟨ SSn ⟩
      lte = ⟨⟩-monotone-≤ $ indexing

      Skle : S k ≤ (S n) *2
      Skle = ≤-trans indexing (lemma n)
        where lemma : (n' : ℕ) → S (S n') ≤ (S n') *2
              lemma O = inl idp
              lemma (S n') = ≤-trans (≤-ap-S (lemma n')) (inr ltS)

    private
      module SS = Susp^StableSucc k (S n) Skle (⊙Susp^ (S n) X) {{⊙Susp^-conn' (S n)}}

    abstract
      stable : πS (S k) (⊙EM (S SSn)) ≃ᴳ πS k (⊙EM SSn)
      stable =
        πS (S k) (⊙EM (S SSn))
          ≃ᴳ⟨ πS-Trunc-fuse-≤-iso _ _ _ (≤T-ap-S lte) ⟩
        πS (S k) (⊙Susp^ SSn X)
          ≃ᴳ⟨ SS.stable ⟩
        πS k (⊙Susp^ (S n) X)
          ≃ᴳ⟨ πS-Trunc-fuse-≤-iso _ _ _ lte ⁻¹ᴳ ⟩
        πS k (⊙EM SSn)
          ≃ᴳ∎

  module BelowDiagonal where

    π₁ : (n : ℕ) → πS 0 (⊙EM (S (S n))) ≃ᴳ 0ᴳ
    π₁ n =
      contr-iso-0ᴳ (πS 0 (⊙EM (S (S n))))
        (connected-at-level-is-contr
          {{raise-level-≤T (≤T-ap-S (≤T-ap-S (-2≤T ⟨ n ⟩₋₂)))
                          (Trunc-level {n = 0})}})

    -- some clutter here arises from the definition of <;
    -- any simple way to avoid this?
    πS-below : (k n : ℕ) → (S k < n)
      → πS k (⊙EM n) ≃ᴳ 0ᴳ
    πS-below 0 .2 ltS = π₁ 0
    πS-below 0 .3 (ltSR ltS) = π₁ 1
    πS-below 0 (S (S n)) (ltSR (ltSR _)) = π₁ n
    πS-below (S k) ._ ltS =
      πS-below k _ ltS
      ∘eᴳ Stable.stable k k (inr ltS)
    πS-below (S k) ._ (ltSR ltS) =
      πS-below k _ (ltSR ltS)
      ∘eᴳ Stable.stable k (S k) (inr (ltSR ltS))
    πS-below (S k) ._ (ltSR (ltSR ltS)) =
      πS-below k _ (ltSR (ltSR ltS))
      ∘eᴳ Stable.stable k (S (S k)) (inr (ltSR (ltSR ltS)))
    πS-below (S k) (S (S (S n))) (ltSR (ltSR (ltSR lt))) =
      πS-below k _ (<-cancel-S (ltSR (ltSR (ltSR lt))))
      ∘eᴳ Stable.stable k n (inr (<-cancel-S (ltSR (ltSR (ltSR lt)))))

  module OnDiagonal where

    π₁ : πS 0 (⊙EM 1) ≃ᴳ πS 0 X
    π₁ = πS-Trunc-fuse-≤-iso 0 1 X ≤T-refl

    private
      module Π₂ = Pi2HSusp H-X

    π₂ : πS 1 (⊙EM 2) ≃ᴳ πS 0 X
    π₂ = Π₂.π₂-Susp
     ∘eᴳ πS-Trunc-fuse-≤-iso 1 2 (⊙Susp X) ≤T-refl

    πS-diag : (n : ℕ) → πS n (⊙EM (S n)) ≃ᴳ πS 0 X
    πS-diag 0 = π₁
    πS-diag 1 = π₂
    πS-diag (S (S n)) = πS-diag (S n)
                    ∘eᴳ Stable.stable (S n) n ≤-refl

  module AboveDiagonal where

    πS-above : ∀ (k n : ℕ) → (n < S k)
      → πS k (⊙EM n) ≃ᴳ 0ᴳ
    πS-above k n lt =
      contr-iso-0ᴳ (πS k (⊙EM n))
        (inhab-prop-is-contr
          [ idp^ (S k) ]
          {{Trunc-preserves-level 0 (Ω^-level -1 (S k) _
            (raise-level-≤T (lemma lt) (EM-level n)))}})
      where lemma : {k n : ℕ} → n < k → ⟨ n ⟩ ≤T (⟨ k ⟩₋₂ +2+ -1)
            lemma ltS = inl (! (+2+-comm _ -1))
            lemma (ltSR lt) = ≤T-trans (lemma lt) (inr ltS)

  module Spectrum where

    private
      module Π₂ = Pi2HSusp H-X

    spectrum0 : ⊙Ω (⊙EM 1) ⊙≃ ⊙EM 0
    spectrum0 =
      ⊙Ω (⊙EM 1)
        ⊙≃⟨ ≃-to-⊙≃ (Trunc=-equiv _ _) idp ⟩
      ⊙Trunc 0 (⊙Ω X)
        ⊙≃⟨ ≃-to-⊙≃ (unTrunc-equiv _) idp ⟩
      ⊙Ω X ⊙≃∎

    spectrum1 : ⊙Ω (⊙EM 2) ⊙≃ ⊙EM 1
    spectrum1 =
      ⊙Ω (⊙EM 2)
        ⊙≃⟨ ≃-to-⊙≃ (Trunc=-equiv _ _) idp ⟩
      ⊙Trunc 1 (⊙Ω (⊙Susp X))
        ⊙≃⟨ Π₂.⊙eq ⟩
      ⊙EM 1 ⊙≃∎

    private
      instance
        sconn : (n : ℕ) → is-connected ⟨ S n ⟩ (de⊙ (⊙Susp^ (S n) X))
        sconn n = ⊙Susp^-conn' (S n)

      kle : (n : ℕ) → ⟨ S (S n) ⟩ ≤T ⟨ n ⟩ +2+ ⟨ n ⟩
      kle O = inl idp
      kle (S n) = ≤T-trans (≤T-ap-S (kle n))
                   (≤T-trans (inl (! (+2+-βr ⟨ n ⟩ ⟨ n ⟩)))
                               (inr ltS))

      module FS (n : ℕ) =
        FreudenthalEquiv ⟨ n ⟩₋₁ ⟨ S (S n) ⟩ (kle n)
          (⊙Susp^ (S n) X)

    spectrumSS : (n : ℕ)
      → ⊙Ω (⊙EM (S (S (S n)))) ⊙≃ ⊙EM (S (S n))
    spectrumSS n =
      ⊙Ω (⊙EM (S (S (S n))))
        ⊙≃⟨ ≃-to-⊙≃ (Trunc=-equiv _ _) idp ⟩
      ⊙Trunc ⟨ S (S n) ⟩ (⊙Ω (⊙Susp^ (S (S n)) X))
        ⊙≃⟨ FS.⊙eq n ⊙⁻¹ ⟩
      ⊙EM (S (S n)) ⊙≃∎

    abstract
      spectrum : (n : ℕ) → ⊙Ω (⊙EM (S n)) ⊙≃ ⊙EM n
      spectrum 0 = spectrum0
      spectrum 1 = spectrum1
      spectrum (S (S n)) = spectrumSS n

module EMExplicit {i} (G : AbGroup i) where
  module HSpace = EM₁HSpace G
  open EMImplicit HSpace.H-⊙EM₁ public

  open BelowDiagonal public using (πS-below)

  πS-diag : (n : ℕ) → πS n (⊙EM (S n)) ≃ᴳ AbGroup.grp G
  πS-diag n = π₁-EM₁ (AbGroup.grp G) ∘eᴳ OnDiagonal.πS-diag n

  open AboveDiagonal public using (πS-above)
  open Spectrum public using (spectrum)


module Tactic.Nat.Subtract.Reflect where

open import Prelude
import Agda.Builtin.Nat as Builtin
open import Builtin.Reflection
open import Control.Monad.State
open import Control.Monad.Transformer
open import Tactic.Reflection
open import Tactic.Reflection.Quote

open import Tactic.Nat.Reflect
open import Tactic.Nat.Subtract.Exp

⟨suc⟩s : SubExp →  SubExp
⟨suc⟩s (lit n) = lit (suc n)
⟨suc⟩s (lit n ⟨+⟩ e) = lit (suc n) ⟨+⟩ e
⟨suc⟩s e = lit 1 ⟨+⟩ e

freshS : Term → R SubExp
freshS t =
  get >>= uncurry′ λ i Γ →
  var i <$ put (suc i , (t , i) ∷ Γ)

pattern _`-_ x y = def (quote Builtin._-_) (vArg x ∷ vArg y ∷ [])

private
  forceInstance : Name → Term → R ⊤
  forceInstance i v = lift $ unify v (def₀ i)
  forceNumber   = forceInstance (quote NumberNat)
  forceSemiring = forceInstance (quote SemiringNat)
  forceSubtractive = forceInstance (quote SubtractiveNat)

termToSubExpR : Term → R SubExp
termToSubExpR (a `+ b) = _⟨+⟩_ <$> termToSubExpR a <*> termToSubExpR b
termToSubExpR (a `* b) = _⟨*⟩_ <$> termToSubExpR a <*> termToSubExpR b
termToSubExpR (a `- b) = _⟨-⟩_ <$> termToSubExpR a <*> termToSubExpR b
-- Handle unsolved instances
termToSubExpR (def (quote Semiring._+_) (_ ∷ _ ∷ vArg i@(meta _ _) ∷ vArg a ∷ vArg b ∷ [])) = do
  forceSemiring i
  ⦇ termToSubExpR a ⟨+⟩ termToSubExpR b ⦈
termToSubExpR (def (quote Semiring._*_) (_ ∷ _ ∷ vArg i@(meta _ _) ∷ vArg a ∷ vArg b ∷ [])) = do
  lift $ unify i (def₀ (quote SemiringNat))
  ⦇ termToSubExpR a ⟨*⟩ termToSubExpR b ⦈
termToSubExpR (def (quote Subtractive._-_) (_ ∷ _ ∷ vArg i@(meta _ _) ∷ vArg a ∷ vArg b ∷ [])) = do
  forceSubtractive i
  ⦇ termToSubExpR a ⟨-⟩ termToSubExpR b ⦈
termToSubExpR (def (quote Semiring.zro) (_ ∷ _ ∷ vArg i@(meta _ _) ∷ [])) = do
  forceSemiring i
  pure (lit 0)
termToSubExpR (def (quote Semiring.one) (_ ∷ _ ∷ vArg i@(meta _ _) ∷ [])) = do
  forceSemiring i
  pure (lit 1)
termToSubExpR (def (quote Number.fromNat) (_ ∷ _ ∷ vArg i@(meta _ _) ∷ vArg a ∷ _ ∷ [])) = do
  forceNumber i
  termToSubExpR a
termToSubExpR `0       = pure (lit 0)
termToSubExpR (`suc a) = ⟨suc⟩s <$> termToSubExpR a
termToSubExpR (meta x _) = lift (blockOnMeta x)
termToSubExpR (lit (nat n)) = pure (lit n)
termToSubExpR unknown  = fail
termToSubExpR t = do
  lift $ ensureNoMetas t
  just i ← gets (flip lookup t ∘ snd)
    where nothing → freshS t
  pure (var i)

termToSubEqR : Term → R (SubExp × SubExp)
termToSubEqR (lhs `≡ rhs) = _,_ <$> termToSubExpR lhs <*> termToSubExpR rhs
termToSubEqR (def (quote _≡_) (_ ∷ hArg (meta x _) ∷ _)) = lift (blockOnMeta x)
termToSubEqR _ = fail

termToSubEq : Term → TC (Maybe ((SubExp × SubExp) × List Term))
termToSubEq t = runR (termToSubEqR t)

pattern _`<_ a b = def (quote LessNat) (vArg a ∷ vArg b ∷ [])

termToSubEqnR : Term → R Eqn
termToSubEqnR (def (quote Ord._<_) (_ ∷ _ ∷ vArg i@(meta _ _) ∷ vArg lhs ∷ vArg rhs ∷ [])) = do
  forceInstance (quote OrdNat) i
  ⦇ termToSubExpR lhs :< termToSubExpR rhs ⦈
termToSubEqnR (lhs `< rhs) = _:<_ <$> termToSubExpR lhs <*> termToSubExpR rhs
termToSubEqnR (lhs `≡ rhs) = _:≡_ <$> termToSubExpR lhs <*> termToSubExpR rhs
termToSubEqnR (def (quote _≡_) (_ ∷ hArg (meta x _) ∷ _)) = lift (blockOnMeta x)
termToSubEqnR t = lift (ensureNoMetas t) *> fail

termToSubEqn : Term → TC (Maybe (Eqn × List Term))
termToSubEqn t = runR (termToSubEqnR t)

private
  lower : Nat → Term → R Term
  lower 0 = pure
  lower i = liftMaybe ∘ strengthen i

termToSubHypsR′ : Nat → Term → R (List Eqn)
termToSubHypsR′ i (pi (vArg hyp) (abs _ a)) =
  _∷_ <$> (termToSubEqnR =<< lower i hyp) <*> termToSubHypsR′ (suc i) a
termToSubHypsR′ _ (meta x _) = lift (blockOnMeta x)
termToSubHypsR′ i a = [_] <$> (termToSubEqnR =<< lower i a)

termToSubHypsR : Term → R (List Eqn)
termToSubHypsR = termToSubHypsR′ 0

termToSubHyps : Term → TC (Maybe (List Eqn × List Term))
termToSubHyps t = runR (termToSubHypsR t)

instance
  QuotableSubExp : Quotable SubExp
  ` {{QuotableSubExp}} (var x) = con (quote SubExp.var) (vArg (` x) ∷ [])
  ` {{QuotableSubExp}} (lit n) = con (quote SubExp.lit) (vArg (` n) ∷ [])
  ` {{QuotableSubExp}} (e ⟨+⟩ e₁) = con (quote SubExp._⟨+⟩_) (map defaultArg $ ` e ∷ ` e₁ ∷ [])
  ` {{QuotableSubExp}} (e ⟨-⟩ e₁) = con (quote SubExp._⟨-⟩_) (map defaultArg $ ` e ∷ ` e₁ ∷ [])
  ` {{QuotableSubExp}} (e ⟨*⟩ e₁) = con (quote SubExp._⟨*⟩_) (map defaultArg $ ` e ∷ ` e₁ ∷ [])

  QuotableEqn : Quotable Eqn
  ` {{QuotableEqn}} (a :≡ b) = con (quote _:≡_) (vArg (` a) ∷ vArg (` b) ∷ [])
  ` {{QuotableEqn}} (a :< b) = con (quote _:<_) (vArg (` a) ∷ vArg (` b) ∷ [])


{-# OPTIONS --safe #-}

module Definition.Typed.Consequences.TypeUnicity where

open import Definition.Untyped hiding (U≢ℕ; U≢Π; U≢ne; ℕ≢Π; ℕ≢ne; Π≢ne; U≢Empty; ℕ≢Empty; Empty≢Π; Empty≢ne)
open import Definition.Untyped.Properties using (subst-Univ-either)
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.Typed.Weakening
open import Definition.Typed.Consequences.Equality
import Definition.Typed.Consequences.Inequality as Ineq
open import Definition.Typed.Consequences.Inversion
open import Definition.Typed.Consequences.Injectivity
open import Definition.Typed.Consequences.NeTypeEq
open import Definition.Typed.Consequences.Syntactic
open import Definition.Typed.Consequences.PiNorm
open import Definition.Typed.Consequences.Substitution
open import Definition.Conversion.Stability

open import Tools.Product
open import Tools.Empty
open import Tools.Sum using (_⊎_; inj₁; inj₂)
import Tools.PropositionalEquality as PE

type-uniq : ∀ {Γ t T₁ T₂ r₁ r₂ l₁ l₂} → Γ ⊢ t ∷ T₁ ^ [ r₁ , l₁ ] → Γ ⊢ t ∷ T₂ ^ [ r₂ , l₂ ] →
                 r₁ PE.≡ r₂ × l₁ PE.≡ l₂ × Γ ⊢ T₁ ≡ T₂ ^ [ r₁ , l₁ ]
type-uniq (univ 0<1 x) (univ 0<1 x') = PE.refl , PE.refl , refl (Ugenⱼ x)
type-uniq (ℕⱼ x) (ℕⱼ x₁) = PE.refl , PE.refl , refl (Ugenⱼ x)
type-uniq (Emptyⱼ x) (Emptyⱼ x₁) = PE.refl , PE.refl , refl (Ugenⱼ x)
type-uniq (Πⱼ x ▹ x₁ ▹ X ▹ X₁) (Πⱼ x₂ ▹ x₃ ▹ Y ▹ Y₁) =
    let _ , _ , eU = type-uniq X₁ Y₁
        er , el = Uinjectivity eU
    in PE.refl , PE.refl , PE.subst (λ r → _ ⊢ _ ≡ Univ r _ ^ _ ) er (refl ((Ugenⱼ (wfTerm Y))) )
type-uniq (∃ⱼ X ▹ X₁) (∃ⱼ Y ▹ Y₁) =
    let _ , enextl , eU = type-uniq X Y
        el = next-inj enextl
    in PE.refl , enextl , PE.subst (λ l → _ ⊢ _ ≡ SProp l ^ _ ) el (refl ((Ugenⱼ (wfTerm Y))) )
type-uniq (var xx x) (var _ y) =
    let T≡T , e = varTypeEq′ x y
        er , el = typelevel-injectivity e
    in er , el , PE.subst (λ A → _ ⊢ _ ≡ A ^ _ ) T≡T (refl (syntacticTerm (var xx x)))
type-uniq (lamⱼ x x₁ x₂ X) (lamⱼ y y₁ y₂ Y) =
    let _ , _ , F≡F = type-uniq (un-univ x₂) (un-univ y₂)
        erF , elF = Uinjectivity F≡F
        erG , elG , G≡G = type-uniq X (PE.subst₂ (λ r l → _ ∙ _ ^ [ r , ι l ] ⊢ _ ∷ _ ^ _) (PE.sym erF) (PE.sym elF) Y)
    in erG , PE.refl , PE.subst₃ (λ rF lF lG → _ ⊢ _ ≡  Π _ ^ rF ° lF ▹ _ ° lG ° _ ^ _) erF elF (ιinj elG)
                                 (univ (Π-cong x x₁ x₂ (refl (un-univ x₂)) (un-univ≡ G≡G)))
type-uniq (X ∘ⱼ X₁) (Y ∘ⱼ Y₁) =
    let er , _ , Π≡Π = type-uniq X Y
        F≡F , erF , elF , elG , G≡G = injectivity Π≡Π
    in er , PE.cong _ elG , (substitutionEq G≡G (substRefl (singleSubst X₁)) (wfTerm X₁))
type-uniq {Γ} ⦅ x , x₁ , X , X₁ ⦆ⱼ (⦅_,_,_,_⦆ⱼ {F = F} {G = G} y y₁ Y Y₁)  =
    let _ , el , F≡F = type-uniq X Y
        _ , el' , G≡G = type-uniq (un-univ x₁) (un-univ (stability (reflConEq (wf x) ∙ sym F≡F)
                                                         (PE.subst (λ l → Γ ∙ F ^ [ % , l ] ⊢ G ^ [ % , l ]) (PE.sym el) y₁)))
    in PE.refl , el , univ (∃-cong x (un-univ≡ F≡F) (un-univ≡ (refl x₁)))
type-uniq (fstⱼ X X₁ X₂) (fstⱼ Y Y₁ Y₂) =
    let er , el , ∃≡∃ = type-uniq X₂ Y₂
        F≡F , _ = ∃injectivity ∃≡∃
    in PE.refl , el , F≡F
type-uniq (sndⱼ X X₁ X₂) (sndⱼ Y Y₁ Y₂) =
    let er , el , ∃≡∃ = type-uniq X₂ Y₂
        F≡F , G≡G = ∃injectivity ∃≡∃
    in PE.refl , el , substitutionEq G≡G (substRefl (singleSubst (fstⱼ X X₁ X₂))) (wfTerm X)
type-uniq (zeroⱼ x) (zeroⱼ x₁) = PE.refl , PE.refl , refl (univ (ℕⱼ x))
type-uniq (sucⱼ X) (sucⱼ Y) = PE.refl , PE.refl , refl (univ (ℕⱼ (wfTerm X)))
type-uniq (natrecⱼ x X X₁ X₂) (natrecⱼ y Y Y₁ Y₂) =
    let _ , _ , U≡U = type-uniq (un-univ x) (un-univ y)
        er , _ = Uinjectivity U≡U
    in er , PE.refl , refl (substitution x (singleSubst X₂) (wfTerm X) )
type-uniq (Emptyrecⱼ x X) (Emptyrecⱼ y Y) =
    let _ , _ , U≡U = type-uniq (un-univ x) (un-univ y)
        er , _ = Uinjectivity U≡U
    in er , PE.refl , refl x
type-uniq (Idⱼ X X₁ X₂) (Idⱼ Y Y₁ Y₂) =
    let _ , enl , U≡U = type-uniq X Y
        el = next-inj enl
    in PE.refl , enl , PE.subst (λ l → _ ⊢ _ ≡ SProp l ^ _) el (refl (Ugenⱼ (wfTerm X)))
type-uniq (Idreflⱼ X) (Idreflⱼ Y) =
    let _ , el , _ = type-uniq X Y
    in PE.refl , el , refl (syntacticTerm (Idreflⱼ X))
type-uniq (transpⱼ x x₁ X X₁ X₂ X₃) (transpⱼ x₂ x₃ Y Y₁ Y₂ Y₃) =
    let _ , el , _ = type-uniq X Y
    in PE.refl , el , refl (substitution x₁ (singleSubst X₂) (wfTerm X))
type-uniq (castⱼ X X₁ X₂ X₃) (castⱼ Y Y₁ Y₂ Y₃) =
    let er , _ , _ = type-uniq X₃ Y₃
    in er , PE.refl , refl (univ X₁)
type-uniq (castreflⱼ X X₁) (castreflⱼ Y Y₁) = PE.refl , PE.refl , refl (syntacticTerm (castreflⱼ X X₁))
type-uniq (conv X x) Y = let er , el , eA = type-uniq X Y in er , el , trans (sym x) eA
type-uniq X (conv Y y) =
    let er , el , eA = type-uniq X Y
    in er , el , trans eA (PE.subst₂ (λ r l → _ ⊢ _ ≡ _ ^ [ r , l ]) (PE.sym er) (PE.sym el) y)


data D : Set where
  @0 c : D

-- The following definition should be rejected. Matching on an erased
-- constructor in an *erased* position should not on its own make it
-- possible to use erased definitions in the right-hand side.

f : @0 D → D
f c = c

x : D
x = f c


-- Andreas, 2015-03-17

open import Common.Size

data ⊥ : Set where

data D (i : Size) : Set where
  c : Size< i → D i

-- This definition of size predecessor should be forbidden...
module _ (i : Size) where
  postulate
    pred : Size< i

-- ...otherwise the injectivity test loops here.
iter : ∀ i → D i → ⊥
iter i (c j) = iter j (c (pred j))

loop : Size → ⊥
loop i = iter i (c (pred i))

absurd : ⊥
absurd = FIXME loop ∞

-- Testcase temporarily mutilated, original error
-- -Issue1428c.agda:13,5-19
-- -We don't like postulated sizes in parametrized modules.
-- +Issue1428c.agda:23,10-15
-- +Not in scope:
-- +  FIXME at Issue1428c.agda:23,10-15
-- +when scope checking FIXME


open import Data.Product using ( proj₁ ; proj₂ )
open import Data.Sum using ( _⊎_ ; inj₁ ; inj₂ )
open import Relation.Binary.PropositionalEquality using ( refl )
open import Web.Semantic.DL.ABox.Interp using ( ⌊_⌋ ; ind ; _*_ )
open import Web.Semantic.DL.ABox.Interp.Morphism using ( _,_ )
open import Web.Semantic.DL.ABox.Model using
  ( _⊨a_ ; on-bnode ; bnodes ; _,_ ; ⊨a-resp-≲ )
open import Web.Semantic.DL.Category.Composition using ( _∙_ )
open import Web.Semantic.DL.Category.Properties.Composition.Lemmas using
  ( compose-left ; compose-right ; compose-resp-⊨a )
open import Web.Semantic.DL.Category.Properties.Tensor.Lemmas using
  ( tensor-up ; tensor-down ; tensor-resp-⊨a )
open import Web.Semantic.DL.Category.Object using ( Object ; IN ; fin )
open import Web.Semantic.DL.Category.Morphism using
  ( _⇒_ ; BN ; impl ; _⊑_ ; _≣_ ; _,_ )
open import Web.Semantic.DL.Category.Tensor using ( _⊗_ ; _⟨⊗⟩_ )
open import Web.Semantic.DL.Category.Unit using ( I )
open import Web.Semantic.DL.Category.Wiring using
  ( wires-≈ ; wires-≈⁻¹ ; assoc )
open import Web.Semantic.DL.Signature using ( Signature )
open import Web.Semantic.DL.TBox using ( TBox )
open import Web.Semantic.DL.TBox.Interp using
  ( Δ ; _⊨_≈_ ; ≈-refl ; ≈-refl′ ; ≈-sym )
open import Web.Semantic.DL.TBox.Interp.Morphism using ( ≲-refl )
open import Web.Semantic.Util using
  ( _∘_ ; False ; ⊎-assoc ; ⊎-assoc⁻¹
  ; _⊕_⊕_ ; inode ; bnode ; enode ; left ; right ; up ; down )

module Web.Semantic.DL.Category.Properties.Tensor.AssocNatural
  {Σ : Signature} {S T : TBox Σ} where

assoc-natural : ∀ {A₁ A₂ A₃ B₁ B₂ B₃ : Object S T}
  (F₁ : A₁ ⇒ B₁) (F₂ : A₂ ⇒ B₂) (F₃ : A₃ ⇒ B₃) →
    (((F₁ ⟨⊗⟩ F₂) ⟨⊗⟩ F₃) ∙ assoc B₁ B₂ B₃ ≣
      assoc A₁ A₂ A₃ ∙ (F₁ ⟨⊗⟩ (F₂ ⟨⊗⟩ F₃)))
assoc-natural {A₁} {A₂} {A₃} {B₁} {B₂} {B₃} F₁ F₂ F₃ = (LHS⊑RHS , RHS⊑LHS) where

  LHS⊑RHS : ((F₁ ⟨⊗⟩ F₂) ⟨⊗⟩ F₃) ∙ assoc B₁ B₂ B₃ ⊑
    assoc A₁ A₂ A₃ ∙ (F₁ ⟨⊗⟩ (F₂ ⟨⊗⟩ F₃))
  LHS⊑RHS J J⊨STA J⊨LHS = (f , J⊨RHS) where

    f : (False ⊕ (IN A₁ ⊎ (IN A₂ ⊎ IN A₃)) ⊕ (BN F₁ ⊎ (BN F₂ ⊎ BN F₃))) → Δ ⌊ J ⌋
    f (inode ())
    f (bnode x) = ind J (inode (⊎-assoc⁻¹ x))
    f (enode v) = ind J (bnode (inode (⊎-assoc⁻¹ v)))

    lemma₁ : ∀ x →
      ⌊ J ⌋ ⊨ ind J (left (up (up x))) ≈
        on-bnode f (ind J) (right (up x))
    lemma₁ (inode x) = ≈-refl ⌊ J ⌋
    lemma₁ (bnode v) = ≈-refl ⌊ J ⌋
    lemma₁ (enode y) = wires-≈ ⊎-assoc⁻¹
      (proj₂ (fin (B₁ ⊗ (B₂ ⊗ B₃))) (inj₁ y))
      (compose-right ((F₁ ⟨⊗⟩ F₂) ⟨⊗⟩ F₃) (assoc B₁ B₂ B₃) J J⊨LHS)

    lemma₂ : ∀ x →
      ⌊ J ⌋ ⊨ ind J (left (up (down x))) ≈
        on-bnode f (ind J) (right (down (up x)))
    lemma₂ (inode x) = ≈-refl ⌊ J ⌋
    lemma₂ (bnode v) = ≈-refl ⌊ J ⌋
    lemma₂ (enode y) = wires-≈ ⊎-assoc⁻¹
      (proj₂ (fin (B₁ ⊗ (B₂ ⊗ B₃))) (inj₂ (inj₁ y)))
      (compose-right ((F₁ ⟨⊗⟩ F₂) ⟨⊗⟩ F₃) (assoc B₁ B₂ B₃) J J⊨LHS)

    lemma₃ : ∀ x →
      ⌊ J ⌋ ⊨ ind J (left (down x)) ≈
        on-bnode f (ind J) (right (down (down x)))
    lemma₃ (inode x) = ≈-refl ⌊ J ⌋
    lemma₃ (bnode v) = ≈-refl ⌊ J ⌋
    lemma₃ (enode y) = wires-≈ ⊎-assoc⁻¹
      (proj₂ (fin (B₁ ⊗ (B₂ ⊗ B₃))) (inj₂ (inj₂ y)))
      (compose-right ((F₁ ⟨⊗⟩ F₂) ⟨⊗⟩ F₃) (assoc B₁ B₂ B₃) J J⊨LHS)

    J⊨RHS : bnodes J f ⊨a impl (assoc A₁ A₂ A₃ ∙ (F₁ ⟨⊗⟩ (F₂ ⟨⊗⟩ F₃)))
    J⊨RHS = compose-resp-⊨a (assoc A₁ A₂ A₃) (F₁ ⟨⊗⟩ (F₂ ⟨⊗⟩ F₃)) (bnodes J f)
      (wires-≈⁻¹ ⊎-assoc⁻¹ (λ x → ≈-refl ⌊ J ⌋) (proj₁ (fin (A₁ ⊗ (A₂ ⊗ A₃)))))
      (tensor-resp-⊨a F₁ (F₂ ⟨⊗⟩ F₃) (right * bnodes J f)
        (⊨a-resp-≲ (≲-refl ⌊ J ⌋ , lemma₁) (impl F₁)
          (tensor-up F₁ F₂ (up * left * J)
            (tensor-up (F₁ ⟨⊗⟩ F₂) F₃ (left * J)
              (compose-left ((F₁ ⟨⊗⟩ F₂) ⟨⊗⟩ F₃) (assoc B₁ B₂ B₃) J J⊨LHS))))
        (tensor-resp-⊨a F₂ F₃ (down * right * bnodes J f)
          (⊨a-resp-≲ (≲-refl ⌊ J ⌋ , lemma₂) (impl F₂)
            (tensor-down F₁ F₂ (up * left * J)
              (tensor-up (F₁ ⟨⊗⟩ F₂) F₃ (left * J)
                (compose-left ((F₁ ⟨⊗⟩ F₂) ⟨⊗⟩ F₃) (assoc B₁ B₂ B₃) J J⊨LHS))))
          (⊨a-resp-≲ (≲-refl ⌊ J ⌋ , lemma₃) (impl F₃)
            (tensor-down (F₁ ⟨⊗⟩ F₂) F₃ (left * J)
              (compose-left ((F₁ ⟨⊗⟩ F₂) ⟨⊗⟩ F₃) (assoc B₁ B₂ B₃) J J⊨LHS)))))

  RHS⊑LHS : assoc A₁ A₂ A₃ ∙ (F₁ ⟨⊗⟩ (F₂ ⟨⊗⟩ F₃)) ⊑
    ((F₁ ⟨⊗⟩ F₂) ⟨⊗⟩ F₃) ∙ assoc B₁ B₂ B₃
  RHS⊑LHS J J⊨STA J⊨RHS = (f , J⊨LHS) where

    f : (((BN F₁ ⊎ BN F₂) ⊎ BN F₃) ⊕ ((IN B₁ ⊎ IN B₂) ⊎ IN B₃) ⊕ False) → Δ ⌊ J ⌋
    f (inode v) = ind J (bnode (enode (⊎-assoc v)))
    f (bnode y) = ind J (enode (⊎-assoc y))
    f (enode ())

    lemma₀ : ∀ x →
      ⌊ J ⌋ ⊨ ind J (enode (⊎-assoc (⊎-assoc⁻¹ x))) ≈ ind J (enode x)
    lemma₀ (inj₁ x) = ≈-refl ⌊ J ⌋
    lemma₀ (inj₂ (inj₁ x)) = ≈-refl ⌊ J ⌋
    lemma₀ (inj₂ (inj₂ y)) = ≈-refl ⌊ J ⌋

    lemma₁ : ∀ x → ⌊ J ⌋ ⊨ ind J (right (up x)) ≈
      on-bnode f (ind J) (left (up (up x)))
    lemma₁ (inode x) = ≈-sym ⌊ J ⌋ (wires-≈ ⊎-assoc⁻¹
      (proj₂ (fin (A₁ ⊗ (A₂ ⊗ A₃))) (inj₁ x))
      (compose-left (assoc A₁ A₂ A₃) (F₁ ⟨⊗⟩ (F₂ ⟨⊗⟩ F₃)) J J⊨RHS))
    lemma₁ (bnode v) = ≈-refl ⌊ J ⌋
    lemma₁ (enode y) = ≈-refl ⌊ J ⌋

    lemma₂ : ∀ x → ⌊ J ⌋ ⊨ ind J (right (down (up x))) ≈
      on-bnode f (ind J) (left (up (down x)))
    lemma₂ (inode x) = ≈-sym ⌊ J ⌋ (wires-≈ ⊎-assoc⁻¹
      (proj₂ (fin (A₁ ⊗ (A₂ ⊗ A₃))) (inj₂ (inj₁ x)))
      (compose-left (assoc A₁ A₂ A₃) (F₁ ⟨⊗⟩ (F₂ ⟨⊗⟩ F₃)) J J⊨RHS))
    lemma₂ (bnode v) = ≈-refl ⌊ J ⌋
    lemma₂ (enode y) = ≈-refl ⌊ J ⌋

    lemma₃ : ∀ x → ⌊ J ⌋ ⊨ ind J (right (down (down x))) ≈
      on-bnode f (ind J) (left (down x))
    lemma₃ (inode x) = ≈-sym ⌊ J ⌋ (wires-≈ ⊎-assoc⁻¹
      (proj₂ (fin (A₁ ⊗ (A₂ ⊗ A₃))) (inj₂ (inj₂ x)))
      (compose-left (assoc A₁ A₂ A₃) (F₁ ⟨⊗⟩ (F₂ ⟨⊗⟩ F₃)) J J⊨RHS))
    lemma₃ (bnode v) = ≈-refl ⌊ J ⌋
    lemma₃ (enode y) = ≈-refl ⌊ J ⌋

    J⊨LHS : bnodes J f ⊨a impl (((F₁ ⟨⊗⟩ F₂) ⟨⊗⟩ F₃) ∙ assoc B₁ B₂ B₃)
    J⊨LHS = compose-resp-⊨a ((F₁ ⟨⊗⟩ F₂) ⟨⊗⟩ F₃) (assoc B₁ B₂ B₃) (bnodes J f)
      (tensor-resp-⊨a (F₁ ⟨⊗⟩ F₂) F₃ (left * bnodes J f)
        (tensor-resp-⊨a F₁ F₂ (up * left * bnodes J f)
          (⊨a-resp-≲ (≲-refl ⌊ J ⌋ , lemma₁) (impl F₁)
            (tensor-up F₁ (F₂ ⟨⊗⟩ F₃) (right * J)
              (compose-right (assoc A₁ A₂ A₃) (F₁ ⟨⊗⟩ (F₂ ⟨⊗⟩ F₃)) J J⊨RHS)))
          (⊨a-resp-≲ (≲-refl ⌊ J ⌋ , lemma₂) (impl F₂)
            (tensor-up F₂ F₃ (down * right * J)
              (tensor-down F₁ (F₂ ⟨⊗⟩ F₃) (right * J)
                (compose-right (assoc A₁ A₂ A₃) (F₁ ⟨⊗⟩ (F₂ ⟨⊗⟩ F₃)) J J⊨RHS)))))
       (⊨a-resp-≲ (≲-refl ⌊ J ⌋ , lemma₃) (impl F₃)
         (tensor-down F₂ F₃ (down * right * J)
           (tensor-down F₁ (F₂ ⟨⊗⟩ F₃) (right * J)
             (compose-right (assoc A₁ A₂ A₃) (F₁ ⟨⊗⟩ (F₂ ⟨⊗⟩ F₃)) J J⊨RHS)))))
      (wires-≈⁻¹ ⊎-assoc⁻¹ lemma₀ (proj₁ (fin (B₁ ⊗ (B₂ ⊗ B₃)))))


-- 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 #-}

open import Data.Product
open import Data.Empty
open import Data.Sum
open import Data.Vec
open import Data.Fin
open import Data.Bool renaming (Bool to 𝔹)
open import Relation.Unary using (_∈_; _⊆_; _∉_)
open import Relation.Binary.Definitions
open import Relation.Binary.Construct.Closure.ReflexiveTransitive
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary

open import Common

open import is-lib.InfSys

module FairCompliance-IS {𝕋 : Set} (message : Message 𝕋) where

  open import SessionType message
  open import Session message
  open import Transitions message

  private
    U : Set
    U = SessionType × SessionType

  data FCompIS-RN : Set where
    client-end : FCompIS-RN
    oi io : FCompIS-RN

  data FCompCOIS-RN : Set where
    co-oi co-io : FCompCOIS-RN

  client-end-r : FinMetaRule U
  client-end-r .Ctx = Σ[ (S , T) ∈ SessionType × SessionType ] Win S × Defined T
  client-end-r .comp ((S , T) , _) =
    [] ,
    --------------------
    (S , T)

  OI-r : MetaRule U
  OI-r .Ctx = Σ[ (f , _) ∈ Continuation × Continuation ] Witness f
  OI-r .Pos ((f , _) , _) = Σ[ t ∈ 𝕋 ] t ∈ dom f
  OI-r .prems ((f , g) , _) (t , _) = f t .force , g t .force
  OI-r .conclu ((f , g) , _) = out f , inp g

  IO-r : MetaRule U
  IO-r .Ctx = Σ[ (_ , g) ∈ Continuation × Continuation ] Witness g
  IO-r .Pos ((_ , g) , _) = Σ[ t ∈ 𝕋 ] t ∈ dom g
  IO-r .prems ((f , g) , _) (t , _) = f t .force , g t .force
  IO-r .conclu ((f , g) , _) = inp f , out g

  co-OI-r : FinMetaRule U
  co-OI-r .Ctx = Σ[ (f , _ , x) ∈ Continuation × Continuation × 𝕋 ] x ∈ dom f
  co-OI-r .comp ((f , g , x) , _) =
    (f x .force , g x .force) ∷ [] ,
    --------------------
    (out f , inp g)

  co-IO-r : FinMetaRule U
  co-IO-r .Ctx = Σ[ (_ , g , x) ∈ Continuation × Continuation × 𝕋 ] x ∈ dom g
  co-IO-r .comp ((f , g , x) , _) =
    (f x .force , g x .force) ∷ [] ,
    --------------------
    (inp f , out g)

  FCompIS : IS U
  FCompIS .Names = FCompIS-RN
  FCompIS .rules client-end = from client-end-r
  FCompIS .rules oi = OI-r
  FCompIS .rules io = IO-r

  FCompCOIS : IS U
  FCompCOIS .Names = FCompCOIS-RN
  FCompCOIS .rules co-oi = from co-OI-r
  FCompCOIS .rules co-io = from co-IO-r

  _⊢_ : SessionType → SessionType → Set
  S ⊢ T = FCoInd⟦ FCompIS , FCompCOIS ⟧ (S , T)

  _⊢ᵢ_ : SessionType → SessionType → Set
  S ⊢ᵢ T = Ind⟦ FCompIS ∪ FCompCOIS ⟧ (S , T)

  {- Properties -}

  ¬nil-⊢ : ∀{S} → ¬ (nil ⊢ S)
  ¬nil-⊢ fc with fc .CoInd⟦_⟧.unfold
  ... | client-end , ((_ , (() , _)) , _) , refl , _

  ⊢ᵢ->succeed : ∀{S T} → S ⊢ᵢ T → MaySucceed (S # T)
  ⊢ᵢ->succeed (fold (inj₁ client-end , ((S , T) , (win , def)) , refl , _)) = (S # T) , ε , win#def win def
  ⊢ᵢ->succeed (fold (inj₁ oi , ((f , g) , (t , ok)) , refl , pr)) =
    let Sys' , red-Sys' , Succ = ⊢ᵢ->succeed (pr (t , ok)) in
    Sys' , sync (out ok) inp ◅ red-Sys' , Succ
  ⊢ᵢ->succeed (fold (inj₁ io , ((f , g) , (t , ok)) , refl , pr)) =
    let Sys' , red-Sys' , Succ = ⊢ᵢ->succeed (pr (t , ok)) in
    Sys' , sync inp (out ok) ◅ red-Sys' , Succ
  ⊢ᵢ->succeed (fold (inj₂ co-oi , (_ , ok-b) , refl , pr)) =
    let Sys' , red-Sys' , Succ = ⊢ᵢ->succeed (pr zero) in
    Sys' , sync (out ok-b) inp ◅ red-Sys' , Succ
  ⊢ᵢ->succeed (fold (inj₂ co-io , (_ , ok-b) , refl , pr)) =
    let Sys' , red-Sys' , Succ = ⊢ᵢ->succeed (pr zero) in
    Sys' , sync inp (out ok-b) ◅ red-Sys' , Succ

  maysucceed->⊢ᵢ : ∀{S T Sys} → Reductions (S # T) Sys → Success Sys → S ⊢ᵢ T
  maysucceed->⊢ᵢ ε (win#def win def) = apply-ind (inj₁ client-end) (_ , (win , def)) λ ()
  maysucceed->⊢ᵢ (sync (out ok) inp ◅ red) Succ =
    let rec = maysucceed->⊢ᵢ red Succ in
    apply-ind (inj₂ co-oi) (_ , ok) λ{zero → rec}
  maysucceed->⊢ᵢ (sync inp (out ok) ◅ red) Succ =
    let rec = maysucceed->⊢ᵢ red Succ in
    apply-ind (inj₂ co-io) (_ , ok) λ{zero → rec}

  {- Soundness -}

  fc-sound : ∀{S T} → S ⊢ T → FairComplianceS (S # T)
  fc-sound fc ε = ⊢ᵢ->succeed (fcoind-to-ind fc)
  fc-sound fc (x ◅ red) with fc .CoInd⟦_⟧.unfold
  ... | client-end , ((_ , (win , def)) , _) , refl , _ = ⊥-elim (success-not-reduce (win#def win def) x)
  fc-sound fc (sync (out ok) (inp {x = t}) ◅ red) | oi , (((f , _) , _) , _) , refl , pr = fc-sound (pr (t , ok)) red
  fc-sound fc (sync (inp {x = t}) (out ok) ◅ red) | io , (((_ , g) , _) , _) , refl , pr = fc-sound (pr (t , ok)) red

  {- Boundedness -}

  fc-bounded : ∀{S T} → FairComplianceS (S # T) → S ⊢ᵢ T
  fc-bounded fc =
    let _ , red-Sys , Succ = fc ε in
    maysucceed->⊢ᵢ red-Sys Succ

  {- Consistency -}

  fc-consistent : ∀{S T} → FairComplianceS (S # T) → ISF[ FCompIS ] (λ{(S , T) → FairComplianceS (S # T)}) (S , T)
  fc-consistent fc with fc ε
  ... | _ , ε , win#def win def = client-end , (_ , (win , def)) , refl , λ ()
  ... | _ , sync (out ok) (inp {x = t}) ◅ _ , _ =
    oi , (_ , (t , ok)) , refl , λ (t' , ok') red → fc (sync (out ok') (inp {x = t'}) ◅ red)
  ... | _ , sync (inp {x = t}) (out ok) ◅ _ , _ =
    io , (_ , (t , ok)) , refl , λ (t' , ok') red → fc (sync (inp {x = t'}) (out ok') ◅ red)

  {- Completeness -}

  fc-complete : ∀{S T} → FairComplianceS (S # T) → S ⊢ T
  fc-complete =
    let S = λ{(S , T) → FairComplianceS (S # T)} in
    bounded-coind[ FCompIS , FCompCOIS ] S fc-bounded fc-consistent

  {- Properties -}

  fci->defined : ∀{S T} → S ⊢ᵢ T → Defined S × Defined T
  fci->defined (fold (inj₁ client-end , (_ , (out _ , def)) , refl , _)) = out , def
  fci->defined (fold (inj₁ oi , _ , refl , _)) = out , inp
  fci->defined (fold (inj₁ io , _ , refl , _)) = inp , out
  fci->defined (fold (inj₂ co-oi , _ , refl , _)) = out , inp
  fci->defined (fold (inj₂ co-io , _ , refl , _)) = inp , out

  fc->defined : ∀{S T} → S ⊢ T → Defined S × Defined T
  fc->defined fc = fci->defined (fcoind-to-ind fc)

  no-fc-with-nil : ∀{S} → ¬ (S ⊢ nil)
  no-fc-with-nil fc with fc .CoInd⟦_⟧.unfold
  ... | client-end , ((_ , (_ , ())) , _) , refl , _

  {- Auxiliary for FS -}

  win⊢def : ∀{S} → Defined S → win ⊢ S
  win⊢def ok = apply-fcoind client-end (_ , (Win-win , ok)) λ ()

  ⊢-after-out : ∀{f g t} → t ∈ dom f → out f ⊢ inp g → f t .force ⊢ g t .force
  ⊢-after-out ok fc with fc .CoInd⟦_⟧.unfold
  ... | client-end , ((_ , (out e , _)) , _) , refl , _ = ⊥-elim (e _ ok)
  ... | oi , _ , refl , pr = pr (_ , ok)

  ⊢-after-in : ∀{f g t} → t ∈ dom g → inp f ⊢ out g → f t .force ⊢ g t .force
  ⊢-after-in ok fc with fc .CoInd⟦_⟧.unfold
  ... | client-end , ((_ , (() , _)) , _) , refl , _
  ... | io , _ , refl , pr = pr (_ , ok)


{-# OPTIONS --safe #-}

module Definition.Typed.EqualityRelation where

open import Definition.Untyped
open import Definition.Typed
open import Definition.Typed.Weakening using (_∷_⊆_)

-- Generic equality relation used with the logical relation

record EqRelSet : Set₁ where
  constructor eqRel
  field
    ---------------
    -- Relations --
    ---------------

    -- Equality of types
    _⊢_≅_^_   : Con Term → (A B : Term) → TypeInfo → Set

    -- Equality of terms
    _⊢_≅_∷_^_ : Con Term → (t u A : Term) → TypeInfo → Set

    -- Equality of neutral terms
    _⊢_~_∷_^_ : Con Term → (t u A : Term) → TypeInfo → Set

    ----------------
    -- Properties --
    ----------------

    -- Generic equality compatibility
    ~-to-≅ₜ : ∀ {k l A r Γ}
            → Γ ⊢ k ~ l ∷ A ^ r
            → Γ ⊢ k ≅ l ∷ A ^ r

    -- Judgmental conversion compatibility
    ≅-eq  : ∀ {A B r Γ}
          → Γ ⊢ A ≅ B ^ r
          → Γ ⊢ A ≡ B ^ r
    ≅ₜ-eq : ∀ {t u A r Γ}
          → Γ ⊢ t ≅ u ∷ A ^ r
          → Γ ⊢ t ≡ u ∷ A ^ r

    -- Universe
    ≅-univ : ∀ {A B r l Γ}
           → Γ ⊢ A ≅ B ∷ (Univ r l) ^ [ ! , next l ]
           → Γ ⊢ A ≅ B ^ [ r , ι l ]

    ≅-un-univ : ∀ {A B r l Γ}
           → Γ ⊢ A ≅ B ^ [ r , ι l ]
           → Γ ⊢ A ≅ B ∷ (Univ r l) ^ [ ! , next l ]

    -- Symmetry
    ≅-sym  : ∀ {A B Γ r} → Γ ⊢ A ≅ B ^ r → Γ ⊢ B ≅ A ^ r
    ≅ₜ-sym : ∀ {t u A r Γ} → Γ ⊢ t ≅ u ∷ A ^ r → Γ ⊢ u ≅ t ∷ A ^ r
    ~-sym  : ∀ {k l A Γ r} → Γ ⊢ k ~ l ∷ A ^ r → Γ ⊢ l ~ k ∷ A ^ r

    -- Transitivity
    ≅-trans  : ∀ {A B C r Γ} → Γ ⊢ A ≅ B ^ r → Γ ⊢ B ≅ C ^ r → Γ ⊢ A ≅ C ^ r
    ≅ₜ-trans : ∀ {t u v A r Γ} → Γ ⊢ t ≅ u ∷ A ^ r → Γ ⊢ u ≅ v ∷ A ^ r → Γ ⊢ t ≅ v ∷ A ^ r
    ~-trans  : ∀ {k l m A r Γ} → Γ ⊢ k ~ l ∷ A ^ r → Γ ⊢ l ~ m ∷ A ^ r → Γ ⊢ k ~ m ∷ A ^ r

    -- Conversion
    ≅-conv : ∀ {t u A B r Γ} → Γ ⊢ t ≅ u ∷ A ^ r → Γ ⊢ A ≡ B ^ r → Γ ⊢ t ≅ u ∷ B ^ r
    ~-conv : ∀ {k l A B r Γ} → Γ ⊢ k ~ l ∷ A ^ r → Γ ⊢ A ≡ B ^ r → Γ ⊢ k ~ l ∷ B ^ r

    -- Weakening
    ≅-wk  : ∀ {A B r ρ Γ Δ}
          → ρ ∷ Δ ⊆ Γ
          → ⊢ Δ
          → Γ ⊢ A ≅ B ^ r
          → Δ ⊢ wk ρ A ≅ wk ρ B ^ r
    ≅ₜ-wk : ∀ {t u A r ρ Γ Δ}
          → ρ ∷ Δ ⊆ Γ
          → ⊢ Δ
          → Γ ⊢ t ≅ u ∷ A ^ r
          → Δ ⊢ wk ρ t ≅ wk ρ u ∷ wk ρ A ^ r
    ~-wk  : ∀ {k l A r ρ Γ Δ}
          → ρ ∷ Δ ⊆ Γ
          → ⊢ Δ
          → Γ ⊢ k ~ l ∷ A ^ r
          → Δ ⊢ wk ρ k ~ wk ρ l ∷ wk ρ A ^ r

    -- Weak head expansion
    ≅-red : ∀ {A A′ B B′ r Γ}
          → Γ ⊢ A ⇒* A′ ^ r
          → Γ ⊢ B ⇒* B′ ^ r
          → Whnf A′
          → Whnf B′
          → Γ ⊢ A′ ≅ B′ ^ r
          → Γ ⊢ A  ≅ B ^ r

    ≅ₜ-red : ∀ {a a′ b b′ A B l Γ}
           → Γ ⊢ A ⇒* B ^ [ ! , l ]
           → Γ ⊢ a ⇒* a′ ∷ B ^ l
           → Γ ⊢ b ⇒* b′ ∷ B ^ l
           → Whnf B
           → Whnf a′
           → Whnf b′
           → Γ ⊢ a′ ≅ b′ ∷ B ^ [ ! , l ]
           → Γ ⊢ a  ≅ b  ∷ A ^ [ ! , l ]

    -- Large universe type reflexivity
    ≅-U¹refl   : ∀ {r Γ} → ⊢ Γ → Γ ⊢ (Univ r ¹) ≅ (Univ r ¹) ^ [ ! , ∞ ]

    -- Small universe type reflexivity
    ≅-U⁰refl   : ∀ {r Γ} → ⊢ Γ → Γ ⊢ (Univ r ⁰) ≅ (Univ r ⁰) ∷ U ¹ ^ [ ! , ∞ ]

    -- Natural number type reflexivity
    ≅ₜ-ℕrefl  : ∀ {Γ} → ⊢ Γ → Γ ⊢ ℕ ≅ ℕ ∷ U ⁰ ^ [ ! , next ⁰ ]

    -- Empty type reflexivity
    ≅ₜ-Emptyrefl  : ∀ {Γ l} → ⊢ Γ → Γ ⊢ Empty l ≅ Empty l ∷ SProp l ^ [ ! , next l ]

    -- Π-congruence
    ≅ₜ-Π-cong : ∀ {F G H E rF lF r lG l Γ}
              → lF ≤ l
              → lG ≤ l
              → Γ ⊢ F ^ [ rF , ι lF ]
              → Γ ⊢ F ≅ H ∷ (Univ rF lF) ^ [ ! , next lF ]
              → Γ ∙ F ^ [ rF , ι lF ] ⊢ G ≅ E ∷ (Univ r lG) ^ [ ! , next lG ]
              → Γ ⊢ Π F ^ rF ° lF ▹ G ° lG ° l ≅ Π H ^ rF ° lF ▹ E ° lG ° l ∷ (Univ r l) ^ [ ! , next l ]

    -- ∃-congruence
    -- Since ∃ types are always small, no need for a type-level rule
    ≅ₜ-∃-cong : ∀ {F G H E l Γ}
              → Γ ⊢ F ^ [ % , ι l ]
              → Γ ⊢ F ≅ H ∷ SProp l ^ [ ! , next l ]
              → Γ ∙ F ^ [ % , ι l ] ⊢ G ≅ E ∷ SProp l ^ [ ! , next l ]
              → Γ ⊢ ∃ F ▹ G ≅ ∃ H ▹ E ∷ SProp l ^ [ ! , next l ]

    -- Zero reflexivity
    ≅ₜ-zerorefl : ∀ {Γ} → ⊢ Γ → Γ ⊢ zero ≅ zero ∷ ℕ ^ [ ! , ι ⁰ ]

    -- Successor congruence
    ≅-suc-cong : ∀ {m n Γ} → Γ ⊢ m ≅ n ∷ ℕ ^ [ ! , ι ⁰ ] → Γ ⊢ suc m ≅ suc n ∷ ℕ ^ [ ! , ι ⁰ ]

    -- η-equality
    ≅-η-eq : ∀ {f g F G rF lF lG l Γ}
              → lF ≤ l
              → lG ≤ l
              → Γ ⊢ F ^ [ rF , ι lF ]
              → Γ ⊢ f ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ ! , ι l ]
              → Γ ⊢ g ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ ! , ι l ]
              → Function f
              → Function g
              → Γ ∙ F ^ [ rF , ι lF ] ⊢ wk1 f ∘ var 0 ^ l ≅ wk1 g ∘ var 0 ^ l ∷ G ^ [ ! , ι lG ]
              → Γ ⊢ f ≅ g ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ ! , ι l ]

    -- Variable reflexivity
    ~-var : ∀ {x A Γ r} → Γ ⊢ var x ∷ A ^ r → Γ ⊢ var x ~ var x ∷ A ^ r

    -- Application congurence
    ~-app : ∀ {a b f g F G rF lF lG l Γ}
          → Γ ⊢ f ~ g ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ ! , ι l ]
          → Γ ⊢ a ≅ b ∷ F ^ [ rF , ι lF ]
          → Γ ⊢ f ∘ a ^ l ~ g ∘ b ^ l ∷ G [ a ] ^ [ ! , ι lG ]

    -- Natural recursion congurence
    ~-natrec : ∀ {z z′ s s′ n n′ F F′ l Γ}
             → Γ ∙ ℕ ^ [ ! , ι ⁰ ] ⊢ F ≅ F′ ^ [ ! , ι l ]
             → Γ     ⊢ z ≅ z′ ∷ F [ zero ] ^ [ ! , ι l ]
             → Γ     ⊢ s ≅ s′ ∷ Π ℕ ^ ! ° ⁰ ▹ (F ^ ! ° l ▹▹ F [ suc (var 0) ]↑ ° l ° l ) ° l ° l ^ [ ! , ι l ]
             → Γ     ⊢ n ~ n′ ∷ ℕ ^ [ ! , ι ⁰ ]
             → Γ     ⊢ natrec l F z s n ~ natrec l F′ z′ s′ n′ ∷ F [ n ] ^ [ ! , ι l ]

    -- Empty recursion congurence
    ~-Emptyrec : ∀ {e e′ F F′ l lEmpty Γ}
             → Γ ⊢ F ≅ F′ ^ [ ! , ι l ]
             → Γ ⊢ e ∷ Empty lEmpty ^ [ % , ι lEmpty ]
             → Γ ⊢ e′ ∷ Empty lEmpty ^ [ % , ι lEmpty ]
             → Γ     ⊢ Emptyrec l lEmpty F e ~ Emptyrec l lEmpty F′ e′ ∷ F ^ [ ! , ι l ]

    -- Id congruences
    ~-Id  : ∀ {A A' l t t' u u' Γ}
          → Γ ⊢ A ~ A' ∷ Univ ! l ^ [ ! , next l ]
          → Γ ⊢ t ≅ t' ∷ A ^ [ ! , ι l ]
          → Γ ⊢ u ≅ u' ∷ A ^ [ ! , ι l ]
          → Γ ⊢ Id A t u ~ Id A' t' u' ∷ SProp l ^ [ ! , next l ]

    ~-Idℕ : ∀ {t t' u u' Γ}
          → ⊢ Γ
          → Γ ⊢ t ~ t' ∷ ℕ ^ [ ! , ι ⁰ ]
          → Γ ⊢ u ≅ u' ∷ ℕ ^ [ ! , ι ⁰ ]
          → Γ ⊢ Id ℕ t u ~ Id ℕ t' u' ∷ SProp ⁰ ^ [ ! , next ⁰ ]

    ~-Idℕ0 : ∀ {u u' Γ}
           → ⊢ Γ
           → Γ ⊢ u ~ u' ∷ ℕ ^ [ ! , ι ⁰ ]
           → Γ ⊢ Id ℕ zero u ~ Id ℕ zero u' ∷ SProp ⁰ ^ [ ! , next ⁰ ]

    ~-IdℕS : ∀ {t t' u u' Γ}
           → ⊢ Γ
           → Γ ⊢ t ≅ t' ∷ ℕ ^ [ ! , ι ⁰ ]
           → Γ ⊢ u ~ u' ∷ ℕ ^ [ ! , ι ⁰ ]
           → Γ ⊢ Id ℕ (suc t) u ~ Id ℕ (suc t') u' ∷ SProp ⁰ ^ [ ! , next ⁰ ]

    ~-IdU : ∀ {t t' u u' Γ} →
          let l = ⁰ in
          let l' = ¹ in
            ⊢ Γ
          → Γ ⊢ t ~ t' ∷ U l ^ [ ! , next l ]
          → Γ ⊢ u ≅ u' ∷ U l ^ [ ! , next l ]
          → Γ ⊢ Id (U l) t u ~ Id (U l) t' u' ∷ SProp l' ^ [ ! , next l' ]

    ~-IdUℕ : ∀ {u u' Γ}
           → ⊢ Γ
           → Γ ⊢ u ~ u' ∷ U ⁰ ^ [ ! , next ⁰ ]
           → Γ ⊢ Id (U ⁰) ℕ u ~ Id (U ⁰) ℕ u' ∷ SProp ¹ ^ [ ! , next ¹ ]

    ~-IdUΠ : ∀ {A rA B A' B' u u' Γ} →
          let l = ⁰ in
          let l' = ¹ in
           Γ ⊢ Π A ^ rA ° ⁰ ▹ B ° ⁰ ° ⁰ ≅ Π A' ^ rA ° ⁰ ▹ B' ° ⁰ ° ⁰ ∷ U ⁰ ^ [ ! , next ⁰ ]
           → Γ ⊢ u ~ u' ∷ U l ^ [ ! , next l ]
           → Γ ⊢ Id (U l)  (Π A ^ rA ° l ▹ B ° l ° ⁰ ) u ~ Id (U l) (Π A' ^ rA ° l ▹ B' ° l ° ⁰) u' ∷ SProp l' ^ [ ! , next l' ]

    -- cast congruences

    ~-cast : ∀ {A A' B B' e e' t t' Γ} →
           let l = ⁰ in
             Γ ⊢ A ~ A' ∷ U l ^ [ ! , next l ]
           → Γ ⊢ B ≅ B' ∷ U l ^ [ ! , next l ]
           → Γ ⊢ t ≅ t' ∷ A ^ [ ! , ι l ]
           → Γ ⊢ e ∷ (Id (U ⁰) A B) ^ [ % , next ⁰ ]
           → Γ ⊢ e' ∷ (Id (U ⁰) A' B') ^ [ % , next ⁰ ]
           → Γ ⊢ cast l A B e t ~ cast l A' B' e' t' ∷ B ^ [ ! , ι l ]

    ~-castℕ : ∀ {B B' e e' t t' Γ}
            → ⊢ Γ
            → Γ ⊢ B ~ B' ∷ U ⁰ ^ [ ! , next ⁰ ]
            → Γ ⊢ t ≅ t' ∷ ℕ ^ [ ! , ι ⁰ ]
            → Γ ⊢ e ∷ (Id (U ⁰) ℕ B) ^ [ % , next ⁰ ]
            → Γ ⊢ e' ∷ (Id (U ⁰) ℕ B') ^ [ % , next ⁰ ]
            → Γ ⊢ cast ⁰ ℕ B e t ~ cast ⁰ ℕ B' e' t' ∷ B ^ [ ! , ι ⁰ ]

    ~-castℕℕ : ∀ {e e' t t' Γ}
             → ⊢ Γ
             → Γ ⊢ t ~ t' ∷ ℕ ^ [ ! , ι ⁰ ]
            → Γ ⊢ e ∷ (Id (U ⁰) ℕ ℕ) ^ [ % , next ⁰ ]
            → Γ ⊢ e' ∷ (Id (U ⁰) ℕ ℕ) ^ [ % , next ⁰ ]
             → Γ ⊢ cast ⁰ ℕ ℕ e t ~ cast ⁰ ℕ ℕ e' t' ∷ ℕ ^ [ ! , ι ⁰ ]

    ~-castΠ : ∀ {A A' rA P P' B B' e e' t t' Γ} →
           let l = ⁰ in
           Γ ⊢ Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰ ≅ Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰ ∷ U ⁰ ^ [ ! , next ⁰ ]
           → Γ ⊢ B ~ B' ∷ U l ^ [ ! , next l ]
           → Γ ⊢ t ≅ t' ∷ Π A ^ rA ° l ▹ P ° l ° l ^ [ ! , ι l ]
           → Γ ⊢ e ∷ (Id (U ⁰) (Π A ^ rA ° l ▹ P ° l ° l) B) ^ [ % , next ⁰ ]
           → Γ ⊢ e' ∷ (Id (U ⁰) (Π A' ^ rA ° l ▹ P' ° l ° l) B') ^ [ % , next ⁰ ]
           → Γ ⊢ cast l (Π A ^ rA ° l ▹ P ° l ° l) B e t ~ cast l (Π A' ^ rA ° l ▹ P' ° l ° l) B' e' t' ∷ B ^ [ ! , ι l ]

    ~-castℕΠ : ∀ {A A' rA P P' e e' t t' Γ}
             → Γ ⊢ A ∷ Univ rA ⁰ ^ [ ! , next ⁰ ]
             → Γ ∙ A ^ [ rA , ι ⁰ ] ⊢ P ∷ U ⁰ ^ [ ! , next ⁰ ]
             → Γ ⊢ Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰ ≅ Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰ ∷ U ⁰ ^ [ ! , next ⁰ ]
             → Γ ⊢ t ≅ t' ∷ ℕ ^ [ ! , ι ⁰ ]
             → Γ ⊢ e ∷ (Id (U ⁰) ℕ (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰)) ^ [ % , next ⁰ ]
             → Γ ⊢ e' ∷ (Id (U ⁰) ℕ (Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰)) ^ [ % , next ⁰ ]
             → Γ ⊢ cast ⁰ ℕ (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰) e t ~ cast ⁰ ℕ (Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰) e' t' ∷ (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰) ^ [ ! , ι ⁰ ]

    ~-castΠℕ : ∀ {A A' rA P P' e e' t t' Γ} →
             let l = ⁰ in
               Γ ⊢ A ∷ Univ rA l ^ [ ! , next l ]
             → Γ ∙ A ^ [ rA , ι l ] ⊢ P ∷ U l ^ [ ! , next l ]
             → Γ ⊢ Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰ ≅ Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰ ∷ U ⁰ ^ [ ! , next ⁰ ]
             → Γ ⊢ t ≅ t' ∷ (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰) ^ [ ! , ι l ]
             → Γ ⊢ e ∷ (Id (U ⁰) (Π A ^ rA ° l ▹ P ° l ° ⁰) ℕ) ^ [ % , next ⁰ ]
             → Γ ⊢ e' ∷ (Id (U ⁰) (Π A' ^ rA ° l ▹ P' ° l  ° ⁰) ℕ) ^ [ % , next ⁰ ]
             → Γ ⊢ cast l (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰) ℕ e t ~ cast l (Π A' ^ rA ° ⁰ ▹ P' ° ⁰ ° ⁰) ℕ e' t' ∷ ℕ ^ [ ! , ι ⁰ ]

    ~-castΠΠ%! : ∀ {A A' P P' B B' Q Q' e e' t t' Γ}
             → Γ ⊢ A ∷ Univ % ⁰ ^ [ ! , next ⁰ ]
             → Γ ∙ A ^ [ % , ι ⁰ ] ⊢ P ∷ U ⁰ ^ [ ! , next ⁰ ]
             → Γ ⊢ Π A ^ % ° ⁰ ▹ P ° ⁰  ° ⁰ ≅ Π A' ^ % ° ⁰ ▹ P' ° ⁰ ° ⁰ ∷ U ⁰ ^ [ ! , next ⁰ ]
             → Γ ⊢ B ∷ Univ ! ⁰ ^ [ ! , next ⁰ ]
             → Γ ∙ B ^ [ ! , ι ⁰ ] ⊢ Q ∷ U ⁰ ^ [ ! , next ⁰ ]
             → Γ ⊢ Π B ^ ! ° ⁰ ▹ Q ° ⁰ ° ⁰ ≅ Π B' ^ ! ° ⁰ ▹ Q' ° ⁰ ° ⁰ ∷ U ⁰ ^ [ ! , next ⁰ ]
             → Γ ⊢ t ≅ t' ∷ Π A ^ % ° ⁰ ▹ P ° ⁰  ° ⁰ ^ [ ! , ι ⁰ ]
             → Γ ⊢ e ∷ (Id (U ⁰) (Π A ^ % ° ⁰ ▹ P ° ⁰ ° ⁰) (Π B ^ ! ° ⁰ ▹ Q ° ⁰ ° ⁰)) ^ [ % , next ⁰ ]
             → Γ ⊢ e' ∷ (Id (U ⁰) (Π A' ^ % ° ⁰ ▹ P' ° ⁰ ° ⁰) (Π B' ^ ! ° ⁰ ▹ Q' ° ⁰ ° ⁰)) ^ [ % , next ⁰ ]
             → Γ ⊢ cast ⁰ (Π A ^ % ° ⁰ ▹ P ° ⁰ ° ⁰) (Π B ^ ! ° ⁰ ▹ Q ° ⁰ ° ⁰) e t ~ cast ⁰ (Π A' ^ % ° ⁰ ▹ P' ° ⁰ ° ⁰) (Π B' ^ ! ° ⁰ ▹ Q' ° ⁰ ° ⁰) e' t' ∷ Π B ^ ! ° ⁰ ▹ Q ° ⁰ ° ⁰ ^ [ ! , ι ⁰ ]

    ~-castΠΠ!% : ∀ {A A' P P' B B' Q Q' e e' t t' Γ}
             → Γ ⊢ A ∷ Univ ! ⁰ ^ [ ! , next ⁰ ]
             → Γ ∙ A ^ [ ! , ι ⁰ ] ⊢ P ∷ U ⁰ ^ [ ! , next ⁰ ]
             → Γ ⊢ Π A ^ ! ° ⁰ ▹ P ° ⁰ ° ⁰ ≅ Π A' ^ ! ° ⁰ ▹ P' ° ⁰ ° ⁰ ∷ U ⁰ ^ [ ! , next ⁰ ]
             → Γ ⊢ B ∷ Univ % ⁰ ^ [ ! , next ⁰ ]
             → Γ ∙ B ^ [ % , ι ⁰ ] ⊢ Q ∷ U ⁰ ^ [ ! , next ⁰ ]
             → Γ ⊢ Π B ^ % ° ⁰ ▹ Q ° ⁰ ° ⁰ ≅ Π B' ^ % ° ⁰ ▹ Q' ° ⁰ ° ⁰ ∷ U ⁰ ^ [ ! , next ⁰ ]
             → Γ ⊢ t ≅ t' ∷ Π A ^ ! ° ⁰ ▹ P ° ⁰ ° ⁰ ^ [ ! , ι ⁰ ]
             → Γ ⊢ e ∷ (Id (U ⁰) (Π A ^ ! ° ⁰ ▹ P ° ⁰ ° ⁰) (Π B ^ % ° ⁰ ▹ Q ° ⁰ ° ⁰)) ^ [ % , next ⁰ ]
             → Γ ⊢ e' ∷ (Id (U ⁰) (Π A' ^ ! ° ⁰ ▹ P' ° ⁰ ° ⁰) (Π B' ^ % ° ⁰ ▹ Q' ° ⁰ ° ⁰)) ^ [ % , next ⁰ ]
             → Γ ⊢ cast ⁰ (Π A ^ ! ° ⁰ ▹ P ° ⁰ ° ⁰) (Π B ^ % ° ⁰ ▹ Q ° ⁰ ° ⁰) e t ~ cast ⁰ (Π A' ^ ! ° ⁰ ▹ P' ° ⁰ ° ⁰) (Π B' ^ % ° ⁰ ▹ Q' ° ⁰ ° ⁰) e' t' ∷ Π B ^ % ° ⁰ ▹ Q ° ⁰  ° ⁰ ^ [ ! , ι ⁰ ]

    ~-irrelevance : ∀ {n n′ A l Γ} → Γ ⊢ n ∷ A ^ [ % , l ] → Γ ⊢ n′ ∷ A ^ [ % , l ]
                  → Γ ⊢ n ~ n′ ∷ A ^ [ % , l ]

  -- Composition of universe and generic equality compatibility
  ~-to-≅ : ∀ {t u r l Γ} → Γ ⊢ t ~ u ∷ (Univ r l) ^ [ ! , next l ] → Γ ⊢ t ≅ u ^ [ r , ι l ]
  ~-to-≅ t~u = ≅-univ (~-to-≅ₜ t~u)


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

open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; sym; refl; subst; trans; cong)
open import Relation.Nullary using (Dec; yes; no)
open import Relation.Nullary.Decidable using (True; toWitness; fromWitness)
open import Function using (_∘_)

import Data.Maybe as Maybe
import Data.Empty as Empty
import Data.Unit as Unit
import Data.Nat as ℕ
import Data.Nat.Properties as ℕₚ
import Data.Product as Product
import Data.Product.Properties as Productₚ
import Data.Vec as Vec
import Data.Vec.Relation.Unary.All as All
import Data.Fin as Fin

open Maybe using (nothing; just)
open Empty using (⊥; ⊥-elim)
open Unit using (⊤; tt)
open ℕ using (ℕ; zero; suc)
open Product using (Σ-syntax; ∃-syntax; _×_; _,_; proj₂; proj₁)
open Vec using (Vec; []; _∷_)
open All using (All; []; _∷_)
open Fin using (Fin ; zero ; suc)
open Relation.Binary.PropositionalEquality.≡-Reasoning

import PiCalculus.Syntax
open PiCalculus.Syntax.Scoped
open import PiCalculus.Semantics
open import PiCalculus.LinearTypeSystem.Algebras

module PiCalculus.LinearTypeSystem.ContextLemmas (Ω : Algebras) where
open Algebras Ω
open import PiCalculus.LinearTypeSystem Ω

private
  variable
    n : ℕ
    idxs idxsₗ idxsᵣ : Idxs n
    γ : PreCtx n
    idx idx' : Idx
    t : Type
    P Q : Scoped n
    i j : Fin n
    Γ Δ Ξ Θ : Ctx idxs
    x x' y z : Usage idx ²

data _≔_⊗_ : Ctx idxs → Ctx idxs → Ctx idxs → Set where
  []  : [] ≔ [] ⊗ []
  _,_ : Γ ≔ Δ ⊗ Ξ → x ≔ y ∙² z → (Γ -, x) ≔ (Δ -, y) ⊗ (Ξ -, z)

ε : ∀ {idxs : Idxs n} → Ctx idxs
ε {idxs = []} = []
ε {idxs = _ -, _} = ε -, (0∙ , 0∙)

⊗-get : {idxs : Idxs n} {Γ Δ Ξ : Ctx idxs} (i : Fin n) → Γ ≔ Δ ⊗ Ξ → All.lookup i Γ ≔ All.lookup i Δ ∙² All.lookup i Ξ
⊗-get zero (Γ≔ , x≔) = x≔
⊗-get (suc i) (Γ≔ , x) = ⊗-get i Γ≔

⊗-idˡ : {idxs : Idxs n} {Γ : Ctx idxs} → Γ ≔ ε {idxs = idxs} ⊗ Γ
⊗-idˡ {Γ = []} = []
⊗-idˡ {Γ = Γ -, x} = ⊗-idˡ , ∙²-idˡ

⊗-unique : {Γ Γ' Δ Ξ  : Ctx idxs} → Γ' ≔ Δ ⊗ Ξ → Γ ≔ Δ ⊗ Ξ → Γ' ≡ Γ
⊗-unique [] [] = refl
⊗-unique (Γ'≔ , x'≔) (Γ≔ , x≔) rewrite ⊗-unique Γ'≔ Γ≔ | ∙²-unique x'≔ x≔ = refl

⊗-uniqueˡ : {Γ Δ Δ' Ξ  : Ctx idxs} → Γ ≔ Δ' ⊗ Ξ → Γ ≔ Δ ⊗ Ξ → Δ' ≡ Δ
⊗-uniqueˡ [] [] = refl
⊗-uniqueˡ (Δ'≔ , y'≔) (Δ≔ , y≔) rewrite ⊗-uniqueˡ Δ'≔ Δ≔ | ∙²-uniqueˡ y'≔ y≔ = refl

⊗-comm : {Γ Δ Ξ : Ctx idxs} → Γ ≔ Δ ⊗ Ξ → Γ ≔ Ξ ⊗ Δ
⊗-comm [] = []
⊗-comm (Γ≔ , x≔) = ⊗-comm Γ≔ , ∙²-comm x≔

⊗-assoc : {Γₘ Γₗ Γᵣ Γₗₗ Γₗᵣ : Ctx idxs}
        → Γₘ ≔ Γₗ ⊗ Γᵣ → Γₗ ≔ Γₗₗ ⊗ Γₗᵣ → Σ[ Γᵣ' ∈ Ctx idxs ] (Γₘ ≔ Γₗₗ ⊗ Γᵣ' × Γᵣ' ≔ Γₗᵣ ⊗ Γᵣ)
⊗-assoc [] [] = [] , [] , []
⊗-assoc (Γₘ≔ , xₘ≔) (Γₗ≔ , xₗ≔) with ⊗-assoc Γₘ≔ Γₗ≔ | ∙²-assoc xₘ≔ xₗ≔
... | (_ , Γₘ'≔ , Γᵣ'≔)  | (_ , xₘ'≔ , xᵣ'≔) = _ , ((Γₘ'≔ , xₘ'≔) , (Γᵣ'≔ , xᵣ'≔))

⊗-assoc⁻¹ : ∀ {x y z u v : Ctx idxs} → x ≔ y ⊗ z → z ≔ u ⊗ v → ∃[ w ] (x ≔ w ⊗ v × w ≔ y ⊗ u)
⊗-assoc⁻¹ a b = let _ , a' , b' = ⊗-assoc (⊗-comm a) (⊗-comm b) in _ , ⊗-comm a' , ⊗-comm b'

⊗-comp : {Γ Δₗ Δᵣ Δ Ξ Θ : Ctx idxs}
       → Γ ≔ Δₗ ⊗ Ξ → Ξ ≔ Δᵣ ⊗ Θ
       → Γ ≔ Δ  ⊗ Θ → Δ ≔ Δₗ ⊗ Δᵣ
⊗-comp l≔ r≔ Γ≔ with ⊗-assoc (⊗-comm l≔) (⊗-comm r≔)
⊗-comp l≔ r≔ Γ≔ | _ , Γ'≔ , R'≔ rewrite ⊗-uniqueˡ Γ≔ (⊗-comm Γ'≔) = ⊗-comm R'≔

⊗-idʳ : {Γ : Ctx idxs} → Γ ≔ Γ ⊗ ε
⊗-idʳ = ⊗-comm ⊗-idˡ

⊗-mut-cancel : ∀ {x y y' z : Ctx idxs} → x ≔ y ⊗ z → z ≔ y' ⊗ x → x ≡ z
⊗-mut-cancel [] [] = refl
⊗-mut-cancel (Γ≔ , x≔) (Ξ≔ , z≔) rewrite ⊗-mut-cancel Γ≔ Ξ≔ | ∙²-mut-cancel x≔ z≔ = refl

∋-≡Idx : {idxs : Idxs n} {i : Fin n} {Γ Δ : Ctx idxs} {x : Usage idx ²}
       → Γ ∋[ i ] x ▹ Δ → Vec.lookup idxs i ≡ idx
∋-≡Idx (zero x) = refl
∋-≡Idx (suc s) rewrite ∋-≡Idx s = refl

∋-≡Type : γ ∋[ i ] t → Vec.lookup γ i ≡ t
∋-≡Type zero = refl
∋-≡Type (suc t) rewrite ∋-≡Type t = refl

≡Type-∋ : Vec.lookup γ i ≡ t → γ ∋[ i ] t
≡Type-∋ {γ = _ -, _} {i = zero} refl = zero
≡Type-∋ {γ = _ -, _} {i = suc i} eq = suc (≡Type-∋ eq)

∋-⊗ : {Γ Ξ : Ctx idxs}
    → Γ ∋[ i ] x ▹ Ξ
    → Σ[ Δ ∈ Ctx idxs ]
      (Γ ≔ Δ ⊗ Ξ × Δ ∋[ i ] x ▹ ε {idxs = idxs})
∋-⊗ (zero x) = _ , (⊗-idˡ , x) , zero ∙²-idʳ
∋-⊗ (suc s) with ∋-⊗ s
∋-⊗ (suc s) | _ , Γ≔ , Δ≔ = _ , (Γ≔ , ∙²-idˡ) , suc Δ≔

⊗-∋ : {Γ Δ Ξ : Ctx idxs}
    → Γ ≔ Δ ⊗ Ξ
    → Δ ∋[ i ] x ▹ ε {idxs = idxs}
    → Γ ∋[ i ] x ▹ Ξ
⊗-∋ (sp , s) (zero x) rewrite ⊗-unique sp ⊗-idˡ | ∙²-unique x ∙²-idʳ = zero s
⊗-∋ (sp , s) (suc only) rewrite ∙²-unique s ∙²-idˡ = suc (⊗-∋ sp only)

∋-ℓ∅ : {idxs : Idxs n} {Γ : Ctx idxs} {i : Fin n} {idx : Idx}→ Vec.lookup idxs i ≡ idx → Γ ∋[ i ] ℓ∅ {idx} ▹ Γ
∋-ℓ∅ {Γ = _ -, _} {i = zero} refl = zero ∙²-idˡ
∋-ℓ∅ {Γ = _ -, _} {i = suc i} eq = suc (∋-ℓ∅ eq)

-- TODO: deprecate, convert to context first
∋-uniqueʳ : {i : Fin n} {idxs : Idxs n} {Γ Δ Ξ : Ctx idxs}
          → Γ ∋[ i ] x ▹ Δ → Γ ∋[ i ] x ▹ Ξ → Δ ≡ Ξ
∋-uniqueʳ (zero a) (zero b) rewrite ∙²-uniqueˡ (∙²-comm a) (∙²-comm b) = refl
∋-uniqueʳ (suc a) (suc b) rewrite ∋-uniqueʳ a b = refl

-- TODO: deprecate, convert to context first
∋-uniqueˡ : Γ ∋[ i ] x ▹ Δ → Ξ ∋[ i ] x ▹ Δ → Γ ≡ Ξ
∋-uniqueˡ (zero a) (zero b) rewrite ∙²-unique a b = refl
∋-uniqueˡ (suc a) (suc b) rewrite ∋-uniqueˡ a b = refl

∋-lookup-≡ : Γ ∋[ i ] x ▹ Δ → All.lookup i Γ ≔ x ∙² All.lookup i Δ
∋-lookup-≡ {Δ = _ -, _} (zero x) = x
∋-lookup-≡ {Δ = _ -, _} (suc s) = ∋-lookup-≡ s

∋-lookup-≢ : Γ ∋[ i ] x ▹ Δ → ∀ j → j ≢ i → All.lookup j Γ ≡ All.lookup j Δ
∋-lookup-≢ (zero x) zero j≢i = ⊥-elim (j≢i refl)
∋-lookup-≢ (suc xati) zero j≢i = refl
∋-lookup-≢ (zero x) (suc j) j≢i = refl
∋-lookup-≢ (suc xati) (suc j) j≢i = ∋-lookup-≢ xati j (j≢i ∘ cong suc)

∙²-⊗ : {Γ Δ Ξ : Ctx idxs}
     → Γ ∋[ i ] x ▹ ε → Δ ∋[ i ] y ▹ ε → Ξ ∋[ i ] z ▹ ε
     → x ≔ y ∙² z → Γ ≔ Δ ⊗ Ξ
∙²-⊗ (zero x) (zero y) (zero z) sp
  rewrite ∙²-unique x ∙²-idʳ | ∙²-unique y ∙²-idʳ | ∙²-unique z ∙²-idʳ = ⊗-idˡ , sp
∙²-⊗ (suc Γ≔) (suc Δ≔) (suc Ξ≔) sp = ∙²-⊗ Γ≔ Δ≔ Ξ≔ sp , ∙²-idˡ

diamond : {Γ Ξ Ψ : Ctx idxs}
        → i ≢ j
        → Γ ∋[ j ] x ▹ Ξ
        → Γ ∋[ i ] y ▹ Ψ
        → Σ[ Θ ∈ Ctx idxs ]
        ( Ξ ∋[ i ] y ▹ Θ
        × Ψ ∋[ j ] x ▹ Θ
        )
diamond i≢j (zero _) (zero _) = ⊥-elim (i≢j refl)
diamond i≢j (zero x) (suc ∋i) = _ , suc ∋i , zero x
diamond i≢j (suc ∋j) (zero x) = _ , zero x , suc ∋j
diamond i≢j (suc ∋j) (suc ∋i) with diamond (i≢j ∘ cong suc) ∋j ∋i
diamond i≢j (suc ∋j) (suc ∋i) | _ , ∋i' , ∋j' = _ , suc ∋i' , suc ∋j'

outer-diamond : {Γ Ξ Ψ Θ ΞΔ Δ ΨΔ : Ctx idxs}
              → i ≢ j
              → Γ ∋[ i ] x ▹ Ξ → Γ ∋[ j ] y ▹ Ψ
              → Ξ ∋[ j ] y ▹ Θ → Ψ ∋[ i ] x ▹ Θ
              → Ξ ≔ ΞΔ ⊗ Δ → Ψ ≔ ΨΔ ⊗ Δ
              → Σ[ ΘΔ ∈ Ctx idxs ] (Θ ≔ ΘΔ ⊗ Δ)
outer-diamond i≢j (zero _) (zero _) (zero _) (zero _) a b = ⊥-elim (i≢j refl)
outer-diamond i≢j (zero x₁) (suc ∋j) (suc ∈j) (zero x) (as , a) (bs , b) = _ , (bs , a)
outer-diamond i≢j (suc ∋i) (zero ∋j) (zero ∈j) (suc ∈i) (as , a) (bs , b) = _ , (as , b)
outer-diamond i≢j (suc ∋i) (suc ∋j) (suc ∈j) (suc ∈i) (as , a) (bs , b) with outer-diamond (i≢j ∘ cong suc) ∋i ∋j ∈j ∈i as bs
outer-diamond i≢j (suc ∋i) (suc ∋j) (suc ∈j) (suc ∈i) (as , a) (bs , b) | _ , s = _ , (s , a)

reverse : {Γ ΓΞ Ξ ΞΨ Ψ : Ctx idxs}
        → Γ ≔ ΓΞ ⊗ Ξ
        → Ξ ≔ ΞΨ ⊗ Ψ
        → Σ[ Θ ∈ Ctx idxs ]
        ( Γ ≔ ΞΨ ⊗ Θ
        × Θ ≔ ΓΞ ⊗ Ψ
        )
reverse Γ≔ΓΞ∙Ξ Ξ≔ΞΨ∙Ψ =
  let _ , Γ≔ΞΨ∙Θ , Θ≔Ψ∙ΓΞ = ⊗-assoc (⊗-comm Γ≔ΓΞ∙Ξ) Ξ≔ΞΨ∙Ψ in
  _ , Γ≔ΞΨ∙Θ , ⊗-comm Θ≔Ψ∙ΓΞ

boil : {Γ Ξ ΞΨ Θ Ψ ΘΨ : Ctx idxs}
     → Γ ∋[ i ] x ▹ Θ
     → Γ ∋[ i ] y ▹ Ξ
     → Ξ ≔ ΞΨ ⊗ Ψ
     → Θ ≔ ΘΨ ⊗ Ψ
     → All.lookup i ΘΨ ≡ ℓ∅
     → ∃[ z ] (x ≔ y ∙² z)
boil {i = zero} (zero a) (zero b) (_ , c) (_ , d) refl rewrite ∙²-unique d ∙²-idˡ with ∙²-assoc⁻¹ b c
boil {i = zero} (zero a) (zero b) (_ , c) (_ , d) refl | _ , e , f rewrite ∙²-uniqueˡ e a = _ , f
boil {i = suc i} (suc a) (suc b) (c , _) (d , _) eq = boil a b c d eq

split : x ≔ y ∙² z
      → Γ ∋[ i ] x ▹ Ξ
      → ∃[ Δ ] (Γ ∋[ i ] y ▹ Δ × Δ ∋[ i ] z ▹ Ξ)
split s (zero x) = let _ , x' , s' = ∙²-assoc x s in _ , zero x' , zero s'
split s (suc x) with split s x
split s (suc x) | _ , y , z = _ , suc y , suc z

split-ℓ∅ : {Γ ΓΨ Ψ ΓΘ Θ ΘΨ : Ctx idxs}
        → Γ ≔ ΓΨ ⊗ Ψ
        → Γ ≔ ΓΘ ⊗ Θ
        → Θ ≔ ΘΨ ⊗ Ψ
        → All.lookup i ΓΨ ≡ ℓ∅
        → All.lookup i ΓΘ ≡ ℓ∅ × All.lookup i ΘΨ ≡ ℓ∅
split-ℓ∅ {i = zero} (a , x) (b , y) (c , z) refl rewrite ∙²-unique x ∙²-idˡ with ∙²-mut-cancel y z
split-ℓ∅ {i = zero} (a , x) (b , y) (c , z) refl | refl = ∙²-uniqueˡ y ∙²-idˡ , ∙²-uniqueˡ z ∙²-idˡ
split-ℓ∅ {i = suc i} (a , _) (b , _) (c , _) eq = split-ℓ∅ a b c eq

⊢-⊗ : {γ : PreCtx n} {idxs : Idxs n} {Γ Ξ : Ctx idxs} → γ ； Γ ⊢ P ▹ Ξ → Σ[ Δ ∈ Ctx idxs ] (Γ ≔ Δ ⊗ Ξ)
⊢-⊗ 𝟘 = ε , ⊗-idˡ
⊢-⊗ (ν t m μ ⊢P) with ⊢-⊗ ⊢P
⊢-⊗ (ν t m μ ⊢P) | (_ -, _) , (P≔ , _) = _ , P≔
⊢-⊗ ((_ , x) ⦅⦆ ⊢P) with ⊢-⊗ ⊢P
⊢-⊗ ((_ , x) ⦅⦆ ⊢P) | (_ -, _) , (P≔ , _) =
  let _ , x≔ , _ = ∋-⊗ x
      _ , xP≔ , _ = ⊗-assoc⁻¹ x≔ P≔
   in _ , xP≔
⊢-⊗ ((_ , x) ⟨ _ , y ⟩ ⊢P) =
  let _ , x≔ , _ = ∋-⊗ x
      _ , y≔ , _ = ∋-⊗ y
      _ , P≔ = ⊢-⊗ ⊢P
      _ , xy≔ , _ = ⊗-assoc⁻¹ x≔ y≔
      _ , Pxy≔ , _ = ⊗-assoc⁻¹ xy≔ P≔
   in _ , Pxy≔
⊢-⊗ (⊢P ∥ ⊢Q) =
  let _ , P≔ = ⊢-⊗ ⊢P
      _ , Q≔ = ⊢-⊗ ⊢Q
      _ , PQ≔ , _ = ⊗-assoc⁻¹ P≔ Q≔
   in _ , PQ≔

ctx-insert : (i : Fin (suc n)) → Usage idx ² → Ctx idxs → Ctx (Vec.insert idxs i idx)
ctx-insert zero xs' Γ = Γ -, xs'
ctx-insert (suc i) xs' (Γ -, xs) = ctx-insert i xs' Γ -, xs

ctx-remove : Ctx idxs → (i : Fin (suc n)) → Ctx (Vec.remove idxs i)
ctx-remove (Γ -, _) zero = Γ
ctx-remove (Γ -, ys -, xs) (suc i) = ctx-remove (Γ -, ys) i -, xs

ctx-update : (i : Fin n) → Usage (Vec.lookup idxs i) ² → Ctx idxs → Ctx idxs
ctx-update zero m' (ms -, m) = ms -, m'
ctx-update (suc i) m' (ms -, m) = ctx-update i m' ms -, m

fromFin : {γ : PreCtx n} {idxs : Idxs n} {Γ : Ctx idxs}
        → ∀ i
        → {y z : Usage (Vec.lookup idxs i) ²}
        → All.lookup i Γ ≔ y ∙² z
        → γ ； Γ ∋[ i ] Vec.lookup γ i ； y ▹ ctx-update i z Γ
fromFin {γ = γ -, t} {Γ = Γ -, x} zero split = zero , (zero split)
fromFin {γ = γ -, t} {Γ = Γ -, x} (suc i) split = there (fromFin i split)

#_ : {γ : PreCtx n} {idxs : Idxs n} {Γ : Ctx idxs}
   → ∀ m {m<n : True (m ℕₚ.<? n)}
   → let i = Fin.fromℕ< (toWitness m<n)
     in {y : Usage (Vec.lookup idxs i) ²}
   → {check : True (∙²-computeʳ (All.lookup i Γ) y)}
   → let z , _ = toWitness check
     in γ ； Γ ∋[ i ] Vec.lookup γ i ； y ▹ ctx-update i z Γ
(# m) {m<n} {check = check} =
  let _ , split = toWitness check
  in fromFin (Fin.fromℕ< (toWitness m<n)) split


open import Data.Nat    using (ℕ; _+_) renaming (_≤?_ to _≤?ₙ_)
open import Data.Bool   using (Bool; true; false; not; _∧_)
open import Data.String using (String; _≟_)
open import Data.Sum    using (_⊎_; [_,_]′; inj₁; inj₂)
open import Data.Product               using (_×_; _,_)
open import Relation.Nullary           using (yes; no)
open import Relation.Nullary.Decidable using (⌊_⌋)
open import Data.Product using (_×_; _,_; -,_; _-,-_; ∃; ∃-syntax)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym)
open import Level using (Level; suc; _⊔_)

open import IMP
open import Hoare

data True {l} : Set l where
  ⊤ : True

data acom {l} : Set (suc l) where
  SKIP  : acom
  _::=_ : String → aexp → acom
  _::_  : acom {l} → acom {l} → acom
  IF_THEN_ELSE_ : bexp → acom {l} → acom {l} → acom
  WHILE[_]_DO_ : assn {l} → bexp → acom {l} → acom

strip : ∀{l} → acom {l} → com
strip SKIP = SKIP
strip (x ::= a) = x ::= a
strip (c₁ :: c₂) = strip c₁ :: strip c₂
strip (IF b THEN c₁ ELSE c₂) = IF b THEN strip c₁ ELSE strip c₂
strip (WHILE[ I ] b DO c) = WHILE b DO strip c

pre : ∀{l} → acom {l} → assn → assn
pre SKIP Q = Q
pre (x ::= a) Q s = Q (s [ x ::= aval a s ])
pre (c₁ :: c₂) Q = pre c₁ (pre c₂ Q)
pre (IF b THEN c₁ ELSE c₂) Q s with bval b s
... | true = pre c₁ Q s
... | false = pre c₂ Q s
pre (WHILE[ I ] b DO c) Q = I

vc : ∀{l} → acom {l} → assn → Set l
vc SKIP Q = True
vc (x ::= x₁) Q = True
vc (c₁ :: c₂) Q = vc c₁ (pre c₂ Q) × vc c₂ Q
vc (IF b THEN c₁ ELSE c₂) Q = vc c₁ Q × vc c₂ Q
vc (WHILE[ I ] b DO c) Q =
     (∀ s → I s × bval b s ≡ true  → pre c I s)
   × (∀ s → I s × bval b s ≡ false → Q s)
   × vc c I

extract-if-pre₁ : ∀{l} {Q : assn {l}} {c₁ c₂ s} b
  → pre (IF b THEN c₁ ELSE c₂) Q s
  × bval b s ≡ true
  → pre c₁ Q s
extract-if-pre₁ b (x , y) rewrite y = x

extract-if-pre₂ : ∀{l} {Q : assn {l}} {c₁ c₂ s} b
  → pre (IF b THEN c₁ ELSE c₂) Q s
  × bval b s ≡ false
  → pre c₂ Q s
extract-if-pre₂ b (x , y) rewrite y = x

vc-correctness : ∀{l} {P Q : assn} c
  → vc {l} c Q
  → (∀ s → P s → pre c Q s)
  → ⊢[ P ] strip c [ Q ]
vc-correctness SKIP v i = Conseq i Skip (λ s z → z)
vc-correctness (x ::= a) v i = Conseq i Loc (λ r k → k)
vc-correctness (c₁ :: c₂) v i = Comp (vc-correctness c₁ (Data.Product.proj₁ v) i)
                                     (vc-correctness c₂ (Data.Product.proj₂ v) (λ s z → z))
vc-correctness {l}{P}{Q} (IF b THEN c₁ ELSE c₂) (fst , snd) i =
     Conseq i (If (vc-correctness c₁ fst (λ s p → extract-if-pre₁ b p))
                  (vc-correctness c₂ snd (λ s p → extract-if-pre₂ b p)))
              (λ s z → z)
vc-correctness (WHILE[ I ] b DO c) (fst , fst₁ , snd) i =
    Conseq i (While (vc-correctness c snd fst)) fst₁

pre-mono : ∀{l} {P P′ : assn {l}} {s′} c
  → (∀ s → P s → P′ s)
  → pre c P s′ → pre c P′ s′
pre-mono {s′ = s′} SKIP x x₁ = x s′ x₁
pre-mono (x₂ ::= x₃) x x₁ = x _ x₁
pre-mono (c :: c₁) x x₁ = pre-mono c (λ s → pre-mono c₁ x) x₁
pre-mono {s′ = s′} (IF x₂ THEN c₁ ELSE c₂) x x₁ with bval x₂ s′
... | true  = pre-mono c₁ x x₁
... | false = pre-mono c₂ x x₁
pre-mono (WHILE[ x₂ ] x₃ DO c) x x₁ = x₁

vc-mono : ∀{l} {P P′ : assn {l}} c
  → (∀ s → P s → P′ s)
  → vc c P → vc c P′
vc-mono SKIP _ _ = ⊤
vc-mono (x ::= a) _ _ = ⊤
vc-mono (c₁ :: c₂) pp′ (vcpre , vc2) = vc-mono c₁ (λ _ → pre-mono c₂ pp′) vcpre , vc-mono c₂ pp′ vc2
vc-mono (IF b THEN c₁ ELSE c₂) pp′ (vc₁ , vc₂) = vc-mono c₁ pp′ vc₁ , vc-mono c₂ pp′ vc₂
vc-mono (WHILE[ I ] b DO c) pp′ (I₁ , I₂ , vcc) = I₁ , (λ s z → pp′ s (I₂ s z)) , vcc

either : ∀ c → c ≡ true ⊎ c ≡ false
either true  = inj₁ refl
either false = inj₂ refl

construct-if-pre : ∀{l} {P Q : assn {l}} {ac₁ ac₂} b s
   → (bval b s ≡ true  → pre ac₁ Q s)
   → (bval b s ≡ false → pre ac₂ Q s)
   → pre (IF b THEN ac₁ ELSE ac₂) Q s
construct-if-pre b s f g with bval b s
... | true = f refl
... | false = g refl

vc-completeness : ∀{l} {P Q : assn {l}} c
  → ⊢[ P ] c [ Q ]
  → ∃[ c̃ ] ( c ≡ strip c̃
           × vc c̃ Q
           × (∀ s → P s → pre c̃ Q s))
vc-completeness c Skip = SKIP , refl , ⊤ , (λ x c → c)
vc-completeness c Loc = _ , refl , ⊤ ,  (λ x c → c)
vc-completeness (c₁ :: c₂) (Comp x y) with vc-completeness c₁ x
... | ac₁ , b , c , d                 with vc-completeness c₂ y
... | ac₂ , f , g , h rewrite b | f
                      = (ac₁ :: ac₂) , refl
                      , (vc-mono ac₁ h c , g)
                      , (λ s ps → pre-mono ac₁ h (d s ps))
vc-completeness {P = P} (IF b THEN c₁ ELSE c₂) (If d₁ d₂) with vc-completeness c₁ d₁
... | ac₁ , f , g , h                                     with vc-completeness c₂ d₂
... | ac₂ , j , k , m rewrite f | j
                      = (IF b THEN ac₁ ELSE ac₂) , refl
                      , (g , k)
                      , (λ s ps → construct-if-pre {P = P} b s (λ t → h s (ps , t)) (λ f → m s (ps , f)))
vc-completeness (WHILE b DO c) (While r) with vc-completeness c r
... | ac , f , g , h rewrite f = (WHILE[ _ ] b DO ac) , refl
                               , (h , (λ s z → z) , g)
                               , λ s z → z
vc-completeness c (Conseq x₁ r x₂) with vc-completeness c r
... | ac , f , g , h rewrite f
                     = ac , refl
                     , vc-mono ac x₂ g
                     , (λ s ps → pre-mono ac x₂ (h s (x₁ s ps)))


module OldBasicILP.Syntax.Translation where

open import Common.Context public

import OldBasicILP.Syntax.ClosedHilbertSequential as CHS
import OldBasicILP.Syntax.ClosedHilbert as CH

-- Translation from closed Hilbert-style sequential to closed Hilbert-style.

mutual
  chsᵀ→chᵀ : CHS.Ty → CH.Ty
  chsᵀ→chᵀ (CHS.α P)   = CH.α P
  chsᵀ→chᵀ (A CHS.▻ B) = chsᵀ→chᵀ A CH.▻ chsᵀ→chᵀ B
  chsᵀ→chᵀ (p CHS.⦂ A) = chsᴾ→chᴾ p CH.⦂ chsᵀ→chᵀ A
  chsᵀ→chᵀ (A CHS.∧ B) = chsᵀ→chᵀ A CH.∧ chsᵀ→chᵀ B
  chsᵀ→chᵀ CHS.⊤      = CH.⊤

  chsᴾ→chᴾ : ∀ {Ξ A} → CHS.Proof Ξ A → CH.Proof (chsᵀ→chᵀ A)
  chsᴾ→chᴾ CHS.[ d ] = CH.[ chsᴰ→ch d top ]

  chsᴰ→ch : ∀ {Ξ A} → CHS.⊢ᴰ Ξ → A ∈ Ξ → CH.⊢ (chsᵀ→chᵀ A)
  chsᴰ→ch (CHS.mp i j d) top     = CH.app (chsᴰ→ch d i) (chsᴰ→ch d j)
  chsᴰ→ch (CHS.ci d)     top     = CH.ci
  chsᴰ→ch (CHS.ck d)     top     = CH.ck
  chsᴰ→ch (CHS.cs d)     top     = CH.cs
  chsᴰ→ch (CHS.nec `d d) top     = CH.box (chsᴰ→ch `d top)
  chsᴰ→ch (CHS.cdist {Ξ} {A} {B} {`Ξ₁} {`Ξ₂} {`d₁} {`d₂} d) top = oops {A} {B} {`Ξ₁} {`Ξ₂} {`d₁} {`d₂}
  chsᴰ→ch (CHS.cup d)    top     = CH.cup
  chsᴰ→ch (CHS.cdown d)  top     = CH.cdown
  chsᴰ→ch (CHS.cpair d)  top     = CH.cpair
  chsᴰ→ch (CHS.cfst d)   top     = CH.cfst
  chsᴰ→ch (CHS.csnd d)   top     = CH.csnd
  chsᴰ→ch (CHS.unit d)   top     = CH.unit
  chsᴰ→ch (CHS.mp i j d) (pop k) = chsᴰ→ch d k
  chsᴰ→ch (CHS.ci d)     (pop k) = chsᴰ→ch d k
  chsᴰ→ch (CHS.ck d)     (pop k) = chsᴰ→ch d k
  chsᴰ→ch (CHS.cs d)     (pop k) = chsᴰ→ch d k
  chsᴰ→ch (CHS.nec `d d) (pop k) = chsᴰ→ch d k
  chsᴰ→ch (CHS.cdist d)  (pop k) = chsᴰ→ch d k
  chsᴰ→ch (CHS.cup d)    (pop k) = chsᴰ→ch d k
  chsᴰ→ch (CHS.cdown d)  (pop k) = chsᴰ→ch d k
  chsᴰ→ch (CHS.cpair d)  (pop k) = chsᴰ→ch d k
  chsᴰ→ch (CHS.cfst d)   (pop k) = chsᴰ→ch d k
  chsᴰ→ch (CHS.csnd d)   (pop k) = chsᴰ→ch d k
  chsᴰ→ch (CHS.unit d)   (pop k) = chsᴰ→ch d k

  -- FIXME: I can’t even postulate this.
  -- postulate
  --   ᴬlem₁ : ∀ {Ξ₁ Ξ₂ A B} {d₁ : CHS.⊢ᴰ Ξ₁ , A CHS.▻ B} {d₂ : CHS.⊢ᴰ Ξ₂ , A}
  --           → chsᴰ→ch (CHS.appᴰ d₁ d₂) ≡ CH.app (chsᴰ→ch d₁ top) (chsᴰ→ch d₂ top)

  postulate
    oops : ∀ {A B Ξ₁ Ξ₂} {d₁ : CHS.⊢ᴰ Ξ₁ , A CHS.▻ B} {d₂ : CHS.⊢ᴰ Ξ₂ , A}
           → CH.⊢ chsᴾ→chᴾ CHS.[ d₁ ] CH.⦂ (chsᵀ→chᵀ A CH.▻ chsᵀ→chᵀ B) CH.▻
                    chsᴾ→chᴾ CHS.[ d₂ ] CH.⦂ chsᵀ→chᵀ A CH.▻
                    chsᴾ→chᴾ CHS.[ CHS.appᴰ d₁ d₂ ] CH.⦂ chsᵀ→chᵀ B

  chs→ch : ∀ {A} → CHS.⊢ A → CH.⊢ (chsᵀ→chᵀ A)
  chs→ch (Ξ , d) = chsᴰ→ch d top

-- Translation from closed Hilbert-style to closed Hilbert-style sequential.

mutual
  chᵀ→chsᵀ : CH.Ty → CHS.Ty
  chᵀ→chsᵀ (CH.α P)   = CHS.α P
  chᵀ→chsᵀ (A CH.▻ B) = chᵀ→chsᵀ A CHS.▻ chᵀ→chsᵀ B
  chᵀ→chsᵀ (p CH.⦂ A) with chᴾ→chsᴾ p
  chᵀ→chsᵀ (p CH.⦂ A) | (Ξ , p′) = p′ CHS.⦂ chᵀ→chsᵀ A
  chᵀ→chsᵀ (A CH.∧ B) = chᵀ→chsᵀ A CHS.∧ chᵀ→chsᵀ B
  chᵀ→chsᵀ CH.⊤      = CHS.⊤

  chᴾ→chsᴾ : ∀ {A} → CH.Proof A → ∃ (λ Ξ → CHS.Proof Ξ (chᵀ→chsᵀ A))
  chᴾ→chsᴾ CH.[ d ] with ch→chs d
  chᴾ→chsᴾ CH.[ d ] | (Ξ , d′) = Ξ , CHS.[ d′ ]

  ch→chs : ∀ {A} → CH.⊢ A → CHS.⊢ (chᵀ→chsᵀ A)
  ch→chs (CH.app d₁ d₂) = CHS.app (ch→chs d₁) (ch→chs d₂)
  ch→chs CH.ci          = ∅ , CHS.ci CHS.nil
  ch→chs CH.ck          = ∅ , CHS.ck CHS.nil
  ch→chs CH.cs          = ∅ , CHS.cs CHS.nil
  ch→chs (CH.box d)     = CHS.box (ch→chs d)
  ch→chs CH.cdist       = ∅ , CHS.cdist CHS.nil
  ch→chs CH.cup         = ∅ , CHS.cup CHS.nil
  ch→chs CH.cdown       = ∅ , CHS.cdown CHS.nil
  ch→chs CH.cpair       = ∅ , CHS.cpair CHS.nil
  ch→chs CH.cfst        = ∅ , CHS.cfst CHS.nil
  ch→chs CH.csnd        = ∅ , CHS.csnd CHS.nil
  ch→chs CH.unit        = ∅ , CHS.unit CHS.nil


------------------------------------------------------------------------
-- Lemmas about the initial bag index
------------------------------------------------------------------------

module TotalParserCombinators.InitialBag where

open import Category.Monad
open import Data.List
import Data.List.Categorical
open import Data.List.Membership.Propositional using (_∈_)
open import Data.List.Membership.Propositional.Properties as M
  hiding (⊛-∈↔)
open import Data.List.Relation.Binary.BagAndSetEquality
  using () renaming (_∼[_]_ to _List-∼[_]_)
open import Data.List.Relation.Unary.Any
open import Data.List.Relation.Unary.Any.Properties
open import Data.Maybe hiding (_>>=_)
open import Data.Product
open import Data.Sum
open import Function.Base
open import Function.Equality using (_⟨$⟩_)
open import Function.Inverse as Inv using (_↔_)
import Function.Related as Related
open import Level
open import Relation.Binary.PropositionalEquality as P
  using (_≡_; refl)
open import Relation.Binary.HeterogeneousEquality as H
  using (refl) renaming (_≅_ to _≅′_)

open Inv.Inverse
open RawMonadPlus {f = zero} Data.List.Categorical.monadPlus
  using () renaming (_⊛_ to _⊛′_)
open Related using (SK-sym)

open import TotalParserCombinators.Parser
open import TotalParserCombinators.Semantics

------------------------------------------------------------------------
-- Optimisation

private

  abstract

    ⊛-∈↔ : ∀ {A B : Set} (fs : List (A → B)) {xs y} →
           (∃₂ λ f x → f ∈ fs × x ∈ xs × y ≡ f x) ↔ y ∈ (fs ⊛′ xs)
    ⊛-∈↔ = M.⊛-∈↔

------------------------------------------------------------------------
-- Sanity check: The initial set index is correctly defined

mutual

  complete : ∀ {Tok R xs x} {p : Parser Tok R xs} →
             x ∈ p · [] → x ∈ xs
  complete x∈p = complete′ x∈p refl

  private

    complete′ : ∀ {Tok R xs x s} {p : Parser Tok R xs} →
                x ∈ p · s → s ≡ [] → x ∈ xs
    complete′ return                                                    refl = to return↔            ⟨$⟩ refl
    complete′ (∣-left      x∈p₁)                                        refl = to ++↔                ⟨$⟩ inj₁ (complete x∈p₁)
    complete′ (∣-right xs₁ x∈p₂)                                        refl = to (++↔ {P = _≡_ _}
                                                                                {xs = xs₁})   ⟨$⟩ inj₂ (complete x∈p₂)
    complete′ (<$> x∈p)                                                 refl = to (map-∈↔ _)         ⟨$⟩ (_ , complete x∈p , refl)
    complete′ (_⊛_   {s₁ = []} {xs = just _}                f∈p₁ x∈p₂)  refl = to (⊛-∈↔ _)           ⟨$⟩ (_ , _ , complete f∈p₁
                                                                                , complete x∈p₂ , refl)
    complete′ (_>>=_ {s₁ = []} {xs = just xs} {f = just _}  x∈p₁ y∈p₂x) refl = to (>>=-∈↔ {xs = xs}) ⟨$⟩ (_ , complete x∈p₁
                                                                                , complete y∈p₂x)
    complete′ (cast {xs₁≈xs₂ = xs₁≈xs₂} x∈p)                            refl = to xs₁≈xs₂            ⟨$⟩ complete x∈p

    complete′ (_⊛_   {s₁ = []} {xs = nothing}               f∈p₁ x∈p₂)  refl with complete x∈p₂
    ... | ()
    complete′ (_>>=_ {s₁ = []} {xs = nothing}               x∈p₁ y∈p₂x) refl with complete x∈p₁
    ... | ()
    complete′ (_>>=_ {s₁ = []} {xs = just _}  {f = nothing} x∈p₁ y∈p₂x) refl with complete y∈p₂x
    ... | ()

    complete′ token                     ()
    complete′ (_⊛_    {s₁ = _ ∷ _} _ _) ()
    complete′ (_>>=_  {s₁ = _ ∷ _} _ _) ()
    complete′ (nonempty _)              ()

mutual

  sound : ∀ {Tok R xs x} (p : Parser Tok R xs) →
          x ∈ xs → x ∈ p · []
  sound (return x)              (here refl) = return
  sound (_∣_ {xs₁ = xs₁} p₁ p₂) x∈xs with from (++↔ {P = _≡_ _} {xs = xs₁}) ⟨$⟩ x∈xs
  ... | inj₁ x∈xs₁ = ∣-left      (sound p₁ x∈xs₁)
  ... | inj₂ x∈xs₂ = ∣-right xs₁ (sound p₂ x∈xs₂)
  sound (f <$> p) x∈xs with from (map-∈↔ f) ⟨$⟩ x∈xs
  ... | (y , y∈xs , refl) = <$> sound p y∈xs
  sound (_⊛_ {fs = fs} {just xs} p₁ p₂) y∈ys
    with from (⊛-∈↔ (flatten fs) {xs = xs}) ⟨$⟩ y∈ys
  sound (_⊛_ {fs = nothing} {just xs} p₁ p₂)  y∈ys | (f′ , x′ , ()    , x′∈xs , refl)
  sound (_⊛_ {fs = just fs} {just xs} p₁ p₂)  y∈ys | (f′ , x′ , f′∈fs , x′∈xs , refl) =
    _⊛_ {fs = ○} {xs = ○} (sound p₁ f′∈fs) (sound p₂ x′∈xs)
  sound (_>>=_ {xs = zs} {f = just f} p₁ p₂) y∈ys
    with from (>>=-∈↔ {xs = flatten zs} {f = f}) ⟨$⟩ y∈ys
  ... | (x , x∈zs , y∈fx) =
    _>>=_ {xs = zs} {f = just f} (sound p₁ x∈zs) (sound′ (p₂ x) x∈zs y∈fx)
  sound (cast xs₁≈xs₂ p) x∈xs = cast (sound p (from xs₁≈xs₂ ⟨$⟩ x∈xs))

  sound (return _)                 (there ())
  sound fail                       ()
  sound token                      ()
  sound (_⊛_   {xs = nothing} _ _) ()
  sound (_>>=_ {f  = nothing} _ _) ()
  sound (nonempty _)               ()

  private

    sound′ : ∀ {Tok R₁ R₂ x y xs} {zs : Maybe (List R₁)}
               (p : ∞⟨ zs ⟩Parser Tok R₂ xs) →
               x ∈ flatten zs → y ∈ xs → y ∈ ♭? p · []
    sound′ {zs = just _}  p _  = sound p
    sound′ {zs = nothing} p ()

mutual

  sound∘complete : ∀ {Tok R xs x} {p : Parser Tok R xs}
                   (x∈p : x ∈ p · []) →
                   sound p (complete x∈p) ≡ x∈p
  sound∘complete x∈p = H.≅-to-≡ (sound∘complete′ x∈p refl)

  private

    sound∘complete′ : ∀ {Tok R xs x s} {p : Parser Tok R xs}
                      (x∈p : x ∈ p · s) (s≡[] : s ≡ []) →
                      sound p (complete′ x∈p s≡[]) ≅′ x∈p
    sound∘complete′ return                                refl = refl
    sound∘complete′ (∣-left {xs₁ = xs₁} {xs₂ = xs₂} x∈p₁) refl
      rewrite left-inverse-of (++↔ {P = _≡_ _} {xs = xs₁} {ys = xs₂}) (inj₁ (complete x∈p₁)) =
        H.cong _∈_·_.∣-left (sound∘complete′ x∈p₁ refl)
    sound∘complete′ (∣-right xs₁ x∈p₂) refl
      rewrite left-inverse-of (++↔ {P = _≡_ _} {xs = xs₁}) (inj₂ (complete x∈p₂)) =
        H.cong (_∈_·_.∣-right xs₁) (sound∘complete′ x∈p₂ refl)
    sound∘complete′ (<$>_ {f = f} x∈p) refl
      rewrite left-inverse-of (map-∈↔ f) (_ , complete x∈p , refl) =
        H.cong _∈_·_.<$>_ (sound∘complete′ x∈p refl)
    sound∘complete′ (_⊛_ {s₁ = []} {fs = fs} {xs = just xs} f∈p₁ x∈p₂) refl
      with complete f∈p₁ | complete x∈p₂
      | from inv ⟨$⟩ (to inv ⟨$⟩ (_ , _ , complete f∈p₁ , complete x∈p₂ , refl))
      | left-inverse-of inv (_ , _ , complete f∈p₁ , complete x∈p₂ , refl)
      | sound∘complete f∈p₁ | sound∘complete x∈p₂
      where inv = ⊛-∈↔ (flatten fs) {xs = xs}
    sound∘complete′ (_⊛_ {s₁ = []} {fs = nothing} {xs = just xs} _ _) refl | () | _ | _ | _ | _ | _
    sound∘complete′ (_⊛_ {s₁ = []} {fs = just fs} {xs = just xs} {p₁ = p₁} {p₂ = p₂}
                         .(sound p₁ ∈fs) .(sound p₂ ∈xs)) refl
      | ∈fs | ∈xs | ._ | refl | refl | refl = refl
    sound∘complete′ (_>>=_ {x = x} {y = y} {s₁ = []} {xs = just _} {f = just f} {p₁ = p₁} {p₂ = p₂} x∈p₁ y∈p₂x) refl
      rewrite left-inverse-of (>>=-∈↔ {f = f}) (_ , complete x∈p₁ , complete y∈p₂x)
         with sound p₁ (complete x∈p₁)
            | sound∘complete x∈p₁
            | sound′ (p₂ x) (complete x∈p₁) (complete y∈p₂x)
            | helper (p₂ x) (complete x∈p₁) y∈p₂x
         where
         helper : ∀ {Tok R₁ R₂ x y ys} {xs : Maybe (List R₁)}
                  (p : ∞⟨ xs ⟩Parser Tok R₂ ys) →
                  (x∈xs : x ∈ flatten xs) (y∈p : y ∈ ♭? p · []) →
                  sound′ p x∈xs (complete y∈p) ≡ y∈p
         helper {xs = nothing} p () _
         helper {xs = just _}  p _  y∈p = sound∘complete y∈p
    ... | ._ | refl | ._ | refl = refl
    sound∘complete′ (cast {xs₁≈xs₂ = xs₁≈xs₂} x∈p)             refl with complete x∈p | sound∘complete x∈p
    sound∘complete′ (cast {xs₁≈xs₂ = xs₁≈xs₂} .(sound _ x∈xs)) refl | x∈xs | refl
      rewrite left-inverse-of xs₁≈xs₂ x∈xs = refl

    sound∘complete′ (_⊛_    {s₁ = []} {xs = nothing}              _    x∈p₂)  refl with complete x∈p₂
    ... | ()
    sound∘complete′ (_>>=_  {s₁ = []} {xs = nothing}              x∈p₁ y∈p₂x) refl with complete x∈p₁
    ... | ()
    sound∘complete′ (_>>=_  {s₁ = []} {xs = just _} {f = nothing} x∈p₁ y∈p₂x) refl with complete y∈p₂x
    ... | ()

    sound∘complete′ token                     ()
    sound∘complete′ (_⊛_    {s₁ = _ ∷ _} _ _) ()
    sound∘complete′ (_>>=_  {s₁ = _ ∷ _} _ _) ()
    sound∘complete′ (nonempty _)              ()

complete∘sound : ∀ {Tok R xs x}
                 (p : Parser Tok R xs) (x∈p : x ∈ xs) →
                 complete (sound p x∈p) ≡ x∈p
complete∘sound (return x)              (here refl) = refl
complete∘sound (_∣_ {xs₁ = xs₁} p₁ p₂) x∈xs
  with from             (++↔ {P = _≡_ _} {xs = xs₁}) ⟨$⟩ x∈xs
     | right-inverse-of (++↔ {P = _≡_ _} {xs = xs₁})     x∈xs
complete∘sound (_∣_ {xs₁ = xs₁} p₁ p₂) .(to ++↔                          ⟨$⟩ inj₁ x∈xs₁) | inj₁ x∈xs₁ | refl =
  P.cong (_⟨$⟩_ (to ++↔)                          ∘ inj₁) $ complete∘sound p₁ x∈xs₁
complete∘sound (_∣_ {xs₁ = xs₁} p₁ p₂) .(to (++↔ {P = _≡_ _} {xs = xs₁}) ⟨$⟩ inj₂ x∈xs₂) | inj₂ x∈xs₂ | refl =
  P.cong (_⟨$⟩_ (to (++↔ {P = _≡_ _} {xs = xs₁})) ∘ inj₂) $ complete∘sound p₂ x∈xs₂
complete∘sound (f <$> p) x∈xs
  with             from (map-∈↔ f) ⟨$⟩ x∈xs
     | right-inverse-of (map-∈↔ f)     x∈xs
complete∘sound (f <$> p) .(to (map-∈↔ f) ⟨$⟩ (y , y∈xs , refl)) | (y , y∈xs , refl) | refl =
  P.cong (λ y∈ → to (map-∈↔ _) ⟨$⟩ (y , y∈ , refl))
         (complete∘sound p y∈xs)
complete∘sound (_⊛_ {fs = fs} {just xs} p₁ p₂) y∈ys
  with from             (⊛-∈↔ (flatten fs) {xs = xs}) ⟨$⟩ y∈ys
     | right-inverse-of (⊛-∈↔ (flatten fs) {xs = xs})     y∈ys
complete∘sound (_⊛_ {fs = nothing} {xs = just xs} p₁ p₂)
               y∈ys                                                | (f′ , x′ , ()    , x′∈xs , refl) | _
complete∘sound (_⊛_ {fs = just fs} {xs = just xs} p₁ p₂)
               .(to (⊛-∈↔ _) ⟨$⟩ (f′ , x′ , f′∈fs , x′∈xs , refl)) | (f′ , x′ , f′∈fs , x′∈xs , refl) | refl =
  P.cong₂ (λ f′∈ x′∈ → to (⊛-∈↔ _) ⟨$⟩ (f′ , x′ , f′∈ , x′∈ , refl))
          (complete∘sound p₁ f′∈fs)
          (complete∘sound p₂ x′∈xs)
complete∘sound (_>>=_ {xs = zs}      {just f} p₁ p₂) y∈ys
  with from             (>>=-∈↔ {xs = flatten zs} {f = f}) ⟨$⟩ y∈ys
     | right-inverse-of (>>=-∈↔ {xs = flatten zs} {f = f})     y∈ys
complete∘sound (_>>=_ {xs = nothing} {just f} p₁ p₂) ._                                           | (x , ()   , y∈fx) | refl
complete∘sound (_>>=_ {xs = just zs} {just f} p₁ p₂) .(to (>>=-∈↔ {f = f}) ⟨$⟩ (x , x∈zs , y∈fx)) | (x , x∈zs , y∈fx) | refl =
  P.cong₂ (λ x∈ y∈ → to (>>=-∈↔ {f = f}) ⟨$⟩ (x , x∈ , y∈))
          (complete∘sound p₁ x∈zs)
          (complete∘sound (p₂ x) y∈fx)
complete∘sound (cast xs₁≈xs₂ p) x∈xs
  rewrite complete∘sound p (from xs₁≈xs₂ ⟨$⟩ x∈xs) =
    right-inverse-of xs₁≈xs₂ x∈xs

complete∘sound (return _)                 (there ())
complete∘sound fail                       ()
complete∘sound token                      ()
complete∘sound (_⊛_   {xs = nothing} _ _) ()
complete∘sound (_>>=_ {f  = nothing} _ _) ()
complete∘sound (nonempty _)               ()

correct : ∀ {Tok R xs x} {p : Parser Tok R xs} → x ∈ p · [] ↔ x ∈ xs
correct {p = p} = record
  { to         = P.→-to-⟶ complete
  ; from       = P.→-to-⟶ $ sound p
  ; inverse-of = record
    { left-inverse-of  = sound∘complete
    ; right-inverse-of = complete∘sound p
    }
  }

------------------------------------------------------------------------
-- Equal parsers have equal initial bags/sets

-- Note that this property holds also for the subbag and subset
-- relations.

cong : ∀ {k Tok R xs₁ xs₂}
         {p₁ : Parser Tok R xs₁} {p₂ : Parser Tok R xs₂} →
       p₁ ∼[ k ] p₂ → initial-bag p₁ List-∼[ k ] initial-bag p₂
cong {xs₁ = xs₁} {xs₂} {p₁} {p₂} p₁≈p₂ {x} =
  (x ∈ xs₁)    ↔⟨ SK-sym correct ⟩
  x ∈ p₁ · []  ∼⟨ p₁≈p₂ ⟩
  x ∈ p₂ · []  ↔⟨ correct ⟩
  (x ∈ xs₂)    ∎
  where open Related.EquationalReasoning


{-# OPTIONS --without-K #-}
module Model.Type.Core where

open import Cats.Category
open import Relation.Binary using (IsEquivalence)

open import Model.RGraph as RG using (RGraph)
open import Util.HoTT.Equiv
open import Util.HoTT.FunctionalExtensionality
open import Util.HoTT.HLevel
open import Util.HoTT.Homotopy
open import Util.HoTT.Univalence
open import Util.Prelude hiding (id) renaming (_∘_ to _∘F_)
open import Util.Relation.Binary.LogicalEquivalence using
  ( _↔_ ; forth ; back ; ↔-reflexive )
open import Util.Relation.Binary.PropositionalEquality using
  ( Σ-≡⁺ ; Σ-≡⁻ ; cast ; subst-subst ; subst-sym-subst ; subst-subst-sym )

open Category._≅_ public
open RG._⇒_

infixr 0 _⇒_
infix  4 _≈_ _≈⟦Type⟧_
infixr 9 _∘_

private
  variable Δ Γ Ψ : RGraph

-- This is an RGraph-indexed family of RGraphs.
record ⟦Type⟧ (Δ : RGraph) : Set₁ where
  no-eta-equality
  field
    ObjHSet : Δ .RG.Obj → HSet 0ℓ

  private
    module M₀ δ = HLevel (ObjHSet δ)
    module M₁ {δ} = HLevel (ObjHSet δ)

  open M₀ public using () renaming
    ( type to Obj )
  open M₁ public using () renaming
    ( level to Obj-IsSet )

  field
    eqHProp : ∀ {δ δ′} → Δ .RG.eq δ δ′ → Obj δ → Obj δ′ → HProp 0ℓ

  private
    module M₂ {δ δ′} (δ≈δ′ : Δ .RG.eq δ δ′) x y   = HLevel (eqHProp δ≈δ′ x y)
    module M₃ {δ δ′} {δ≈δ′ : Δ .RG.eq δ δ′} {x y} = HLevel (eqHProp δ≈δ′ x y)

  open M₂ public using () renaming
    ( type to eq )
  open M₃ public using () renaming
    ( level to eq-IsProp )

  field
    eq-refl : ∀ {δ} (x : Obj δ) → eq (Δ .RG.eq-refl δ) x x

open ⟦Type⟧ public

private
  variable T U V W : ⟦Type⟧ Δ

open RGraph

transportEq : ∀ (T : ⟦Type⟧ Δ) {δ γ} {δ≈γ₀ δ≈γ₁ : Δ .eq δ γ} {x y}
  → T .eq δ≈γ₀ x y → T .eq δ≈γ₁ x y
transportEq {Δ} T = subst (λ p → T .eq p _ _) (Δ .eq-IsProp _ _)

transportObj : ∀ (T : ⟦Type⟧ Δ) {δ γ}
  → δ ≡ γ
  → T .Obj δ
  → T .Obj γ
transportObj T = subst (T .Obj)

abstract
  transportObj-resp : ∀ (T : ⟦Type⟧ Δ) {δ γ}
    → (δ≡γ₀ δ≡γ₁ : δ ≡ γ)
    → ∀ {x y}
    → x ≡ y
    → transportObj T δ≡γ₀ x ≡ transportObj T δ≡γ₁ y
  transportObj-resp {Δ} T refl δ≡γ₁ x≡y
    rewrite Δ .Obj-IsSet δ≡γ₁ refl = x≡y

transportObj-resp-eq : ∀ (T : ⟦Type⟧ Δ) {δ₀ γ₀ δ₁ γ₁}
  → (δ₀≡γ₀ : δ₀ ≡ γ₀) (δ₁≡γ₁ : δ₁ ≡ γ₁)
  → ∀ {x y} {δ₀≈δ₁ : Δ .eq δ₀ δ₁} {γ₀≈γ₁ : Δ .eq γ₀ γ₁}
  → T .eq δ₀≈δ₁ x y
  → T .eq γ₀≈γ₁ (transportObj T δ₀≡γ₀ x) (transportObj T δ₁≡γ₁ y)
transportObj-resp-eq {Δ} T refl refl {δ₀≈δ₁ = δ₀≈δ₁} {γ₀≈γ₁} x≈y
  = transportEq T x≈y

abstract
  transportObj∘transportObj : ∀ (T : ⟦Type⟧ Δ) {δ δ′ δ″}
    → (p : δ ≡ δ′) (q : δ′ ≡ δ″) {x : T .Obj δ}
    → transportObj T q (transportObj T p x) ≡ transportObj T (trans p q) x
  transportObj∘transportObj T p q = subst-subst p

  transportObj-refl : ∀ (T : ⟦Type⟧ Δ) {δ}
    → (p : δ ≡ δ) {x : T .Obj δ}
    → transportObj T p x ≡ x
  transportObj-refl {Δ} T p rewrite Δ .Obj-IsSet p refl = refl

record _⇒_ (T U : ⟦Type⟧ Δ) : Set where
  no-eta-equality
  field
    fobj : ∀ {δ} → Obj T δ → Obj U δ
    feq : ∀ {δ δ′} (δ≈δ′ : Δ .RG.eq δ δ′) {x y}
      → eq T δ≈δ′ x y
      → eq U δ≈δ′ (fobj x) (fobj y)

open _⇒_ public

record _≈_ (f g : T ⇒ U) : Set where
  no-eta-equality
  constructor ≈⁺
  field
    ≈⁻ : ∀ δ (x : Obj T δ) → f .fobj x ≡ g .fobj x

open _≈_ public

≈-isEquivalence : (T U : ⟦Type⟧ Δ) → IsEquivalence (_≈_ {T = T} {U})
≈-isEquivalence T U = record
  { refl = ≈⁺ λ γ x → refl
  ; sym = λ f≈g → ≈⁺ λ γ x → sym (f≈g .≈⁻ γ x)
  ; trans = λ f≈g g≈h → ≈⁺ λ γ x → trans (f≈g .≈⁻ γ x) (g≈h .≈⁻ γ x)
  }

⇒Canon : (T U : ⟦Type⟧ Δ) → Set
⇒Canon {Δ} T U
  = Σ[ fobj ∈ (∀ {δ} → T .Obj δ → U .Obj δ) ]
      (∀ {δ δ′} (δ≈δ′ : Δ .RG.eq δ δ′) {x y}
        → T .eq δ≈δ′ x y → U .eq δ≈δ′ (fobj x) (fobj y))

⇒≅⇒Canon : (T ⇒ U) ≅ ⇒Canon T U
⇒≅⇒Canon = record
  { forth = λ f → f .fobj , f .feq
  ; isIso = record
    { back = λ f → record { fobj = f .proj₁ ; feq = f .proj₂ }
    ; back∘forth = λ where
        record { fobj = fo ; feq = fe } → refl
    ; forth∘back = λ x → refl
    }
  }

⇒Canon-IsSet : (T U : ⟦Type⟧ Δ) → IsSet (⇒Canon T U)
⇒Canon-IsSet T U = Σ-IsSet
  (∀∙-IsSet λ δ → →-IsSet (U .Obj-IsSet))
  (λ _ → ∀∙-IsSet λ δ → ∀∙-IsSet λ δ′ → ∀-IsSet λ δ≈δ′ → ∀∙-IsSet λ x
    → ∀∙-IsSet λ y → →-IsSet (IsProp→IsSet (U .eq-IsProp)))

⇒-IsSet : IsSet (T ⇒ U)
⇒-IsSet {T = T} {U} = ≅-pres-IsOfHLevel 2 (≅-sym ⇒≅⇒Canon) (⇒Canon-IsSet T U)

≈→≡Canon : {f g : T ⇒ U}
  → f ≈ g → ⇒≅⇒Canon .forth f ≡ ⇒≅⇒Canon .forth g
≈→≡Canon {U = U} f≈g = Σ-≡⁺
  ( (funext∙ (funext (λ x → f≈g .≈⁻ _ x)))
  , funext∙ (funext∙ (funext λ a → funext∙ (funext∙
      (funext λ eq → U .eq-IsProp _ _)))) )

≈→≡ : {f g : T ⇒ U} → f ≈ g → f ≡ g
≈→≡ f≈g = ≅-Injective ⇒≅⇒Canon (≈→≡Canon f≈g)

private
  open module M₀ {Δ} {T U : ⟦Type⟧ Δ}
    = IsEquivalence (≈-isEquivalence T U)
    public using () renaming
    ( refl to ≈-refl
    ; sym to ≈-sym
    ; trans to ≈-trans
    ; reflexive to ≈-reflexive )

id : {T : ⟦Type⟧ Δ} → T ⇒ T
id = record
  { fobj = λ x → x
  ; feq = λ γ≈γ′ x → x
  }

_∘_ : {T U V : ⟦Type⟧ Δ} → U ⇒ V → T ⇒ U → T ⇒ V
_∘_ {Γ} f g = record
  { fobj = f .fobj ∘F g .fobj
  ; feq = λ γ≈γ′ → f .feq γ≈γ′ ∘F g .feq γ≈γ′
  }

⟦Types⟧ : RGraph → Category (lsuc 0ℓ) 0ℓ 0ℓ
⟦Types⟧ Δ = record
  { Obj = ⟦Type⟧ Δ
  ; _⇒_ = _⇒_
  ; _≈_ = _≈_
  ; id = id
  ; _∘_ = _∘_
  ; equiv = ≈-isEquivalence _ _
  ; ∘-resp = λ {Δ Γ Ψ f g h i} f≈g h≈i
    → ≈⁺ λ γ x → trans (f≈g .≈⁻ γ (h .fobj x)) (cong (g .fobj) (h≈i .≈⁻ γ x))
  ; id-r = ≈⁺ λ γ x → refl
  ; id-l = ≈⁺ λ γ x → refl
  ; assoc = ≈⁺ λ γ x → refl
  }

module ⟦Types⟧ {Γ} = Category (⟦Types⟧ Γ)

_≈⟦Type⟧_ : (T U : ⟦Type⟧ Δ) → Set₁
_≈⟦Type⟧_ = ⟦Types⟧._≅_

private
  open module M₁ {Δ} = ⟦Types⟧.≅ {Δ} public using () renaming
    ( refl to ≈⟦Type⟧-refl
    ; sym to ≈⟦Type⟧-sym
    ; trans to ≈⟦Type⟧-trans
    ; reflexive to ≈⟦Type⟧-reflexive )

module ≅-Reasoning = ⟦Types⟧.≅-Reasoning
module ≈-Reasoning = ⟦Types⟧.≈-Reasoning

⟦Type⟧Canon : RGraph → Set₁
⟦Type⟧Canon Δ
  = Σ[ ObjHSet ∈ (Δ .Obj → HSet 0ℓ) ]
    Σ[ eq ∈ (∀ {δ δ′} (δ≈δ′ : Δ .eq δ δ′)
        → (x : ObjHSet δ .type) (y : ObjHSet δ′ .type) → HProp 0ℓ) ]
    (∀ {δ} (x : ObjHSet δ .type) → eq (Δ .eq-refl δ) x x .type)

⟦Type⟧≅⟦Type⟧Canon : ∀ {Δ} → ⟦Type⟧ Δ ≅ ⟦Type⟧Canon Δ
⟦Type⟧≅⟦Type⟧Canon {Δ} = record
  { forth = λ T → T .ObjHSet , T .eqHProp , T .eq-refl
  ; isIso = record
    { back = λ T → record
      { ObjHSet = T .proj₁
      ; eqHProp = T .proj₂ .proj₁
      ; eq-refl = T .proj₂ .proj₂
      }
    ; back∘forth = λ where
        record { ObjHSet = x ; eqHProp = y ; eq-refl = z } → refl
    ; forth∘back = λ x → refl
    }
  }

proj₁∘subst : ∀ {α β γ} {A : Set α} (B : A → Set β) (C : ∀ a → B a → Set γ)
  → ∀ {a a′} (p : a ≡ a′) (x : Σ (B a) (C a))
  → proj₁ (subst (λ a → Σ (B a) (C a)) p x) ≡ subst B p (proj₁ x)
proj₁∘subst B C refl x = refl

≈⟦Type⟧→≡Canon
  : T ≈⟦Type⟧ U
  → ⟦Type⟧≅⟦Type⟧Canon .forth T ≡ ⟦Type⟧≅⟦Type⟧Canon .forth U
≈⟦Type⟧→≡Canon {Δ} {T} {U} T≈U
  = Σ-≡⁺ (T≡U , Σ-≡⁺ (T≈≡U≈ , funext∙ (funext (λ x → U .eq-IsProp _ _))))
  where
    T≅U : ∀ δ → T .Obj δ ≅ U .Obj δ
    T≅U δ = record
      { forth = T≈U .forth .fobj
      ; isIso = record
        { back = T≈U .back .fobj
        ; back∘forth = λ x → T≈U .back-forth .≈⁻ _ _
        ; forth∘back = λ x → T≈U .forth-back .≈⁻ _ _
        }
      }

    T≡U : T .ObjHSet ≡ U .ObjHSet
    T≡U = funext λ δ → HLevel-≡⁺ _ _ (≅→≡ (T≅U δ))

    -- "HoTT makes everything so much nicer", they said. "It's all automatic",
    -- they said.
    abstract
      subst-type : ∀ {α n} (A B : HLevel n α)
        → (p : A .type ≡ B .type)
        → (x : A .type)
        → subst type (HLevel-≡⁺ A B p) x ≡ cast p x
      subst-type A B refl x with IsOfHLevel-IsProp _ (A .level) (B .level)
      ... | refl = refl

      -- I believe this equation would be definitional in Cubical TT.
      subst-T≡U : ∀ {δ} x
        → subst (λ T → T δ .type) T≡U x ≡ T≈U .forth .fobj x
      subst-T≡U {δ} x
        = trans (subst-funext (λ δ T → T .type) _ x)
          (trans (subst-type (T .ObjHSet δ) (U .ObjHSet δ) (≅→≡ (T≅U δ)) x)
            (cast-≅→≡ (T≅U δ)))

      go : (Δ : Set) (T U : Δ → HSet 0ℓ) (eqΔ : Δ → Δ → Set)
        → (eqT : ∀ δ δ′ → eqΔ δ δ′ → T δ .type → T δ′ .type → HProp 0ℓ)
        → (eqU : ∀ δ δ′ → eqΔ δ δ′ → U δ .type → U δ′ .type → HProp 0ℓ)
        → (T≡U : T ≡ U)
        → (∀ δ δ′ (δ≈δ′ : eqΔ δ δ′) (x : T δ .type) (y : T δ′ .type)
            → eqT _ _ δ≈δ′ x y .type
            ↔ eqU _ _ δ≈δ′
                (subst (λ T → T δ .type) T≡U x)
                (subst (λ T → T δ′ .type) T≡U y)
                .type)
        → _≡_ {A = ∀ {δ δ′} → eqΔ δ δ′ → U δ .type → U δ′ .type → HProp 0ℓ}
            (subst (λ T → ∀ {δ δ′} → eqΔ δ δ′ → T δ .type → T δ′ .type → HProp 0ℓ)
              T≡U (λ {δ δ′} → eqT δ δ′))
            (λ {δ δ′} → eqU δ δ′)
      go Δ T U eqΔ eqT eqU refl eq = funext∙ λ {δ} → funext∙ λ {δ′}
        → funext λ δ≈δ′ → funext λ x → funext λ y
        → HProp-≡⁺ _ _ (eq δ δ′ δ≈δ′ x y)

      T≈≡U≈ : _
      T≈≡U≈
        = trans
            (proj₁∘subst
              (λ T → ∀ {δ δ′} → Δ .eq δ δ′ → T δ .type → T δ′ .type → HProp 0ℓ)
              (λ T eq → ∀ {δ} (x : T δ .type) → eq (Δ .eq-refl δ) x x .type)
              T≡U
              ((λ {δ δ′} → T .eqHProp {δ} {δ′}), (λ {δ} x → T .eq-refl {δ} x)))
            (go (Δ .Obj) (T .ObjHSet) (U .ObjHSet) (Δ .eq)
              (λ δ δ′ → T .eqHProp {δ} {δ′})
              (λ δ δ′ → U .eqHProp {δ} {δ′})
              T≡U
              λ δ δ′ δ≈δ′ x y → record
                { forth = λ x≈y
                    → subst₂ (λ x y → U .eq δ≈δ′ x y) (sym (subst-T≡U x))
                        (sym (subst-T≡U y)) (T≈U .forth .feq δ≈δ′ x≈y)
                ; back = λ x≈y
                    → subst₂ (λ x y → T .eq δ≈δ′ x y)
                        (trans (cong (T≈U .back .fobj) (subst-T≡U x))
                          (T≈U .back-forth .≈⁻ δ x))
                        (trans (cong (T≈U .back .fobj) (subst-T≡U y))
                          (T≈U .back-forth .≈⁻ δ′ y))
                        (T≈U .back .feq δ≈δ′ x≈y)
                })

≈⟦Type⟧→≡ : T ≈⟦Type⟧ U → T ≡ U
≈⟦Type⟧→≡ T≈U = ≅-Injective ⟦Type⟧≅⟦Type⟧Canon (≈⟦Type⟧→≡Canon T≈U)

subT : ∀ {Δ Ω} → Δ RG.⇒ Ω → ⟦Type⟧ Ω → ⟦Type⟧ Δ
subT {Γ} {Ω} f T = record
  { ObjHSet = T .ObjHSet ∘F f .fobj
  ; eqHProp = T .eqHProp ∘F f .feq
  ; eq-refl = λ x
      → subst (λ p → T .eq p x x) (Ω .eq-IsProp _ _) (T .eq-refl x)
  }

subt : ∀ {Δ Ω} (f : Δ RG.⇒ Ω) {T U : ⟦Type⟧ Ω}
  → T ⇒ U
  → subT f T ⇒ subT f U
subt f g = record
  { fobj = g .fobj
  ; feq = λ γ≈γ′ → g .feq (f .feq γ≈γ′)
  }

subt-∘ : ∀ {Δ Ω} (f : Δ RG.⇒ Ω) {T U V : ⟦Type⟧ Ω}
  → (g : U ⇒ V) (h : T ⇒ U)
  → subt f (g ∘ h) ≈ subt f g ∘ subt f h
subt-∘ f g h = ≈⁺ λ δ γ → refl

subT∘subT : ∀ {Δ Γ Ψ}
  → {f : Δ RG.⇒ Ψ} {g : Γ RG.⇒ Δ} {T : ⟦Type⟧ Ψ}
  → subT g (subT f T) ≈⟦Type⟧ subT (f RG.∘ g) T
subT∘subT = record
  { forth = record { fobj = λ x → x ; feq = λ _ x → x }
  ; back = record { fobj = λ x → x ; feq = λ _ x → x }
  ; back-forth = ≈⁺ λ γ x → refl
  ; forth-back = ≈⁺ λ γ x → refl
  }

castObj : {T U : ⟦Type⟧ Δ} → T ≡ U → ∀ {δ} → Obj T δ → Obj U δ
castObj p {δ} = subst (λ T → Obj T δ) p

≡→≈⟦Type⟧ : {T U : ⟦Type⟧ Δ} → T ≡ U → T ≈⟦Type⟧ U
≡→≈⟦Type⟧ {Δ} {T} {U} T≡U = record
  { forth = record
    { fobj = λ {δ} → castObj T≡U
    ; feq = λ δ≈δ′ x≈y → go T≡U x≈y
    }
  ; back = record
    { fobj = λ {δ} → castObj (sym T≡U)
    ; feq = λ δ≈δ′ x≈y → go (sym T≡U) x≈y
    }
  ; back-forth = ≈⁺ λ δ x → subst-sym-subst T≡U
  ; forth-back = ≈⁺ λ δ x → subst-subst-sym T≡U
  }
  where
    go : {T U : ⟦Type⟧ Δ} (p : T ≡ U) → ∀ {δ δ′} {δ≈δ′ : Δ .eq δ δ′} {x y}
      → eq T δ≈δ′ x y
      → eq U δ≈δ′ (castObj p x) (castObj p y)
    go refl x≈y = x≈y


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

-- Substitution structure by initiality
module SOAS.Metatheory.Substitution {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.Abstract.Monoid

open import SOAS.Coalgebraic.Map
open import SOAS.Coalgebraic.Monoid
open import SOAS.Coalgebraic.Lift

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

open Strength ⅀:Str

private
  variable
    Γ Δ : Ctx
    α β : T

-- Substitution is a 𝕋-parametrised traversal into 𝕋
module Substitution = Traversal 𝕋ᴮ 𝕒𝕝𝕘 id 𝕞𝕧𝕒𝕣

𝕤𝕦𝕓 : 𝕋 ⇾̣ 〖 𝕋 , 𝕋 〗
𝕤𝕦𝕓 = Substitution.𝕥𝕣𝕒𝕧

-- The renaming and algebra structures on 𝕋 are compatible, so 𝕤𝕦𝕓 is coalgebraic
𝕤𝕦𝕓ᶜ : Coalgebraic 𝕋ᴮ 𝕋ᴮ 𝕋ᴮ 𝕤𝕦𝕓
𝕤𝕦𝕓ᶜ = Travᶜ.𝕥𝕣𝕒𝕧ᶜ 𝕋ᴮ 𝕒𝕝𝕘 id 𝕞𝕧𝕒𝕣 𝕋ᴮ idᴮ⇒ Renaming.𝕤𝕖𝕞ᵃ⇒

module 𝕤𝕦𝕓ᶜ = Coalgebraic 𝕤𝕦𝕓ᶜ

-- Compatibility of renaming and substitution
compat : {ρ : Γ ↝ Δ} (t : 𝕋 α Γ) → 𝕣𝕖𝕟 t ρ ≡ 𝕤𝕦𝕓 t (𝕧𝕒𝕣 ∘ ρ)
compat {ρ = ρ} t =  begin 𝕣𝕖𝕟 t ρ           ≡˘⟨ 𝕥𝕣𝕒𝕧-η≈id 𝕋ᴮ id refl ⟩
                          𝕤𝕦𝕓 (𝕣𝕖𝕟 t ρ) 𝕧𝕒𝕣  ≡⟨ 𝕤𝕦𝕓ᶜ.f∘r ⟩
                          𝕤𝕦𝕓 t (𝕧𝕒𝕣 ∘ ρ)   ∎ where open ≡-Reasoning

-- Substitution associativity law
𝕤𝕦𝕓-comp : MapEq₂ 𝕋ᴮ 𝕋ᴮ 𝕒𝕝𝕘 (λ t σ ς → 𝕤𝕦𝕓 (𝕤𝕦𝕓 t σ) ς)
                           (λ t σ ς → 𝕤𝕦𝕓 t (λ v → 𝕤𝕦𝕓 (σ v) ς))
𝕤𝕦𝕓-comp = record
  { φ = id
  ; ϕ = 𝕤𝕦𝕓
  ; χ = 𝕞𝕧𝕒𝕣
  ; f⟨𝑣⟩ = 𝕥≈₁ 𝕥⟨𝕧⟩
  ; f⟨𝑚⟩ = trans (𝕥≈₁ 𝕥⟨𝕞⟩) 𝕥⟨𝕞⟩
  ; f⟨𝑎⟩ = λ{ {σ = σ}{ς}{t} → begin
        𝕤𝕦𝕓 (𝕤𝕦𝕓 (𝕒𝕝𝕘 t) σ) ς
    ≡⟨ 𝕥≈₁ 𝕥⟨𝕒⟩ ⟩
        𝕤𝕦𝕓 (𝕒𝕝𝕘 (str 𝕋ᴮ 𝕋 (⅀₁ 𝕤𝕦𝕓 t) σ)) ς
    ≡⟨ 𝕥⟨𝕒⟩ ⟩
        𝕒𝕝𝕘 (str 𝕋ᴮ 𝕋 (⅀₁ 𝕤𝕦𝕓 (str 𝕋ᴮ 𝕋 (⅀₁ 𝕤𝕦𝕓 t) σ)) ς)
    ≡˘⟨ congr (str-nat₂ 𝕤𝕦𝕓 (⅀₁ 𝕤𝕦𝕓 t) σ) (λ - → 𝕒𝕝𝕘 (str 𝕋ᴮ 𝕋 - ς)) ⟩
        𝕒𝕝𝕘 (str 𝕋ᴮ 𝕋 (str 𝕋ᴮ 〖 𝕋 , 𝕋 〗 (⅀.F₁ (λ { h ς → 𝕤𝕦𝕓 (h ς) }) (⅀₁ 𝕤𝕦𝕓 t)) σ) ς)
    ≡˘⟨ congr ⅀.homomorphism (λ - → 𝕒𝕝𝕘 (str 𝕋ᴮ 𝕋 (str 𝕋ᴮ 〖 𝕋 , 𝕋 〗 - σ) ς)) ⟩
        𝕒𝕝𝕘 (str 𝕋ᴮ 𝕋 (str 𝕋ᴮ 〖 𝕋 , 𝕋 〗 (⅀₁ (λ{ t σ → 𝕤𝕦𝕓 (𝕤𝕦𝕓 t σ)}) t) σ) ς)
    ∎ }
  ; g⟨𝑣⟩ = 𝕥⟨𝕧⟩
  ; g⟨𝑚⟩ = 𝕥⟨𝕞⟩
  ; g⟨𝑎⟩ = λ{ {σ = σ}{ς}{t} → begin
        𝕤𝕦𝕓 (𝕒𝕝𝕘 t) (λ v → 𝕤𝕦𝕓 (σ v) ς)
    ≡⟨ 𝕥⟨𝕒⟩ ⟩
        𝕒𝕝𝕘 (str 𝕋ᴮ 𝕋 (⅀₁ 𝕤𝕦𝕓 t) (λ v → 𝕤𝕦𝕓 (σ v) ς))
    ≡⟨ cong 𝕒𝕝𝕘 (str-dist 𝕋 𝕤𝕦𝕓ᶜ (⅀₁ 𝕤𝕦𝕓 t) (λ {τ} z → σ z) ς) ⟩
        𝕒𝕝𝕘 (str 𝕋ᴮ 𝕋 (str 𝕋ᴮ 〖 𝕋 , 𝕋 〗 (⅀.F₁ (precomp 𝕋 𝕤𝕦𝕓) (⅀₁ 𝕤𝕦𝕓 t)) σ) ς)
    ≡˘⟨ congr ⅀.homomorphism (λ - → 𝕒𝕝𝕘 (str 𝕋ᴮ 𝕋 (str 𝕋ᴮ 〖 𝕋 , 𝕋 〗 - σ) ς)) ⟩
        𝕒𝕝𝕘 (str 𝕋ᴮ 𝕋 (str 𝕋ᴮ 〖 𝕋 , 𝕋 〗 (⅀₁ (λ{ t σ ς → 𝕤𝕦𝕓 t (λ v → 𝕤𝕦𝕓 (σ v) ς)}) t) σ) ς)
    ∎ }
  } where open ≡-Reasoning ; open Substitution

-- Coalgebraic monoid structure on 𝕋
𝕋ᵐ : Mon 𝕋
𝕋ᵐ = record
  { η = 𝕧𝕒𝕣
  ; μ = 𝕤𝕦𝕓
  ; lunit = Substitution.𝕥⟨𝕧⟩
  ; runit = λ{ {t = t} → trans (𝕥𝕣𝕒𝕧-η≈𝕤𝕖𝕞 𝕋ᴮ 𝕋ᵃ id refl) 𝕤𝕖𝕞-id }
  ; assoc = λ{ {t = t} → MapEq₂.≈ 𝕤𝕦𝕓-comp t }
  }

open Mon 𝕋ᵐ using ([_/] ; [_,_/]₂ ; lunit ; runit ; assoc) public

𝕋ᴹ : CoalgMon 𝕋
𝕋ᴹ = record { ᴮ = 𝕋ᴮ ; ᵐ = 𝕋ᵐ ; η-compat = refl ; μ-compat = λ{ {t = t} → compat t } }

-- Corollaries: renaming and simultaneous substitution commutes with
-- single-variable substitution
open import SOAS.ContextMaps.Combinators
𝕣𝕖𝕟[/] : (ρ : Γ ↝ Δ)(b : 𝕋 α (β ∙ Γ))(a : 𝕋 β Γ)
      → 𝕣𝕖𝕟 ([ a /] b) ρ ≡ [ (𝕣𝕖𝕟 a ρ) /] (𝕣𝕖𝕟 b (rlift _ ρ))
𝕣𝕖𝕟[/] ρ b a = begin
      𝕣𝕖𝕟 ([ a /] b) ρ
  ≡⟨ 𝕤𝕦𝕓ᶜ.r∘f ⟩
      𝕤𝕦𝕓 b (λ v → 𝕣𝕖𝕟 (add 𝕋 a 𝕧𝕒𝕣 v) ρ)
  ≡⟨ cong (𝕤𝕦𝕓 b) (dext (λ{ new → refl ; (old y) → Renaming.𝕥⟨𝕧⟩})) ⟩
      𝕤𝕦𝕓 b (λ v → add 𝕋 (𝕣𝕖𝕟  a ρ) 𝕧𝕒𝕣 (rlift _ ρ v))
  ≡˘⟨ 𝕤𝕦𝕓ᶜ.f∘r ⟩
      [ 𝕣𝕖𝕟 a ρ /] (𝕣𝕖𝕟 b (rlift _ ρ))
  ∎ where open ≡-Reasoning

𝕤𝕦𝕓[/] : (σ : Γ ~[ 𝕋 ]↝ Δ)(b : 𝕋 α (β ∙ Γ))(a : 𝕋 β Γ)
      → 𝕤𝕦𝕓 ([ a /] b) σ ≡ [ 𝕤𝕦𝕓 a σ /] (𝕤𝕦𝕓 b (lift 𝕋ᴮ ⌈ β ⌋ σ))
𝕤𝕦𝕓[/] {β = β} σ b a = begin
      𝕤𝕦𝕓 ([ a /] b) σ
  ≡⟨ assoc ⟩
      𝕤𝕦𝕓 b (λ v → 𝕤𝕦𝕓 (add 𝕋 a 𝕧𝕒𝕣 v) σ)
  ≡⟨ cong (𝕤𝕦𝕓 b) (dext (λ{ new → sym lunit ; (old v) → sym (begin
        𝕤𝕦𝕓 (𝕣𝕖𝕟 (σ v) old) (add 𝕋 (𝕤𝕦𝕓 a σ) 𝕧𝕒𝕣)
    ≡⟨ 𝕤𝕦𝕓ᶜ.f∘r ⟩
        𝕤𝕦𝕓 (σ v) (λ v → add 𝕋 (𝕤𝕦𝕓 a σ) 𝕧𝕒𝕣 (old v))
    ≡⟨ cong (𝕤𝕦𝕓 (σ v)) (dext (λ{ new → refl ; (old v) → refl})) ⟩
        𝕤𝕦𝕓 (σ v) 𝕧𝕒𝕣
    ≡⟨ runit ⟩
        σ v
    ≡˘⟨ lunit ⟩
        𝕤𝕦𝕓 (𝕧𝕒𝕣 v) σ
    ∎)})) ⟩
      𝕤𝕦𝕓 b (λ v → 𝕤𝕦𝕓 (lift 𝕋ᴮ ⌈ β ⌋ σ v) (add 𝕋 (𝕤𝕦𝕓 a σ) 𝕧𝕒𝕣))
  ≡˘⟨ assoc ⟩
      [ 𝕤𝕦𝕓 a σ /] (𝕤𝕦𝕓 b (lift 𝕋ᴮ ⌈ β ⌋ σ))
  ∎ where open ≡-Reasoning


module Typed.LTLCRef where

open import Data.List.Relation.Ternary.Interleaving.Propositional
open import Relation.Unary hiding (_∈_)
open import Relation.Unary.PredicateTransformer using (Pt)
open import Function
open import Category.Monad

open import Relation.Ternary.Separation
open import Relation.Ternary.Separation.Allstar
open import Relation.Ternary.Separation.Morphisms
open import Relation.Ternary.Separation.Monad
open import Relation.Ternary.Separation.Monad.Reader

open import Prelude

data Ty : Set where
  unit : Ty
  ref  : Ty → Ty
  prod : Ty → Ty → Ty
  _⊸_  : (a b : Ty) → Ty

Ctx  = List Ty
CtxT = List Ty → List Ty

open import Relation.Ternary.Separation.Construct.List Ty
open import Relation.Ternary.Separation.Construct.Market
open import Relation.Ternary.Separation.Construct.Product

infixr 20 _◂_
_◂_ : Ty → CtxT → CtxT
(x ◂ f) Γ = x ∷ f Γ

variable a b c : Ty
variable ℓv  : Level
variable τ   : Set ℓv
variable Γ Γ₁ Γ₂ Γ₃ : List τ

data Exp : Ty → Ctx → Set where
  -- base type
  tt       : ε[ Exp unit ]
  letunit  : ∀[ Exp unit ✴ Exp a ⇒ Exp a ]

  -- linear λ calculus
  var      : ∀[ Just a ⇒ Exp a ]
  lam      : ∀[ (a ◂ id ⊢ Exp b) ⇒ Exp (a ⊸ b) ]
  ap       : ∀[ Exp (a ⊸ b) ✴ Exp a ⇒ Exp b ]

  -- products
  pair    : ∀[ Exp a ✴ Exp b ⇒ Exp (prod a b) ]
  letpair : ∀[ Exp (prod a b) ✴ (λ Γ → a ∷ b ∷ Γ) ⊢ Exp c ⇒ Exp c ]

  -- state
  ref     : ε[ Exp a ⇒ Exp (ref a) ]
  swaps   : ∀[ Exp (ref a) ✴ Exp b ⇒ Exp (prod a (ref b)) ]
  del     : ε[ Exp (ref unit) ⇒ Exp unit ]

-- store types
ST = List Ty

-- values
data Val : Ty → Pred ST 0ℓ where
  tt    : ε[ Val unit ]
  clos  : Exp b (a ∷ Γ) → ∀[ Allstar Val Γ ⇒ Val (a ⊸ b) ]
  ref   : ∀[ Just a ⇒ Val (ref a) ]
  pair  : ∀[ Val a ✴ Val b ⇒ Val (prod a b) ]

{- The 'give-it-to-me-straight' semantics -}

Store : ST → ST → Set
Store = Allstar Val

-- {- First attempt -- evaluation without a frame, seems simple enough... -}
-- eval₁ : ∀ {Ψ Γ} → Exp a Γ → Allstar Val Γ Φ₁ → Store Ψ Φ₂ → Φ₁ ⊎ Φ₂ ≣ Ψ →
--         ∃ λ Ψ' → ∃₂ λ Φ₃ Φ₄ → Store Ψ' Φ₃ × Val a Φ₄ × Φ₃ ⊎ Φ₄ ≣ Ψ'

-- eval₁ (num x) nil μ σ = -, -, -, μ , num x , ⊎-comm σ

-- eval₁ (lam e) env μ σ = -, -, -, μ , (clos e env) , ⊎-comm σ

-- eval₁ (ap (f ×⟨ σ ⟩ e)) env μ σ₂ =
--   let
--     env₁ ×⟨ σ₃ ⟩ env₂ = repartition σ env
--     _ , τ₁ , τ₂ = ⊎-assoc (⊎-comm σ₃) σ₂

--   {- Oops, store contains more stuff than used; i.e. we have a frame -}
--   in case eval₁ f env₁ μ {!τ₂!} of λ where
--     (_ , _ , _ , μ' , clos e env₃ , σ₄) → {!!}

-- eval₁ (var x) = {!!}

-- eval₁ (ref e) = {!!}

-- eval₁ (deref e) = {!!}

-- eval₁ (asgn x) = {!!}

-- {- First attempt -- evaluation *with* a frame. Are you sure want this? -}
-- eval₂ : ∀ {Ψ Γ Φf} → Exp a Γ → Allstar Val Γ Φ₁ → Store Ψ Φ₂ → Φ₁ ⊎ Φ₂ ≣ Φ → Φ ⊎ Φf ≣ Ψ →
--         ∃₂ λ Φ' Ψ' → ∃₂ λ Φ₃ Φ₄ → Store Ψ' Φ₃ × Val a Φ₄ × Φ₃ ⊎ Φ₄ ≣ Φ' × Φ' ⊎ Φf ≣ Ψ'
-- eval₂ (num x) nil μ σ₁ σ₂ =
--   case ⊎-id⁻ˡ σ₁ of λ where refl → -, -, -, -, μ , num x , ⊎-idʳ , σ₂
-- eval₂ (lam x) env μ σ₁ σ₂ = {!!}

-- eval₂ (pair (e₁ ×⟨ σ ⟩ e₂)) env μ σ₁ σ₂ =
--   let
--     env₁ ×⟨ σ₃ ⟩ env₂ = repartition σ env
--     _ , τ₁ , τ₂ = ⊎-assoc (⊎-comm σ₃) σ₁ -- separation between sub-env and store
--     _ , τ₃ , τ₄ = ⊎-assoc (⊎-comm τ₁) σ₂ -- compute the frame

--   in case eval₂ e₁ env₁ μ τ₂ τ₃ of λ where
--     (_ , _ , _ , _ , μ' , v₁ , σ₄ , σ₅) →
--        let v = eval₂ e₂ env₂ μ' {!τ₁!} {!!} in {!!}

-- eval₂ (var x) env μ σ₁ σ₂ = {!!}
-- eval₂ (ref e) env μ σ₁ σ₂ = {!!}
-- eval₂ (deref e) env μ σ₁ σ₂ = {!!}
-- eval₂ (asgn x) env μ σ₁ σ₂ = {!!}
-- eval₂ (ap (f ×⟨ σ ⟩ e)) env μ σ₁ σ₂ = {!!}

{- The monadic semantics -}

module _ {i : Size} where
  open import Relation.Ternary.Separation.Monad.Delay public
  open import Relation.Ternary.Separation.Monad.State
  open import Relation.Ternary.Separation.Monad.State.Heap Val

  open HeapOps (Delay i) {{ monad = delay-monad }}
    using (state-monad; newref; read; write; Cells)
    public

  open ReaderTransformer id-morph Val (StateT (Delay i) Cells)
    {{ monad = state-monad }}
    renaming (Reader to M'; reader-monad to monad)
    public

  open Monads.Monad monad public
  open Monads using (_&_; str; typed-str) public

M : Size → (Γ₁ Γ₂ : Ctx) → Pt ST 0ℓ
M i = M' {i}

mutual
  eval⊸ : ∀ {i Γ} → Exp (a ⊸ b) Γ → ∀[ Val a ⇒ M i Γ ε (Val b) ]
  eval⊸ e v = do
    clos e env ×⟨ σ₂ ⟩ v ← ►eval e & v
    empty                ← append (cons (v ×⟨ ⊎-comm σ₂ ⟩ env))
    ►eval e

  eval : ∀ {i Γ} → Exp a Γ → ε[ M i Γ ε (Val a) ]

  eval tt = do
    return tt

  eval (letunit (e₁ ×⟨ Γ≺ ⟩ e₂)) = do
    tt ← frame Γ≺ (►eval e₁)
    ►eval e₂

  eval (var refl) = do
    lookup

  eval (lam e) = do
    env ← ask
    return (clos e env)

  eval (pair (e₁ ×⟨ Γ≺ ⟩ e₂)) = do
    v₁    ← frame Γ≺ (►eval e₁)
    v₂✴v₁ ← ►eval e₂ & v₁
    return (pair (✴-swap v₂✴v₁))

  eval (letpair (e₁ ×⟨ Γ≺ ⟩ e₂)) = do
    pair (v₁ ×⟨ σ ⟩ v₂) ← frame Γ≺ (►eval e₁)
    empty               ← prepend (cons (v₁ ×⟨ σ ⟩ (singleton v₂)))
    ►eval e₂

  eval (ap (f ×⟨ Γ≺ ⟩ e)) = do
    v ← frame (⊎-comm Γ≺) (►eval e)
    eval⊸ f v

  eval (ref e) = do
    v ← ►eval e
    r ← liftM (newref v)
    return (ref r)

  eval (swaps (e₁ ×⟨ Γ≺ ⟩ e₂)) = do
    ref ra ← frame Γ≺ (►eval e₁)
    vb ×⟨ σ₁ ⟩ ra ← ►eval e₂ & ra
    rb ×⟨ σ₂ ⟩ va ← liftM (write (ra ×⟨ ⊎-comm σ₁ ⟩ vb))
    return (pair (va ×⟨ (⊎-comm σ₂) ⟩ (ref rb)))

  eval (del e) = do
    ref r ← ►eval e
    liftM (read r)

  ►eval : ∀ {i Γ} → Exp a Γ → ε[ M i Γ ε (Val a) ]
  app (app (►eval e) env σ) μ σ' = later (λ where .force → app (app (eval e) env σ) μ σ')


open import Data.Product using ( _,_ ; proj₁ ; proj₂ )
open import Web.Semantic.DL.ABox.Interp using
  ( Interp ; _*_ ; ⌊_⌋ ; ind )
open import Web.Semantic.DL.ABox.Interp.Morphism using
  ( _≲_ ; _**_ ; ≲-refl )
open import Web.Semantic.DL.ABox.Model using
  ( _⊨a_ ; _⊨b_ ; *-resp-⟨ABox⟩ ; ⊨a-resp-≲ )
open import Web.Semantic.DL.Category.Object using
  ( Object ; _,_ ; IN ; fin ; iface )
open import Web.Semantic.DL.Category.Morphism using
  ( _⇒_ ; _,_ ; BN ; impl
  ; apply ; apply✓ ; apply-init )
open import Web.Semantic.DL.Category.Wiring using
  ( identity ; wires-≈ ; wires-≈⁻¹ )
open import Web.Semantic.DL.Category.Composition using
  ( _∙_ ; ⊨a-intro-⟫ ; ⊨b-intro-⟫ )
open import Web.Semantic.DL.Integrity using ( med-≲ ; init-med )
open import Web.Semantic.DL.KB using ( _,_ )
open import Web.Semantic.DL.KB.Model using ( _⊨_ )
open import Web.Semantic.DL.Signature using ( Signature )
open import Web.Semantic.DL.TBox using ( TBox ; _,_ )
open import Web.Semantic.DL.TBox.Interp using ( _⊨_≈_ )
open import Web.Semantic.Util using
  ( id ; _⊕_⊕_ ; inode ; bnode ; enode ; left ; right )

module Web.Semantic.DL.Category.Properties.Composition.Lemmas
  {Σ : Signature} {S T : TBox Σ} where

identity-elim : ∀ (A : Object S T) I → (I ⊨a impl (identity A)) →
  ∀ x → (⌊ I ⌋ ⊨ ind I (inode x) ≈ ind I (enode x))
identity-elim (X , X∈Fin , A) I I⊨id x = wires-≈ id (proj₂ X∈Fin x) I⊨id

identity-intro : ∀ (A : Object S T) I →
  (∀ x → (⌊ I ⌋ ⊨ ind I (inode x) ≈ ind I (enode x))) →
    (I ⊨a impl (identity A))
identity-intro (X , X∈Fin , A) I ∀x∙x₁≈x₂ = wires-≈⁻¹ id ∀x∙x₁≈x₂ (proj₁ X∈Fin)

compose-left : ∀ {A B C : Object S T} (F : A ⇒ B) (G : B ⇒ C) I →
  (I ⊨a impl (F ∙ G)) → (left * I ⊨a impl F)
compose-left {X , X∈Fin , A} {Y , Y∈Fin , B} {Z , Z∈Fin , C}
  (V , F , F✓) (W , G , G✓) I (I⊨F , I⊨G) =
    *-resp-⟨ABox⟩ left I F I⊨F

compose-right : ∀ {A B C : Object S T} (F : A ⇒ B) (G : B ⇒ C) I →
  (I ⊨a impl (F ∙ G)) → (right * I ⊨a impl G)
compose-right {X , X∈Fin , A} {Y , Y∈Fin , B} {Z , Z∈Fin , C}
  (V , F , F✓) (W , G , G✓) I (I⊨F , I⊨G) =
    *-resp-⟨ABox⟩ right I G I⊨G

compose-mid : ∀ {A B C : Object S T} (F : A ⇒ B) (G : B ⇒ C)
  (I : Interp Σ (IN A ⊕ (BN F ⊕ IN B ⊕ BN G) ⊕ IN C)) →
    (inode * I ⊨ (S , T) , iface A) → (I ⊨a impl (F ∙ G)) →
      (bnode * bnode * I ⊨ (S , T) , iface B)
compose-mid {A} {B} {C} F G I ((I⊨S , I⊨T) , I⊨A) I⊨F⟫G =
  ((I⊨S , I⊨T) , ⊨a-resp-≲ (enode ** J≲I) (iface B) J⊨B) where

  J : Interp Σ (IN A ⊕ BN F ⊕ IN B)
  J = apply F (inode * I) ((I⊨S , I⊨T) , I⊨A)

  J⊨B : enode * J ⊨a iface B
  J⊨B = proj₂ (apply✓ F (inode * I) ((I⊨S , I⊨T) , I⊨A))

  J≲I : J ≲ left * I
  J≲I = med-≲ (init-med (apply-init F (inode * I) ((I⊨S , I⊨T) , I⊨A))
    (left * I) (≲-refl (inode * I)) (I⊨S , compose-left F G I I⊨F⟫G))

compose-resp-⊨a : ∀ {A B C : Object S T} (F : A ⇒ B) (G : B ⇒ C) I →
  (left * I ⊨a impl F) → (right * I ⊨a impl G) → (I ⊨a impl (F ∙ G))
compose-resp-⊨a {X , X∈Fin , A} {Y , Y∈Fin , B} {Z , Z∈Fin , C}
  (V , F , F✓) (W , G , G✓) I I⊨F I⊨G =
    ⊨a-intro-⟫ I F G I⊨F I⊨G

compose-resp-⊨b : ∀ {A B C : Object S T} {V W : Set} (F : A ⇒ B) (G : B ⇒ C)
  (I : Interp Σ (IN A ⊕ (V ⊕ IN B ⊕ W) ⊕ IN C)) →
    (left * I ⊨b impl F) → (right * I ⊨b impl G) → (I ⊨b impl (F ∙ G))
compose-resp-⊨b {X , X∈Fin , A} {Y , Y∈Fin , B} {Z , Z∈Fin , C}
  (V , F , F✓) (W , G , G✓) I I⊨F I⊨G =
    ⊨b-intro-⟫ I F G I⊨F I⊨G